style: Update for team lint (#297)
* Update for team lint * fix tests * remove pedantic and sort deps * remove jiffy from system message Co-authored-by: Salvatore Giordano <[email protected]>
This commit is contained in:
co-authored by
Salvatore Giordano
parent
e13ce930a6
commit
c5252ab4d4
@@ -1,63 +1,147 @@
|
|||||||
include: package:pedantic/analysis_options.yaml
|
|
||||||
|
|
||||||
analyzer:
|
analyzer:
|
||||||
exclude:
|
exclude:
|
||||||
- lib/**/*.g.dart
|
- lib/**/*.g.dart
|
||||||
- lib/**/*.freezed.dart
|
- lib/**/*.freezed.dart
|
||||||
- example/*
|
- example/*
|
||||||
- test/*
|
- test/*
|
||||||
|
|
||||||
linter:
|
linter:
|
||||||
rules:
|
rules:
|
||||||
# these rules are documented on and in the same order as
|
- always_use_package_imports
|
||||||
# the Dart Lint rules page to make maintenance easier
|
|
||||||
# https://github.com/dart-lang/linter/blob/master/example/all.yaml
|
|
||||||
# - always_declare_return_types
|
|
||||||
# - always_specify_types
|
|
||||||
# - annotate_overrides
|
|
||||||
# - avoid_as
|
|
||||||
- avoid_empty_else
|
- avoid_empty_else
|
||||||
- avoid_init_to_null
|
- avoid_relative_lib_imports
|
||||||
- avoid_return_types_on_setters
|
- avoid_slow_async_io
|
||||||
- avoid_web_libraries_in_flutter
|
- avoid_types_as_parameter_names
|
||||||
- await_only_futures
|
|
||||||
- camel_case_types
|
|
||||||
- cancel_subscriptions
|
- cancel_subscriptions
|
||||||
- close_sinks
|
- close_sinks
|
||||||
# - comment_references # we do not presume as to what people want to reference in their dartdocs
|
|
||||||
# - constant_identifier_names # https://github.com/dart-lang/linter/issues/204
|
|
||||||
- control_flow_in_finally
|
- control_flow_in_finally
|
||||||
- empty_constructor_bodies
|
- diagnostic_describe_all_properties
|
||||||
- empty_statements
|
- empty_statements
|
||||||
- hash_and_equals
|
- hash_and_equals
|
||||||
- implementation_imports
|
- invariant_booleans
|
||||||
# - invariant_booleans
|
- iterable_contains_unrelated_type
|
||||||
# - iterable_contains_unrelated_type
|
- list_remove_unrelated_type
|
||||||
- library_names
|
- literal_only_boolean_expressions
|
||||||
# - library_prefixes
|
- no_adjacent_strings_in_list
|
||||||
# - list_remove_unrelated_type
|
- no_duplicate_case_values
|
||||||
# - literal_only_boolean_expressions
|
- no_logic_in_create_state
|
||||||
- non_constant_identifier_names
|
- prefer_void_to_null
|
||||||
# - one_member_abstracts
|
|
||||||
# - only_throw_errors
|
|
||||||
# - overridden_fields
|
|
||||||
- package_api_docs
|
|
||||||
- package_names
|
|
||||||
- package_prefixed_library_names
|
|
||||||
- prefer_is_not_empty
|
|
||||||
# - prefer_mixin # https://github.com/dart-lang/language/issues/32
|
|
||||||
- public_member_api_docs
|
|
||||||
- slash_for_doc_comments
|
|
||||||
# - sort_constructors_first
|
|
||||||
# - sort_unnamed_constructors_first
|
|
||||||
# - super_goes_last # no longer needed w/ Dart 2
|
|
||||||
- test_types_in_equals
|
- test_types_in_equals
|
||||||
- throw_in_finally
|
- throw_in_finally
|
||||||
# - type_annotate_public_apis # subset of always_specify_types
|
|
||||||
- type_init_formals
|
|
||||||
# - unawaited_futures
|
|
||||||
- unnecessary_brace_in_string_interps
|
|
||||||
- unnecessary_getters_setters
|
|
||||||
- unnecessary_statements
|
- unnecessary_statements
|
||||||
- unrelated_type_equality_checks
|
- unrelated_type_equality_checks
|
||||||
|
- omit_local_variable_types
|
||||||
|
- use_key_in_widget_constructors
|
||||||
- valid_regexps
|
- valid_regexps
|
||||||
|
- always_declare_return_types
|
||||||
|
- always_put_required_named_parameters_first
|
||||||
|
- always_require_non_null_named_parameters
|
||||||
|
- annotate_overrides
|
||||||
|
- avoid_bool_literals_in_conditional_expressions
|
||||||
|
- avoid_catching_errors
|
||||||
|
- avoid_init_to_null
|
||||||
|
- avoid_null_checks_in_equality_operators
|
||||||
|
- avoid_positional_boolean_parameters
|
||||||
|
- avoid_private_typedef_functions
|
||||||
|
- avoid_redundant_argument_values
|
||||||
|
- avoid_return_types_on_setters
|
||||||
|
- avoid_returning_null
|
||||||
|
- avoid_returning_null_for_void
|
||||||
|
- avoid_shadowing_type_parameters
|
||||||
|
- avoid_single_cascade_in_expression_statements
|
||||||
|
- avoid_unnecessary_containers
|
||||||
|
- avoid_unused_constructor_parameters
|
||||||
|
- await_only_futures
|
||||||
|
- camel_case_extensions
|
||||||
|
- camel_case_types
|
||||||
|
- cascade_invocations
|
||||||
|
|
||||||
|
- constant_identifier_names
|
||||||
|
- curly_braces_in_flow_control_structures
|
||||||
|
- directives_ordering
|
||||||
|
- empty_catches
|
||||||
|
- empty_constructor_bodies
|
||||||
|
- exhaustive_cases
|
||||||
|
- file_names
|
||||||
|
- implementation_imports
|
||||||
|
- join_return_with_assignment
|
||||||
|
- leading_newlines_in_multiline_strings
|
||||||
|
- library_names
|
||||||
|
- library_prefixes
|
||||||
|
- lines_longer_than_80_chars
|
||||||
|
- missing_whitespace_between_adjacent_strings
|
||||||
|
- non_constant_identifier_names
|
||||||
|
- null_closures
|
||||||
|
- one_member_abstracts
|
||||||
|
- only_throw_errors
|
||||||
|
- package_api_docs
|
||||||
|
- package_prefixed_library_names
|
||||||
|
- parameter_assignments
|
||||||
|
- prefer_adjacent_string_concatenation
|
||||||
|
- prefer_asserts_in_initializer_lists
|
||||||
|
- prefer_asserts_with_message
|
||||||
|
- prefer_collection_literals
|
||||||
|
- prefer_conditional_assignment
|
||||||
|
- prefer_const_constructors
|
||||||
|
- prefer_const_constructors_in_immutables
|
||||||
|
- prefer_const_declarations
|
||||||
|
- prefer_const_literals_to_create_immutables
|
||||||
|
- prefer_constructors_over_static_methods
|
||||||
|
- prefer_contains
|
||||||
|
- prefer_equal_for_default_values
|
||||||
|
- prefer_expression_function_bodies
|
||||||
|
- prefer_final_fields
|
||||||
|
- prefer_final_in_for_each
|
||||||
|
- prefer_final_locals
|
||||||
|
- prefer_function_declarations_over_variables
|
||||||
|
- prefer_generic_function_type_aliases
|
||||||
|
- prefer_if_elements_to_conditional_expressions
|
||||||
|
- prefer_if_null_operators
|
||||||
|
- prefer_initializing_formals
|
||||||
|
- prefer_inlined_adds
|
||||||
|
- prefer_int_literals
|
||||||
|
- prefer_interpolation_to_compose_strings
|
||||||
|
- prefer_is_empty
|
||||||
|
- prefer_is_not_empty
|
||||||
|
- prefer_is_not_operator
|
||||||
|
- prefer_null_aware_operators
|
||||||
|
- prefer_single_quotes
|
||||||
|
- prefer_spread_collections
|
||||||
|
- prefer_typing_uninitialized_variables
|
||||||
|
- provide_deprecation_message
|
||||||
|
- public_member_api_docs
|
||||||
|
- recursive_getters
|
||||||
|
- sized_box_for_whitespace
|
||||||
|
- slash_for_doc_comments
|
||||||
|
- sort_child_properties_last
|
||||||
|
- sort_constructors_first
|
||||||
|
- sort_unnamed_constructors_first
|
||||||
|
|
||||||
|
- type_annotate_public_apis
|
||||||
|
- type_init_formals
|
||||||
|
- unnecessary_await_in_return
|
||||||
|
- unnecessary_brace_in_string_interps
|
||||||
|
- unnecessary_const
|
||||||
|
- unnecessary_getters_setters
|
||||||
|
- unnecessary_lambdas
|
||||||
|
- unnecessary_new
|
||||||
|
- unnecessary_null_aware_assignments
|
||||||
|
- unnecessary_null_in_if_null_operators
|
||||||
|
- unnecessary_nullable_for_final_variable_declarations
|
||||||
|
- unnecessary_parenthesis
|
||||||
|
- unnecessary_raw_strings
|
||||||
|
- unnecessary_string_escapes
|
||||||
|
- unnecessary_string_interpolations
|
||||||
|
- unnecessary_this
|
||||||
|
- use_is_even_rather_than_modulo
|
||||||
|
- use_late_for_private_fields_and_variables
|
||||||
|
- use_rethrow_when_possible
|
||||||
|
- use_setters_to_change_properties
|
||||||
|
- use_to_and_as_if_applicable
|
||||||
|
- package_names
|
||||||
|
- sort_pub_dependencies
|
||||||
|
|
||||||
|
# To be added when null-safe:
|
||||||
|
# - cast_nullable_to_non_nullable
|
||||||
|
#- unnecessary_null_checks
|
||||||
|
# - tighten_type_of_initializing_formals
|
||||||
|
# - null_check_on_nullable_type_parameter
|
||||||
@@ -2,9 +2,9 @@ import 'dart:async';
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:pedantic/pedantic.dart' show unawaited;
|
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:pedantic/pedantic.dart' show unawaited;
|
||||||
import 'package:rxdart/rxdart.dart';
|
import 'package:rxdart/rxdart.dart';
|
||||||
import 'package:stream_chat/src/api/retry_queue.dart';
|
import 'package:stream_chat/src/api/retry_queue.dart';
|
||||||
import 'package:stream_chat/src/debounce.dart';
|
import 'package:stream_chat/src/debounce.dart';
|
||||||
@@ -14,13 +14,6 @@ import 'package:stream_chat/src/models/channel_state.dart';
|
|||||||
import 'package:stream_chat/src/models/user.dart';
|
import 'package:stream_chat/src/models/user.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat/stream_chat.dart';
|
||||||
|
|
||||||
import '../client.dart';
|
|
||||||
import '../models/event.dart';
|
|
||||||
import '../models/member.dart';
|
|
||||||
import '../models/message.dart';
|
|
||||||
import 'requests.dart';
|
|
||||||
import 'responses.dart';
|
|
||||||
|
|
||||||
/// This a the class that manages a specific channel.
|
/// This a the class that manages a specific channel.
|
||||||
class Channel {
|
class Channel {
|
||||||
/// Create a channel client instance.
|
/// Create a channel client instance.
|
||||||
@@ -57,7 +50,8 @@ class Channel {
|
|||||||
set extraData(Map<String, dynamic> extraData) {
|
set extraData(Map<String, dynamic> extraData) {
|
||||||
if (_initializedCompleter.isCompleted) {
|
if (_initializedCompleter.isCompleted) {
|
||||||
throw Exception(
|
throw Exception(
|
||||||
'Once the channel is initialized you should use channel.update to update channel data');
|
'Once the channel is initialized you should use channel.update '
|
||||||
|
'to update channel data');
|
||||||
}
|
}
|
||||||
_extraData = extraData;
|
_extraData = extraData;
|
||||||
}
|
}
|
||||||
@@ -168,14 +162,15 @@ class Channel {
|
|||||||
final Completer<bool> _initializedCompleter = Completer();
|
final Completer<bool> _initializedCompleter = Completer();
|
||||||
|
|
||||||
/// True if this is initialized
|
/// True if this is initialized
|
||||||
/// Call [watch] to initialize the client or instantiate it using [Channel.fromState]
|
/// Call [watch] to initialize the client or instantiate it using
|
||||||
|
/// [Channel.fromState]
|
||||||
Future<bool> get initialized => _initializedCompleter.future;
|
Future<bool> get initialized => _initializedCompleter.future;
|
||||||
|
|
||||||
final _cancelableAttachmentUploadRequest = <String, CancelToken>{};
|
final _cancelableAttachmentUploadRequest = <String, CancelToken>{};
|
||||||
final _messageAttachmentsUploadCompleter = <String, Completer>{};
|
final _messageAttachmentsUploadCompleter = <String, Completer>{};
|
||||||
|
|
||||||
/// Cancels [attachmentId] upload request. Throws exception if the request hasn't
|
/// Cancels [attachmentId] upload request. Throws exception if the request
|
||||||
/// even started yet, Already completed or Already cancelled.
|
/// hasn't even started yet, Already completed or Already cancelled.
|
||||||
///
|
///
|
||||||
/// Optionally, provide a [reason] for the cancellation.
|
/// Optionally, provide a [reason] for the cancellation.
|
||||||
void cancelAttachmentUpload(
|
void cancelAttachmentUpload(
|
||||||
@@ -185,7 +180,8 @@ class Channel {
|
|||||||
final cancelToken = _cancelableAttachmentUploadRequest[attachmentId];
|
final cancelToken = _cancelableAttachmentUploadRequest[attachmentId];
|
||||||
if (cancelToken == null) {
|
if (cancelToken == null) {
|
||||||
throw Exception(
|
throw Exception(
|
||||||
"Upload request for this Attachment hasn't started yet or else Already completed",
|
"Upload request for this Attachment hasn't started yet or else "
|
||||||
|
'Already completed',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (cancelToken.isCancelled) throw Exception('Already cancelled');
|
if (cancelToken.isCancelled) throw Exception('Already cancelled');
|
||||||
@@ -193,15 +189,14 @@ class Channel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Retries the failed [attachmentId] upload request.
|
/// Retries the failed [attachmentId] upload request.
|
||||||
Future<void> retryAttachmentUpload(String messageId, String attachmentId) {
|
Future<void> retryAttachmentUpload(String messageId, String attachmentId) =>
|
||||||
return _uploadAttachments(messageId, [attachmentId]);
|
_uploadAttachments(messageId, [attachmentId]);
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _uploadAttachments(
|
Future<void> _uploadAttachments(
|
||||||
String messageId,
|
String messageId,
|
||||||
Iterable<String> attachmentIds,
|
Iterable<String> attachmentIds,
|
||||||
) {
|
) {
|
||||||
var message = state.messages.firstWhere(
|
final message = state.messages.firstWhere(
|
||||||
(it) => it.id == messageId,
|
(it) => it.id == messageId,
|
||||||
orElse: () => null,
|
orElse: () => null,
|
||||||
);
|
);
|
||||||
@@ -228,9 +223,8 @@ class Channel {
|
|||||||
client.logger.info('Uploading ${it.id} attachment...');
|
client.logger.info('Uploading ${it.id} attachment...');
|
||||||
|
|
||||||
void updateAttachment(Attachment attachment) {
|
void updateAttachment(Attachment attachment) {
|
||||||
final index = message.attachments.indexWhere((it) {
|
final index =
|
||||||
return it.id == attachment.id;
|
message.attachments.indexWhere((it) => it.id == attachment.id);
|
||||||
});
|
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
message.attachments[index] = attachment;
|
message.attachments[index] = attachment;
|
||||||
state?.addMessage(message);
|
state?.addMessage(message);
|
||||||
@@ -239,7 +233,7 @@ class Channel {
|
|||||||
|
|
||||||
void onSendProgress(int sent, int total) {
|
void onSendProgress(int sent, int total) {
|
||||||
debounce(
|
debounce(
|
||||||
timeout: Duration(seconds: 1),
|
timeout: const Duration(seconds: 1),
|
||||||
target: updateAttachment,
|
target: updateAttachment,
|
||||||
positionalArguments: [
|
positionalArguments: [
|
||||||
it.copyWith(
|
it.copyWith(
|
||||||
@@ -270,11 +264,17 @@ class Channel {
|
|||||||
client.logger.info('Attachment ${it.id} uploaded successfully...');
|
client.logger.info('Attachment ${it.id} uploaded successfully...');
|
||||||
if (isImage) {
|
if (isImage) {
|
||||||
updateAttachment(
|
updateAttachment(
|
||||||
it.copyWith(imageUrl: url, uploadState: UploadState.success()),
|
it.copyWith(
|
||||||
|
imageUrl: url,
|
||||||
|
uploadState: const UploadState.success(),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
updateAttachment(
|
updateAttachment(
|
||||||
it.copyWith(assetUrl: url, uploadState: UploadState.success()),
|
it.copyWith(
|
||||||
|
assetUrl: url,
|
||||||
|
uploadState: const UploadState.success(),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}).catchError((e, stk) {
|
}).catchError((e, stk) {
|
||||||
@@ -305,6 +305,7 @@ class Channel {
|
|||||||
(m) => m.id == message?.quotedMessageId,
|
(m) => m.id == message?.quotedMessageId,
|
||||||
orElse: () => null,
|
orElse: () => null,
|
||||||
);
|
);
|
||||||
|
// ignore: parameter_assignments
|
||||||
message = message.copyWith(
|
message = message.copyWith(
|
||||||
createdAt: message.createdAt ?? DateTime.now(),
|
createdAt: message.createdAt ?? DateTime.now(),
|
||||||
user: _client.state.user,
|
user: _client.state.user,
|
||||||
@@ -313,7 +314,7 @@ class Channel {
|
|||||||
attachments: message.attachments?.map(
|
attachments: message.attachments?.map(
|
||||||
(it) {
|
(it) {
|
||||||
if (it.uploadState.isSuccess) return it;
|
if (it.uploadState.isSuccess) return it;
|
||||||
return it.copyWith(uploadState: UploadState.preparing());
|
return it.copyWith(uploadState: const UploadState.preparing());
|
||||||
},
|
},
|
||||||
)?.toList(),
|
)?.toList(),
|
||||||
);
|
);
|
||||||
@@ -340,6 +341,7 @@ class Channel {
|
|||||||
message.attachments.map((it) => it.id),
|
message.attachments.map((it) => it.id),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
// ignore: parameter_assignments
|
||||||
message = await attachmentsUploadCompleter.future;
|
message = await attachmentsUploadCompleter.future;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -364,13 +366,14 @@ class Channel {
|
|||||||
.remove(message.id)
|
.remove(message.id)
|
||||||
?.completeError('Message Cancelled');
|
?.completeError('Message Cancelled');
|
||||||
|
|
||||||
|
// ignore: parameter_assignments
|
||||||
message = message.copyWith(
|
message = message.copyWith(
|
||||||
status: MessageSendingStatus.updating,
|
status: MessageSendingStatus.updating,
|
||||||
updatedAt: message.updatedAt ?? DateTime.now(),
|
updatedAt: message.updatedAt ?? DateTime.now(),
|
||||||
attachments: message.attachments?.map(
|
attachments: message.attachments?.map(
|
||||||
(it) {
|
(it) {
|
||||||
if (it.uploadState.isSuccess) return it;
|
if (it.uploadState.isSuccess) return it;
|
||||||
return it.copyWith(uploadState: UploadState.preparing());
|
return it.copyWith(uploadState: const UploadState.preparing());
|
||||||
},
|
},
|
||||||
)?.toList(),
|
)?.toList(),
|
||||||
);
|
);
|
||||||
@@ -388,6 +391,7 @@ class Channel {
|
|||||||
message.attachments.map((it) => it.id),
|
message.attachments.map((it) => it.id),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
// ignore: parameter_assignments
|
||||||
message = await attachmentsUploadCompleter.future;
|
message = await attachmentsUploadCompleter.future;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -423,6 +427,7 @@ class Channel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// ignore: parameter_assignments
|
||||||
message = message.copyWith(
|
message = message.copyWith(
|
||||||
type: 'deleted',
|
type: 'deleted',
|
||||||
status: MessageSendingStatus.deleting,
|
status: MessageSendingStatus.deleting,
|
||||||
@@ -456,7 +461,7 @@ class Channel {
|
|||||||
throw ArgumentError('Invalid timeout or Expiration date');
|
throw ArgumentError('Invalid timeout or Expiration date');
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}());
|
}(), 'Check for invalid token or expiration date');
|
||||||
|
|
||||||
DateTime pinExpires;
|
DateTime pinExpires;
|
||||||
if (timeoutOrExpirationDate is DateTime) {
|
if (timeoutOrExpirationDate is DateTime) {
|
||||||
@@ -475,39 +480,36 @@ class Channel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Unpins provided message
|
/// Unpins provided message
|
||||||
Future<UpdateMessageResponse> unpinMessage(Message message) {
|
Future<UpdateMessageResponse> unpinMessage(Message message) =>
|
||||||
return updateMessage(message.copyWith(pinned: false));
|
updateMessage(message.copyWith(pinned: false));
|
||||||
}
|
|
||||||
|
|
||||||
/// Send a file to this channel
|
/// Send a file to this channel
|
||||||
Future<SendFileResponse> sendFile(
|
Future<SendFileResponse> sendFile(
|
||||||
AttachmentFile file, {
|
AttachmentFile file, {
|
||||||
ProgressCallback onSendProgress,
|
ProgressCallback onSendProgress,
|
||||||
CancelToken cancelToken,
|
CancelToken cancelToken,
|
||||||
}) {
|
}) =>
|
||||||
return _client.sendFile(
|
_client.sendFile(
|
||||||
file,
|
file,
|
||||||
id,
|
id,
|
||||||
type,
|
type,
|
||||||
onSendProgress: onSendProgress,
|
onSendProgress: onSendProgress,
|
||||||
cancelToken: cancelToken,
|
cancelToken: cancelToken,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
/// Send an image to this channel
|
/// Send an image to this channel
|
||||||
Future<SendImageResponse> sendImage(
|
Future<SendImageResponse> sendImage(
|
||||||
AttachmentFile file, {
|
AttachmentFile file, {
|
||||||
ProgressCallback onSendProgress,
|
ProgressCallback onSendProgress,
|
||||||
CancelToken cancelToken,
|
CancelToken cancelToken,
|
||||||
}) {
|
}) =>
|
||||||
return _client.sendImage(
|
_client.sendImage(
|
||||||
file,
|
file,
|
||||||
id,
|
id,
|
||||||
type,
|
type,
|
||||||
onSendProgress: onSendProgress,
|
onSendProgress: onSendProgress,
|
||||||
cancelToken: cancelToken,
|
cancelToken: cancelToken,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
/// A message search.
|
/// A message search.
|
||||||
Future<SearchMessagesResponse> search({
|
Future<SearchMessagesResponse> search({
|
||||||
@@ -515,35 +517,32 @@ class Channel {
|
|||||||
Map<String, dynamic> messageFilters,
|
Map<String, dynamic> messageFilters,
|
||||||
List<SortOption> sort,
|
List<SortOption> sort,
|
||||||
PaginationParams paginationParams,
|
PaginationParams paginationParams,
|
||||||
}) {
|
}) =>
|
||||||
return _client.search(
|
_client.search(
|
||||||
{
|
{
|
||||||
'cid': {
|
'cid': {
|
||||||
r'$in': [cid],
|
r'$in': [cid],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
sort: sort,
|
||||||
sort: sort,
|
query: query,
|
||||||
query: query,
|
paginationParams: paginationParams,
|
||||||
paginationParams: paginationParams,
|
messageFilters: messageFilters,
|
||||||
messageFilters: messageFilters,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Delete a file from this channel
|
/// Delete a file from this channel
|
||||||
Future<EmptyResponse> deleteFile(
|
Future<EmptyResponse> deleteFile(
|
||||||
String url, {
|
String url, {
|
||||||
CancelToken cancelToken,
|
CancelToken cancelToken,
|
||||||
}) {
|
}) =>
|
||||||
return _client.deleteFile(url, id, type, cancelToken: cancelToken);
|
_client.deleteFile(url, id, type, cancelToken: cancelToken);
|
||||||
}
|
|
||||||
|
|
||||||
/// Delete an image from this channel
|
/// Delete an image from this channel
|
||||||
Future<EmptyResponse> deleteImage(
|
Future<EmptyResponse> deleteImage(
|
||||||
String url, {
|
String url, {
|
||||||
CancelToken cancelToken,
|
CancelToken cancelToken,
|
||||||
}) {
|
}) =>
|
||||||
return _client.deleteImage(url, id, type, cancelToken: cancelToken);
|
_client.deleteImage(url, id, type, cancelToken: cancelToken);
|
||||||
}
|
|
||||||
|
|
||||||
/// Send an event on this channel
|
/// Send an event on this channel
|
||||||
Future<EmptyResponse> sendEvent(Event event) {
|
Future<EmptyResponse> sendEvent(Event event) {
|
||||||
@@ -551,9 +550,7 @@ class Channel {
|
|||||||
return _client.post(
|
return _client.post(
|
||||||
'$_channelURL/event',
|
'$_channelURL/event',
|
||||||
data: {'event': event.toJson()},
|
data: {'event': event.toJson()},
|
||||||
).then((res) {
|
).then((res) => _client.decode(res.data, EmptyResponse.fromJson));
|
||||||
return _client.decode(res.data, EmptyResponse.fromJson);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send a reaction to this channel
|
/// Send a reaction to this channel
|
||||||
@@ -643,11 +640,10 @@ class Channel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final latestReactions = [...message.latestReactions ?? <Reaction>[]]
|
final latestReactions = [...message.latestReactions ?? <Reaction>[]]
|
||||||
..removeWhere((r) {
|
..removeWhere((r) =>
|
||||||
return r.userId == reaction.userId &&
|
r.userId == reaction.userId &&
|
||||||
r.type == reaction.type &&
|
r.type == reaction.type &&
|
||||||
r.messageId == reaction.messageId;
|
r.messageId == reaction.messageId);
|
||||||
});
|
|
||||||
|
|
||||||
final ownReactions = [...latestReactions ?? <Reaction>[]]
|
final ownReactions = [...latestReactions ?? <Reaction>[]]
|
||||||
..removeWhere((it) => it.userId != user.id);
|
..removeWhere((it) => it.userId != user.id);
|
||||||
@@ -825,7 +821,7 @@ class Channel {
|
|||||||
})
|
})
|
||||||
..addAll(options);
|
..addAll(options);
|
||||||
|
|
||||||
var response;
|
ChannelState response;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
response = await query(options: watchOptions);
|
response = await query(options: watchOptions);
|
||||||
@@ -861,7 +857,8 @@ class Channel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// List the message replies for a parent message
|
/// List the message replies for a parent message
|
||||||
/// Set [preferOffline] to true to avoid the api call if the data is already in the offline storage
|
/// Set [preferOffline] to true to avoid the api call if the data is already
|
||||||
|
/// in the offline storage
|
||||||
Future<QueryRepliesResponse> getReplies(
|
Future<QueryRepliesResponse> getReplies(
|
||||||
String parentId,
|
String parentId,
|
||||||
PaginationParams options, {
|
PaginationParams options, {
|
||||||
@@ -940,16 +937,15 @@ class Channel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new channel
|
/// Creates a new channel
|
||||||
Future<ChannelState> create() async {
|
Future<ChannelState> create() async => query(options: {
|
||||||
return query(options: {
|
'watch': false,
|
||||||
'watch': false,
|
'state': false,
|
||||||
'state': false,
|
'presence': false,
|
||||||
'presence': false,
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Query the API, get messages, members or other channel fields
|
/// Query the API, get messages, members or other channel fields
|
||||||
/// Set [preferOffline] to true to avoid the api call if the data is already in the offline storage
|
/// Set [preferOffline] to true to avoid the api call if the data is already
|
||||||
|
/// in the offline storage
|
||||||
Future<ChannelState> query({
|
Future<ChannelState> query({
|
||||||
Map<String, dynamic> options = const {},
|
Map<String, dynamic> options = const {},
|
||||||
PaginationParams messagesPagination,
|
PaginationParams messagesPagination,
|
||||||
@@ -1112,8 +1108,9 @@ class Channel {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Hides the channel from [StreamChatClient.queryChannels] for the user until a message is added
|
/// Hides the channel from [StreamChatClient.queryChannels] for the user
|
||||||
/// If [clearHistory] is set to true - all messages will be removed for the user
|
/// until a message is added If [clearHistory] is set to true - all messages
|
||||||
|
/// will be removed for the user
|
||||||
Future<EmptyResponse> hide({bool clearHistory = false}) async {
|
Future<EmptyResponse> hide({bool clearHistory = false}) async {
|
||||||
_checkInitialized();
|
_checkInitialized();
|
||||||
final response = await _client
|
final response = await _client
|
||||||
@@ -1134,28 +1131,28 @@ class Channel {
|
|||||||
return _client.decode(response.data, EmptyResponse.fromJson);
|
return _client.decode(response.data, EmptyResponse.fromJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stream of [Event] coming from websocket connection specific for the channel
|
/// Stream of [Event] coming from websocket connection specific for the
|
||||||
/// Pass an eventType as parameter in order to filter just a type of event
|
/// channel. Pass an eventType as parameter in order to filter just a type
|
||||||
|
/// of event
|
||||||
Stream<Event> on([
|
Stream<Event> on([
|
||||||
String eventType,
|
String eventType,
|
||||||
String eventType2,
|
String eventType2,
|
||||||
String eventType3,
|
String eventType3,
|
||||||
String eventType4,
|
String eventType4,
|
||||||
]) {
|
]) =>
|
||||||
return _client
|
_client
|
||||||
.on(
|
.on(
|
||||||
eventType,
|
eventType,
|
||||||
eventType2,
|
eventType2,
|
||||||
eventType3,
|
eventType3,
|
||||||
eventType4,
|
eventType4,
|
||||||
)
|
)
|
||||||
.where((e) => e.cid == cid);
|
.where((e) => e.cid == cid);
|
||||||
}
|
|
||||||
|
|
||||||
DateTime _lastTypingEvent;
|
DateTime _lastTypingEvent;
|
||||||
|
|
||||||
/// First of the [EventType.typingStart] and [EventType.typingStop] events based on the users keystrokes.
|
/// First of the [EventType.typingStart] and [EventType.typingStop] events
|
||||||
/// Call this on every keystroke.
|
/// based on the users keystrokes. Call this on every keystroke.
|
||||||
Future<void> keyStroke([String parentId]) async {
|
Future<void> keyStroke([String parentId]) async {
|
||||||
if (config?.typingEvents == false) {
|
if (config?.typingEvents == false) {
|
||||||
return;
|
return;
|
||||||
@@ -1196,15 +1193,14 @@ class Channel {
|
|||||||
void _checkInitialized() {
|
void _checkInitialized() {
|
||||||
if (!_initializedCompleter.isCompleted) {
|
if (!_initializedCompleter.isCompleted) {
|
||||||
throw Exception(
|
throw Exception(
|
||||||
"Channel $cid hasn't been initialized yet. Make sure to call .watch() or to instantiate the client using [Channel.fromState]");
|
"Channel $cid hasn't been initialized yet. Make sure to call .watch()"
|
||||||
|
' or to instantiate the client using [Channel.fromState]');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The class that handles the state of the channel listening to the events
|
/// The class that handles the state of the channel listening to the events
|
||||||
class ChannelClientState {
|
class ChannelClientState {
|
||||||
final _subscriptions = <StreamSubscription>[];
|
|
||||||
|
|
||||||
/// Creates a new instance listening to events and updating the state
|
/// Creates a new instance listening to events and updating the state
|
||||||
ChannelClientState(this._channel, ChannelState channelState) {
|
ChannelClientState(this._channel, ChannelState channelState) {
|
||||||
retryQueue = RetryQueue(
|
retryQueue = RetryQueue(
|
||||||
@@ -1252,14 +1248,16 @@ class ChannelClientState {
|
|||||||
_channel._client.chatPersistenceClient
|
_channel._client.chatPersistenceClient
|
||||||
?.getChannelStateByCid(_channel.cid)
|
?.getChannelStateByCid(_channel.cid)
|
||||||
?.then((state) {
|
?.then((state) {
|
||||||
// Replacing the persistence state members with the latest `channelState.members`
|
// Replacing the persistence state members with the latest
|
||||||
// as they may have changes over the time.
|
// `channelState.members` as they may have changes over the time.
|
||||||
updateChannelState(state.copyWith(members: channelState.members));
|
updateChannelState(state.copyWith(members: channelState.members));
|
||||||
retryFailedMessages();
|
retryFailedMessages();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final _subscriptions = <StreamSubscription>[];
|
||||||
|
|
||||||
void _computeInitialUnread() {
|
void _computeInitialUnread() {
|
||||||
final userRead = channelState?.read?.firstWhere(
|
final userRead = channelState?.read?.firstWhere(
|
||||||
(r) => r.user.id == _channel._client.state?.user?.id,
|
(r) => r.user.id == _channel._client.state?.user?.id,
|
||||||
@@ -1338,8 +1336,8 @@ class ChannelClientState {
|
|||||||
|
|
||||||
/// Flag which indicates if [ChannelClientState] contain latest/recent messages or not.
|
/// Flag which indicates if [ChannelClientState] contain latest/recent messages or not.
|
||||||
/// This flag should be managed by UI sdks.
|
/// This flag should be managed by UI sdks.
|
||||||
/// When false, any new message (received by WebSocket event - [EventType.messageNew]) will not
|
/// When false, any new message (received by WebSocket event
|
||||||
/// be pushed on to message list.
|
/// - [EventType.messageNew]) will not be pushed on to message list.
|
||||||
bool get isUpToDate => _isUpToDateController.value;
|
bool get isUpToDate => _isUpToDateController.value;
|
||||||
|
|
||||||
set isUpToDate(bool isUpToDate) => _isUpToDateController.add(isUpToDate);
|
set isUpToDate(bool isUpToDate) => _isUpToDateController.add(isUpToDate);
|
||||||
@@ -1357,12 +1355,18 @@ class ChannelClientState {
|
|||||||
Future<void> retryFailedMessages() async {
|
Future<void> retryFailedMessages() async {
|
||||||
final failedMessages =
|
final failedMessages =
|
||||||
<Message>[...messages, ...threads.values.expand((v) => v)]
|
<Message>[...messages, ...threads.values.expand((v) => v)]
|
||||||
.where((message) =>
|
.where(
|
||||||
message.status != null &&
|
(message) =>
|
||||||
message.status != MessageSendingStatus.sent &&
|
message.status != null &&
|
||||||
message.createdAt.isBefore(DateTime.now().subtract(Duration(
|
message.status != MessageSendingStatus.sent &&
|
||||||
seconds: 1,
|
message.createdAt.isBefore(
|
||||||
))))
|
DateTime.now().subtract(
|
||||||
|
const Duration(
|
||||||
|
seconds: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
retryQueue.add(failedMessages);
|
retryQueue.add(failedMessages);
|
||||||
@@ -1471,27 +1475,32 @@ class ChannelClientState {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_subscriptions.add(_channel
|
_subscriptions.add(
|
||||||
.on(
|
_channel
|
||||||
EventType.messageRead,
|
.on(
|
||||||
EventType.notificationMarkRead,
|
EventType.messageRead,
|
||||||
)
|
EventType.notificationMarkRead,
|
||||||
.listen((event) {
|
)
|
||||||
final readList = List<Read>.from(_channelState?.read ?? []);
|
.listen(
|
||||||
final userReadIndex = read?.indexWhere((r) => r.user.id == event.user.id);
|
(event) {
|
||||||
|
final readList = List<Read>.from(_channelState?.read ?? []);
|
||||||
|
final userReadIndex =
|
||||||
|
read?.indexWhere((r) => r.user.id == event.user.id);
|
||||||
|
|
||||||
if (userReadIndex != null && userReadIndex != -1) {
|
if (userReadIndex != null && userReadIndex != -1) {
|
||||||
final userRead = readList.removeAt(userReadIndex);
|
final userRead = readList.removeAt(userReadIndex);
|
||||||
if (userRead.user?.id == _channel._client.state.user.id) {
|
if (userRead.user?.id == _channel._client.state.user.id) {
|
||||||
_unreadCountController.add(0);
|
_unreadCountController.add(0);
|
||||||
}
|
}
|
||||||
readList.add(Read(
|
readList.add(Read(
|
||||||
user: event.user,
|
user: event.user,
|
||||||
lastRead: event.createdAt,
|
lastRead: event.createdAt,
|
||||||
));
|
));
|
||||||
_channelState = _channelState.copyWith(read: readList);
|
_channelState = _channelState.copyWith(read: readList);
|
||||||
}
|
}
|
||||||
}));
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Channel message list
|
/// Channel message list
|
||||||
@@ -1527,11 +1536,8 @@ class ChannelClientState {
|
|||||||
List<Member>, Map<String, User>, List<Member>>(
|
List<Member>, Map<String, User>, List<Member>>(
|
||||||
channelStateStream.map((cs) => cs.members),
|
channelStateStream.map((cs) => cs.members),
|
||||||
_channel.client.state.usersStream,
|
_channel.client.state.usersStream,
|
||||||
(members, users) {
|
(members, users) =>
|
||||||
return members
|
members.map((e) => e.copyWith(user: users[e.user.id])).toList(),
|
||||||
.map((e) => e.copyWith(user: users[e.user.id]))
|
|
||||||
.toList();
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Channel watcher count
|
/// Channel watcher count
|
||||||
@@ -1551,9 +1557,7 @@ class ChannelClientState {
|
|||||||
CombineLatestStream.combine2<List<User>, Map<String, User>, List<User>>(
|
CombineLatestStream.combine2<List<User>, Map<String, User>, List<User>>(
|
||||||
channelStateStream.map((cs) => cs.watchers),
|
channelStateStream.map((cs) => cs.watchers),
|
||||||
_channel.client.state.usersStream,
|
_channel.client.state.usersStream,
|
||||||
(watchers, users) {
|
(watchers, users) => watchers.map((e) => users[e.id] ?? e).toList(),
|
||||||
return watchers.map((e) => users[e.id] ?? e).toList();
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Channel read list
|
/// Channel read list
|
||||||
@@ -1625,9 +1629,7 @@ class ChannelClientState {
|
|||||||
true)
|
true)
|
||||||
?.toList() ??
|
?.toList() ??
|
||||||
[],
|
[],
|
||||||
];
|
]..sort(_sortByCreatedAt);
|
||||||
|
|
||||||
newMessages.sort(_sortByCreatedAt);
|
|
||||||
|
|
||||||
final newWatchers = <User>[
|
final newWatchers = <User>[
|
||||||
...updatedState?.watchers ?? [],
|
...updatedState?.watchers ?? [],
|
||||||
@@ -1692,10 +1694,9 @@ class ChannelClientState {
|
|||||||
|
|
||||||
set _channelState(ChannelState v) {
|
set _channelState(ChannelState v) {
|
||||||
_channelStateController.add(v);
|
_channelStateController.add(v);
|
||||||
|
|
||||||
if (_channel._client.persistenceEnabled) {
|
if (_channel._client.persistenceEnabled) {
|
||||||
debounce(
|
debounce(
|
||||||
timeout: Duration(milliseconds: 500),
|
timeout: const Duration(milliseconds: 500),
|
||||||
target: _channel._client.chatPersistenceClient?.updateChannelState,
|
target: _channel._client.chatPersistenceClient?.updateChannelState,
|
||||||
positionalArguments: [v],
|
positionalArguments: [v],
|
||||||
);
|
);
|
||||||
@@ -1735,28 +1736,37 @@ class ChannelClientState {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_subscriptions.add(_channel.on(EventType.typingStart).listen((event) {
|
_subscriptions
|
||||||
if (event.user.id != _channel.client.state.user.id) {
|
..add(
|
||||||
_typings[event.user] = DateTime.now();
|
_channel.on(EventType.typingStart).listen(
|
||||||
_typingEventsController.add(_typings.keys.toList());
|
(event) {
|
||||||
}
|
if (event.user.id != _channel.client.state.user.id) {
|
||||||
}));
|
_typings[event.user] = DateTime.now();
|
||||||
|
_typingEventsController.add(_typings.keys.toList());
|
||||||
_subscriptions.add(_channel.on(EventType.typingStop).listen((event) {
|
}
|
||||||
if (event.user.id != _channel.client.state.user.id) {
|
},
|
||||||
_typings.remove(event.user);
|
),
|
||||||
_typingEventsController.add(_typings.keys.toList());
|
)
|
||||||
}
|
..add(
|
||||||
}));
|
_channel.on(EventType.typingStop).listen(
|
||||||
|
(event) {
|
||||||
|
if (event.user.id != _channel.client.state.user.id) {
|
||||||
|
_typings.remove(event.user);
|
||||||
|
_typingEventsController.add(_typings.keys.toList());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer _cleaningTimer;
|
Timer _cleaningTimer;
|
||||||
|
|
||||||
void _startCleaning() {
|
void _startCleaning() {
|
||||||
if (_channel.config?.typingEvents == false) {
|
if (_channel.config?.typingEvents == false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_cleaningTimer = Timer.periodic(Duration(seconds: 1), (_) {
|
_cleaningTimer = Timer.periodic(const Duration(seconds: 1), (_) {
|
||||||
final now = DateTime.now();
|
final now = DateTime.now();
|
||||||
|
|
||||||
if (_channel._lastTypingEvent != null &&
|
if (_channel._lastTypingEvent != null &&
|
||||||
@@ -1769,8 +1779,9 @@ class ChannelClientState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Timer _pinnedMessagesTimer;
|
Timer _pinnedMessagesTimer;
|
||||||
|
|
||||||
void _startCleaningPinnedMessages() {
|
void _startCleaningPinnedMessages() {
|
||||||
_pinnedMessagesTimer = Timer.periodic(Duration(seconds: 30), (_) {
|
_pinnedMessagesTimer = Timer.periodic(const Duration(seconds: 30), (_) {
|
||||||
final now = DateTime.now();
|
final now = DateTime.now();
|
||||||
var expiredMessages = channelState.pinnedMessages
|
var expiredMessages = channelState.pinnedMessages
|
||||||
?.where((m) => m.pinExpires?.isBefore(now) == true)
|
?.where((m) => m.pinExpires?.isBefore(now) == true)
|
||||||
@@ -1781,8 +1792,6 @@ class ChannelClientState {
|
|||||||
.map((m) => m.copyWith(
|
.map((m) => m.copyWith(
|
||||||
pinExpires: null,
|
pinExpires: null,
|
||||||
pinned: false,
|
pinned: false,
|
||||||
pinnedAt: null,
|
|
||||||
pinnedBy: null,
|
|
||||||
))
|
))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
|
|||||||
@@ -5,22 +5,6 @@ part 'requests.g.dart';
|
|||||||
/// Sorting options
|
/// Sorting options
|
||||||
@JsonSerializable(createFactory: false)
|
@JsonSerializable(createFactory: false)
|
||||||
class SortOption<T> {
|
class SortOption<T> {
|
||||||
/// Ascending order
|
|
||||||
static const ASC = 1;
|
|
||||||
|
|
||||||
/// Descending order
|
|
||||||
static const DESC = -1;
|
|
||||||
|
|
||||||
/// A sorting field name
|
|
||||||
final String field;
|
|
||||||
|
|
||||||
/// A sorting direction
|
|
||||||
final int direction;
|
|
||||||
|
|
||||||
/// Sorting field Comparator required for offline sorting
|
|
||||||
@JsonKey(ignore: true)
|
|
||||||
final Comparator<T> comparator;
|
|
||||||
|
|
||||||
/// Creates a new SortOption instance
|
/// Creates a new SortOption instance
|
||||||
///
|
///
|
||||||
/// For example:
|
/// For example:
|
||||||
@@ -34,6 +18,24 @@ class SortOption<T> {
|
|||||||
this.comparator,
|
this.comparator,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// Ascending order
|
||||||
|
// ignore: constant_identifier_names
|
||||||
|
static const ASC = 1;
|
||||||
|
|
||||||
|
/// Descending order
|
||||||
|
// ignore: constant_identifier_names
|
||||||
|
static const DESC = -1;
|
||||||
|
|
||||||
|
/// A sorting field name
|
||||||
|
final String field;
|
||||||
|
|
||||||
|
/// A sorting direction
|
||||||
|
final int direction;
|
||||||
|
|
||||||
|
/// Sorting field Comparator required for offline sorting
|
||||||
|
@JsonKey(ignore: true)
|
||||||
|
final Comparator<T> comparator;
|
||||||
|
|
||||||
/// Serialize model to json
|
/// Serialize model to json
|
||||||
Map<String, dynamic> toJson() => _$SortOptionToJson(this);
|
Map<String, dynamic> toJson() => _$SortOptionToJson(this);
|
||||||
}
|
}
|
||||||
@@ -41,6 +43,25 @@ class SortOption<T> {
|
|||||||
/// Pagination options.
|
/// Pagination options.
|
||||||
@JsonSerializable(createFactory: false, includeIfNull: false)
|
@JsonSerializable(createFactory: false, includeIfNull: false)
|
||||||
class PaginationParams {
|
class PaginationParams {
|
||||||
|
/// Creates a new PaginationParams instance
|
||||||
|
///
|
||||||
|
/// For example:
|
||||||
|
/// ```dart
|
||||||
|
/// // limit to 50
|
||||||
|
/// final paginationParams = PaginationParams(limit: 50);
|
||||||
|
///
|
||||||
|
/// // limit to 50 with offset
|
||||||
|
/// final paginationParams = PaginationParams(limit: 50, offset: 50);
|
||||||
|
/// ```
|
||||||
|
const PaginationParams({
|
||||||
|
this.limit = 10,
|
||||||
|
this.offset = 0,
|
||||||
|
this.greaterThan,
|
||||||
|
this.greaterThanOrEqual,
|
||||||
|
this.lessThan,
|
||||||
|
this.lessThanOrEqual,
|
||||||
|
});
|
||||||
|
|
||||||
/// The amount of items requested from the APIs.
|
/// The amount of items requested from the APIs.
|
||||||
final int limit;
|
final int limit;
|
||||||
|
|
||||||
@@ -63,25 +84,6 @@ class PaginationParams {
|
|||||||
@JsonKey(name: 'id_lte')
|
@JsonKey(name: 'id_lte')
|
||||||
final String lessThanOrEqual;
|
final String lessThanOrEqual;
|
||||||
|
|
||||||
/// Creates a new PaginationParams instance
|
|
||||||
///
|
|
||||||
/// For example:
|
|
||||||
/// ```dart
|
|
||||||
/// // limit to 50
|
|
||||||
/// final paginationParams = PaginationParams(limit: 50);
|
|
||||||
///
|
|
||||||
/// // limit to 50 with offset
|
|
||||||
/// final paginationParams = PaginationParams(limit: 50, offset: 50);
|
|
||||||
/// ```
|
|
||||||
const PaginationParams({
|
|
||||||
this.limit = 10,
|
|
||||||
this.offset = 0,
|
|
||||||
this.greaterThan,
|
|
||||||
this.greaterThanOrEqual,
|
|
||||||
this.lessThan,
|
|
||||||
this.lessThanOrEqual,
|
|
||||||
});
|
|
||||||
|
|
||||||
/// Serialize model to json
|
/// Serialize model to json
|
||||||
Map<String, dynamic> toJson() => _$PaginationParamsToJson(this);
|
Map<String, dynamic> toJson() => _$PaginationParamsToJson(this);
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
import 'package:stream_chat/src/client.dart';
|
import 'package:stream_chat/src/client.dart';
|
||||||
|
import 'package:stream_chat/src/models/channel_model.dart';
|
||||||
|
import 'package:stream_chat/src/models/channel_state.dart';
|
||||||
import 'package:stream_chat/src/models/device.dart';
|
import 'package:stream_chat/src/models/device.dart';
|
||||||
import 'package:stream_chat/src/models/event.dart';
|
import 'package:stream_chat/src/models/event.dart';
|
||||||
|
import 'package:stream_chat/src/models/member.dart';
|
||||||
import '../models/channel_model.dart';
|
import 'package:stream_chat/src/models/message.dart';
|
||||||
import '../models/channel_state.dart';
|
import 'package:stream_chat/src/models/reaction.dart';
|
||||||
import '../models/member.dart';
|
import 'package:stream_chat/src/models/read.dart';
|
||||||
import '../models/message.dart';
|
import 'package:stream_chat/src/models/user.dart';
|
||||||
import '../models/reaction.dart';
|
|
||||||
import '../models/read.dart';
|
|
||||||
import '../models/user.dart';
|
|
||||||
|
|
||||||
part 'responses.g.dart';
|
part 'responses.g.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ class RetryPolicy {
|
|||||||
final bool Function(StreamChatClient client, int attempt, ApiError apiError)
|
final bool Function(StreamChatClient client, int attempt, ApiError apiError)
|
||||||
shouldRetry;
|
shouldRetry;
|
||||||
|
|
||||||
/// In the case that we want to retry a failed request the retryTimeout method is called to determine the timeout
|
/// In the case that we want to retry a failed request the retryTimeout
|
||||||
|
/// method is called to determine the timeout
|
||||||
final Duration Function(
|
final Duration Function(
|
||||||
StreamChatClient client, int attempt, ApiError apiError) retryTimeout;
|
StreamChatClient client, int attempt, ApiError apiError) retryTimeout;
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:meta/meta.dart';
|
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:meta/meta.dart';
|
||||||
import 'package:stream_chat/src/api/channel.dart';
|
import 'package:stream_chat/src/api/channel.dart';
|
||||||
import 'package:stream_chat/src/api/retry_policy.dart';
|
import 'package:stream_chat/src/api/retry_policy.dart';
|
||||||
import 'package:stream_chat/src/event_type.dart';
|
import 'package:stream_chat/src/event_type.dart';
|
||||||
@@ -12,12 +12,6 @@ import 'package:stream_chat/stream_chat.dart';
|
|||||||
|
|
||||||
/// The retry queue associated to a channel
|
/// The retry queue associated to a channel
|
||||||
class RetryQueue {
|
class RetryQueue {
|
||||||
/// The channel of this queue
|
|
||||||
final Channel channel;
|
|
||||||
|
|
||||||
/// The logger associated to this queue
|
|
||||||
final Logger logger;
|
|
||||||
|
|
||||||
/// Instantiate a new RetryQueue object
|
/// Instantiate a new RetryQueue object
|
||||||
RetryQueue({
|
RetryQueue({
|
||||||
@required this.channel,
|
@required this.channel,
|
||||||
@@ -30,6 +24,12 @@ class RetryQueue {
|
|||||||
_listenFailedEvents();
|
_listenFailedEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The channel of this queue
|
||||||
|
final Channel channel;
|
||||||
|
|
||||||
|
/// The logger associated to this queue
|
||||||
|
final Logger logger;
|
||||||
|
|
||||||
final _subscriptions = <StreamSubscription>[];
|
final _subscriptions = <StreamSubscription>[];
|
||||||
|
|
||||||
void _listenConnectionRecovered() {
|
void _listenConnectionRecovered() {
|
||||||
|
|||||||
@@ -2,28 +2,29 @@ import 'dart:async';
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:meta/meta.dart';
|
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:meta/meta.dart';
|
||||||
import 'package:rxdart/rxdart.dart';
|
import 'package:rxdart/rxdart.dart';
|
||||||
import 'package:web_socket_channel/web_socket_channel.dart';
|
import 'package:stream_chat/src/api/connection_status.dart';
|
||||||
|
import 'package:stream_chat/src/api/web_socket_channel_stub.dart'
|
||||||
import '../models/event.dart';
|
|
||||||
import '../models/user.dart';
|
|
||||||
import 'connection_status.dart';
|
|
||||||
import 'web_socket_channel_stub.dart'
|
|
||||||
if (dart.library.html) 'web_socket_channel_html.dart'
|
if (dart.library.html) 'web_socket_channel_html.dart'
|
||||||
if (dart.library.io) 'web_socket_channel_io.dart';
|
if (dart.library.io) 'web_socket_channel_io.dart';
|
||||||
|
import 'package:stream_chat/src/models/event.dart';
|
||||||
|
import 'package:stream_chat/src/models/user.dart';
|
||||||
|
import 'package:web_socket_channel/web_socket_channel.dart';
|
||||||
|
|
||||||
/// Typedef which exposes an [Event] as the only parameter.
|
/// Typedef which exposes an [Event] as the only parameter.
|
||||||
typedef EventHandler = void Function(Event);
|
typedef EventHandler = void Function(Event);
|
||||||
|
|
||||||
/// Typedef used for connecting to a websocket. Method returns a [WebSocketChannel]
|
/// Typedef used for connecting to a websocket. Method returns a
|
||||||
/// and accepts a connection [url] and an optional [Iterable] of `protocols`.
|
/// [WebSocketChannel] and accepts a connection [url] and an optional
|
||||||
|
/// [Iterable] of `protocols`.
|
||||||
typedef ConnectWebSocket = WebSocketChannel Function(String url,
|
typedef ConnectWebSocket = WebSocketChannel Function(String url,
|
||||||
{Iterable<String> protocols});
|
{Iterable<String> protocols});
|
||||||
|
|
||||||
// TODO: parse error even
|
// TODO: parse error even
|
||||||
// TODO: if parsing an error into an event fails we should not hide the original error
|
// TODO: if parsing an error into an event fails we should not hide the
|
||||||
|
// TODO: original error
|
||||||
/// A WebSocket connection that reconnects upon failure.
|
/// A WebSocket connection that reconnects upon failure.
|
||||||
class WebSocket {
|
class WebSocket {
|
||||||
/// Creates a new websocket
|
/// Creates a new websocket
|
||||||
@@ -75,7 +76,8 @@ class WebSocket {
|
|||||||
/// WS connection payload
|
/// WS connection payload
|
||||||
final Map<String, dynamic> connectPayload;
|
final Map<String, dynamic> connectPayload;
|
||||||
|
|
||||||
/// Functions that will be called every time a new event is received from the connection
|
/// Functions that will be called every time a new event is received from the
|
||||||
|
/// connection
|
||||||
final EventHandler handler;
|
final EventHandler handler;
|
||||||
|
|
||||||
/// A WS specific logger instance
|
/// A WS specific logger instance
|
||||||
@@ -87,8 +89,9 @@ class WebSocket {
|
|||||||
final ConnectWebSocket connectFunc;
|
final ConnectWebSocket connectFunc;
|
||||||
|
|
||||||
/// Interval of the reconnection monitor timer
|
/// Interval of the reconnection monitor timer
|
||||||
/// This checks that it received a new event in the last [reconnectionMonitorTimeout] seconds,
|
/// This checks that it received a new event in the last
|
||||||
/// otherwise it considers the connection unhealthy and reconnects the WS
|
/// [reconnectionMonitorTimeout] seconds, otherwise it considers the
|
||||||
|
/// connection unhealthy and reconnects the WS
|
||||||
final int reconnectionMonitorInterval;
|
final int reconnectionMonitorInterval;
|
||||||
|
|
||||||
/// Interval of the health event sending timer
|
/// Interval of the health event sending timer
|
||||||
@@ -96,7 +99,8 @@ class WebSocket {
|
|||||||
/// make the server aware that the client is still listening
|
/// make the server aware that the client is still listening
|
||||||
final int healthCheckInterval;
|
final int healthCheckInterval;
|
||||||
|
|
||||||
/// The timeout that uses the reconnection monitor timer to consider the connection unhealthy
|
/// The timeout that uses the reconnection monitor timer to consider the
|
||||||
|
/// connection unhealthy
|
||||||
final int reconnectionMonitorTimeout;
|
final int reconnectionMonitorTimeout;
|
||||||
|
|
||||||
final _connectionStatusController =
|
final _connectionStatusController =
|
||||||
@@ -121,9 +125,7 @@ class WebSocket {
|
|||||||
_connecting = false,
|
_connecting = false,
|
||||||
_reconnecting = false;
|
_reconnecting = false;
|
||||||
|
|
||||||
Event _decodeEvent(String source) {
|
Event _decodeEvent(String source) => Event.fromJson(json.decode(source));
|
||||||
return Event.fromJson(json.decode(source));
|
|
||||||
}
|
|
||||||
|
|
||||||
Completer<Event> _connectionCompleter = Completer<Event>();
|
Completer<Event> _connectionCompleter = Completer<Event>();
|
||||||
|
|
||||||
@@ -166,8 +168,8 @@ class WebSocket {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(
|
logger.info('connection closed | closeCode: ${_channel.closeCode} | '
|
||||||
'connection closed | closeCode: ${_channel.closeCode} | closedReason: ${_channel.closeReason}');
|
'closedReason: ${_channel.closeReason}');
|
||||||
|
|
||||||
if (!_reconnecting) {
|
if (!_reconnecting) {
|
||||||
_reconnect();
|
_reconnect();
|
||||||
@@ -200,8 +202,7 @@ class WebSocket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onConnectionError(error, [stacktrace]) async {
|
Future<void> _onConnectionError(error, [stacktrace]) async {
|
||||||
logger.severe('error connecting');
|
logger..severe('error connecting')..severe(error);
|
||||||
logger.severe(error);
|
|
||||||
if (stacktrace != null) {
|
if (stacktrace != null) {
|
||||||
logger.severe(stacktrace);
|
logger.severe(stacktrace);
|
||||||
}
|
}
|
||||||
@@ -219,21 +220,21 @@ class WebSocket {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _startReconnectionMonitor() {
|
void _reconnectionTimer(_) {
|
||||||
final reconnectionTimer = (_) {
|
final now = DateTime.now();
|
||||||
final now = DateTime.now();
|
if (_lastEventAt != null &&
|
||||||
if (_lastEventAt != null &&
|
now.difference(_lastEventAt).inSeconds > reconnectionMonitorTimeout) {
|
||||||
now.difference(_lastEventAt).inSeconds > reconnectionMonitorTimeout) {
|
_channel.sink.close();
|
||||||
_channel.sink.close();
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
|
void _startReconnectionMonitor() {
|
||||||
_reconnectionMonitor = Timer.periodic(
|
_reconnectionMonitor = Timer.periodic(
|
||||||
Duration(seconds: reconnectionMonitorInterval),
|
Duration(seconds: reconnectionMonitorInterval),
|
||||||
reconnectionTimer,
|
_reconnectionTimer,
|
||||||
);
|
);
|
||||||
|
|
||||||
reconnectionTimer(_reconnectionMonitor);
|
_reconnectionTimer(_reconnectionMonitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _reconnectTimer() async {
|
void _reconnectTimer() async {
|
||||||
@@ -283,20 +284,20 @@ class WebSocket {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _healthCheckTimer(_) {
|
||||||
|
logger.info('sending health.check');
|
||||||
|
_channel.sink.add("{'type': 'health.check'}");
|
||||||
|
}
|
||||||
|
|
||||||
void _startHealthCheck() {
|
void _startHealthCheck() {
|
||||||
logger.info('start health check monitor');
|
logger.info('start health check monitor');
|
||||||
|
|
||||||
final healthCheckTimer = (_) {
|
|
||||||
logger.info('sending health.check');
|
|
||||||
_channel.sink.add("{'type': 'health.check'}");
|
|
||||||
};
|
|
||||||
|
|
||||||
_healthCheck = Timer.periodic(
|
_healthCheck = Timer.periodic(
|
||||||
Duration(seconds: healthCheckInterval),
|
Duration(seconds: healthCheckInterval),
|
||||||
healthCheckTimer,
|
_healthCheckTimer,
|
||||||
);
|
);
|
||||||
|
|
||||||
healthCheckTimer(_healthCheck);
|
_healthCheckTimer(_healthCheck);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Disconnects the WS and releases eventual resources
|
/// Disconnects the WS and releases eventual resources
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:stream_chat/src/api/responses.dart';
|
import 'package:stream_chat/src/api/responses.dart';
|
||||||
|
import 'package:stream_chat/src/client.dart';
|
||||||
import 'package:stream_chat/src/models/attachment_file.dart';
|
import 'package:stream_chat/src/models/attachment_file.dart';
|
||||||
import 'client.dart';
|
import 'package:stream_chat/src/extensions/string_extension.dart';
|
||||||
import 'extensions/string_extension.dart';
|
|
||||||
|
|
||||||
/// Class responsible for uploading images and files to a given channel
|
/// Class responsible for uploading images and files to a given channel
|
||||||
abstract class AttachmentFileUploader {
|
abstract class AttachmentFileUploader {
|
||||||
@@ -57,11 +57,11 @@ abstract class AttachmentFileUploader {
|
|||||||
|
|
||||||
/// Stream's default implementation of [AttachmentFileUploader]
|
/// Stream's default implementation of [AttachmentFileUploader]
|
||||||
class StreamAttachmentFileUploader implements AttachmentFileUploader {
|
class StreamAttachmentFileUploader implements AttachmentFileUploader {
|
||||||
final StreamChatClient _client;
|
|
||||||
|
|
||||||
/// Creates a new [StreamAttachmentFileUploader] instance.
|
/// Creates a new [StreamAttachmentFileUploader] instance.
|
||||||
const StreamAttachmentFileUploader(this._client);
|
const StreamAttachmentFileUploader(this._client);
|
||||||
|
|
||||||
|
final StreamChatClient _client;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<SendImageResponse> sendImage(
|
Future<SendImageResponse> sendImage(
|
||||||
AttachmentFile file,
|
AttachmentFile file,
|
||||||
|
|||||||
@@ -1,36 +1,35 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'package:stream_chat/src/extensions/map_extension.dart';
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
import 'package:pedantic/pedantic.dart' show unawaited;
|
import 'package:pedantic/pedantic.dart' show unawaited;
|
||||||
import 'package:rxdart/rxdart.dart';
|
import 'package:rxdart/rxdart.dart';
|
||||||
|
import 'package:stream_chat/src/api/channel.dart';
|
||||||
|
import 'package:stream_chat/src/api/connection_status.dart';
|
||||||
|
import 'package:stream_chat/src/api/requests.dart';
|
||||||
|
import 'package:stream_chat/src/api/responses.dart';
|
||||||
import 'package:stream_chat/src/api/retry_policy.dart';
|
import 'package:stream_chat/src/api/retry_policy.dart';
|
||||||
|
import 'package:stream_chat/src/api/websocket.dart';
|
||||||
|
import 'package:stream_chat/src/attachment_file_uploader.dart';
|
||||||
|
import 'package:stream_chat/src/db/chat_persistence_client.dart';
|
||||||
import 'package:stream_chat/src/event_type.dart';
|
import 'package:stream_chat/src/event_type.dart';
|
||||||
|
import 'package:stream_chat/src/exceptions.dart';
|
||||||
import 'package:stream_chat/src/models/attachment_file.dart';
|
import 'package:stream_chat/src/models/attachment_file.dart';
|
||||||
import 'package:stream_chat/src/models/channel_model.dart';
|
import 'package:stream_chat/src/models/channel_model.dart';
|
||||||
|
import 'package:stream_chat/src/models/channel_state.dart';
|
||||||
|
import 'package:stream_chat/src/models/event.dart';
|
||||||
|
import 'package:stream_chat/src/models/message.dart';
|
||||||
import 'package:stream_chat/src/models/own_user.dart';
|
import 'package:stream_chat/src/models/own_user.dart';
|
||||||
|
import 'package:stream_chat/src/models/user.dart';
|
||||||
import 'package:stream_chat/src/platform_detector/platform_detector.dart';
|
import 'package:stream_chat/src/platform_detector/platform_detector.dart';
|
||||||
import 'package:stream_chat/version.dart';
|
import 'package:stream_chat/version.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
import 'attachment_file_uploader.dart';
|
/// Handler function used for logging records. Function requires a single
|
||||||
import 'api/channel.dart';
|
/// [LogRecord] as the only parameter.
|
||||||
import 'api/connection_status.dart';
|
|
||||||
import 'api/requests.dart';
|
|
||||||
import 'api/responses.dart';
|
|
||||||
import 'api/websocket.dart';
|
|
||||||
import 'db/chat_persistence_client.dart';
|
|
||||||
import 'exceptions.dart';
|
|
||||||
import 'models/channel_state.dart';
|
|
||||||
import 'models/event.dart';
|
|
||||||
import 'models/message.dart';
|
|
||||||
import 'models/user.dart';
|
|
||||||
import 'extensions/map_extension.dart';
|
|
||||||
|
|
||||||
/// Handler function used for logging records. Function requires a single [LogRecord]
|
|
||||||
/// as the only parameter.
|
|
||||||
typedef LogHandlerFunction = void Function(LogRecord record);
|
typedef LogHandlerFunction = void Function(LogRecord record);
|
||||||
|
|
||||||
/// Used for decoding [Map] data to a generic type `T`.
|
/// Used for decoding [Map] data to a generic type `T`.
|
||||||
@@ -62,7 +61,9 @@ extension on PushProvider {
|
|||||||
|
|
||||||
/// The official Dart client for Stream Chat,
|
/// The official Dart client for Stream Chat,
|
||||||
/// a service for building chat applications.
|
/// a service for building chat applications.
|
||||||
/// This library can be used on any Dart project and on both mobile and web apps with Flutter.
|
/// This library can be used on any Dart project and on both mobile and web apps
|
||||||
|
/// with Flutter.
|
||||||
|
///
|
||||||
/// You can sign up for a Stream account at https://getstream.io/chat/
|
/// You can sign up for a Stream account at https://getstream.io/chat/
|
||||||
///
|
///
|
||||||
/// The Chat client will manage API call, event handling and manage the
|
/// The Chat client will manage API call, event handling and manage the
|
||||||
@@ -73,7 +74,8 @@ extension on PushProvider {
|
|||||||
/// ```
|
/// ```
|
||||||
class StreamChatClient {
|
class StreamChatClient {
|
||||||
/// Create a client instance with default options.
|
/// Create a client instance with default options.
|
||||||
/// You should only create the client once and re-use it across your application.
|
/// You should only create the client once and re-use it across your
|
||||||
|
/// application.
|
||||||
StreamChatClient(
|
StreamChatClient(
|
||||||
this.apiKey, {
|
this.apiKey, {
|
||||||
this.tokenProvider,
|
this.tokenProvider,
|
||||||
@@ -83,6 +85,7 @@ class StreamChatClient {
|
|||||||
Duration connectTimeout = const Duration(seconds: 6),
|
Duration connectTimeout = const Duration(seconds: 6),
|
||||||
Duration receiveTimeout = const Duration(seconds: 6),
|
Duration receiveTimeout = const Duration(seconds: 6),
|
||||||
Dio httpClient,
|
Dio httpClient,
|
||||||
|
// ignore: avoid_unused_constructor_parameters
|
||||||
RetryPolicy retryPolicy,
|
RetryPolicy retryPolicy,
|
||||||
this.attachmentFileUploader,
|
this.attachmentFileUploader,
|
||||||
}) {
|
}) {
|
||||||
@@ -122,30 +125,39 @@ class StreamChatClient {
|
|||||||
/// This client state
|
/// This client state
|
||||||
ClientState state;
|
ClientState state;
|
||||||
|
|
||||||
/// By default the Chat client will write all messages with level Warn or Error to stdout.
|
/// By default the Chat client will write all messages with level Warn or
|
||||||
/// During development you might want to enable more logging information, you can change the default log level when constructing the client.
|
/// Error to stdout.
|
||||||
|
///
|
||||||
|
/// During development you might want to enable more logging information,
|
||||||
|
/// you can change the default log level when constructing the client.
|
||||||
///
|
///
|
||||||
/// ```dart
|
/// ```dart
|
||||||
/// final client = StreamChatClient("stream-chat-api-key", logLevel: Level.INFO);
|
/// final client = StreamChatClient("stream-chat-api-key",
|
||||||
|
/// logLevel: Level.INFO);
|
||||||
/// ```
|
/// ```
|
||||||
final Level logLevel;
|
final Level logLevel;
|
||||||
|
|
||||||
/// Client specific logger instance.
|
/// Client specific logger instance.
|
||||||
/// Refer to the class [Logger] to learn more about the specific implementation.
|
/// Refer to the class [Logger] to learn more about the specific
|
||||||
|
/// implementation.
|
||||||
final Logger logger = Logger.detached('📡');
|
final Logger logger = Logger.detached('📡');
|
||||||
|
|
||||||
/// A function that has a parameter of type [LogRecord].
|
/// A function that has a parameter of type [LogRecord].
|
||||||
/// This is called on every new log record.
|
/// This is called on every new log record.
|
||||||
/// By default the client will use the handler returned by [_getDefaultLogHandler].
|
/// By default the client will use the handler returned by
|
||||||
/// Setting it you can handle the log messages directly instead of have them written to stdout,
|
/// [_getDefaultLogHandler].
|
||||||
/// this is very convenient if you use an error tracking tool or if you want to centralize your logs into one facility.
|
/// Setting it you can handle the log messages directly instead of have them
|
||||||
|
/// written to stdout,
|
||||||
|
/// this is very convenient if you use an error tracking tool or if you want
|
||||||
|
/// to centralize your logs into one facility.
|
||||||
///
|
///
|
||||||
/// ```dart
|
/// ```dart
|
||||||
/// myLogHandlerFunction = (LogRecord record) {
|
/// myLogHandlerFunction = (LogRecord record) {
|
||||||
/// // do something with the record (ie. send it to Sentry or Fabric)
|
/// // do something with the record (ie. send it to Sentry or Fabric)
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// final client = StreamChatClient("stream-chat-api-key", logHandlerFunction: myLogHandlerFunction);
|
/// final client = StreamChatClient("stream-chat-api-key",
|
||||||
|
/// logHandlerFunction: myLogHandlerFunction);
|
||||||
///```
|
///```
|
||||||
LogHandlerFunction logHandlerFunction;
|
LogHandlerFunction logHandlerFunction;
|
||||||
|
|
||||||
@@ -156,14 +168,18 @@ class StreamChatClient {
|
|||||||
/// Your project Stream Chat base url.
|
/// Your project Stream Chat base url.
|
||||||
final String baseURL;
|
final String baseURL;
|
||||||
|
|
||||||
/// A function in which you send a request to your own backend to get a Stream Chat API token.
|
/// A function in which you send a request to your own backend to get a Stream
|
||||||
|
/// Chat API token.
|
||||||
|
///
|
||||||
/// The token will be the return value of the function.
|
/// The token will be the return value of the function.
|
||||||
/// It's used by the client to refresh the token once expired or to connect the user without a predefined token using [connectUserWithProvider].
|
/// It's used by the client to refresh the token once expired or to connect
|
||||||
|
/// the user without a predefined token using [connectUserWithProvider].
|
||||||
final TokenProvider tokenProvider;
|
final TokenProvider tokenProvider;
|
||||||
|
|
||||||
/// [Dio] httpClient
|
/// [Dio] httpClient
|
||||||
/// It's be chosen because it's easy to use and supports interesting features out of the box
|
/// It's be chosen because it's easy to use and supports interesting features
|
||||||
/// (Interceptors, Global configuration, FormData, File downloading etc.)
|
/// out of the box (Interceptors, Global configuration, FormData,
|
||||||
|
/// File downloading etc.)
|
||||||
@visibleForTesting
|
@visibleForTesting
|
||||||
Dio httpClient = Dio();
|
Dio httpClient = Dio();
|
||||||
|
|
||||||
@@ -234,7 +250,7 @@ class StreamChatClient {
|
|||||||
(options.data is Map || options.data == null)) {
|
(options.data is Map || options.data == null)) {
|
||||||
options.data = {
|
options.data = {
|
||||||
'connection_id': _connectionId,
|
'connection_id': _connectionId,
|
||||||
...(options.data ?? {}),
|
...options.data ?? {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,7 +294,7 @@ class StreamChatClient {
|
|||||||
await _disconnect();
|
await _disconnect();
|
||||||
|
|
||||||
final newToken = await tokenProvider(userId);
|
final newToken = await tokenProvider(userId);
|
||||||
await Future.delayed(Duration(seconds: 4));
|
await Future.delayed(const Duration(seconds: 4));
|
||||||
token = newToken;
|
token = newToken;
|
||||||
|
|
||||||
httpClient.unlock();
|
httpClient.unlock();
|
||||||
@@ -312,7 +328,10 @@ class StreamChatClient {
|
|||||||
};
|
};
|
||||||
return (LogRecord record) {
|
return (LogRecord record) {
|
||||||
print(
|
print(
|
||||||
'(${record.time}) ${levelEmojiMapper[record.level.name] ?? record.level.name} ${record.loggerName} ${record.message}');
|
'(${record.time}) '
|
||||||
|
'${levelEmojiMapper[record.level.name] ?? record.level.name} '
|
||||||
|
'${record.loggerName} ${record.message}',
|
||||||
|
);
|
||||||
if (record.stackTrace != null) {
|
if (record.stackTrace != null) {
|
||||||
print(record.stackTrace);
|
print(record.stackTrace);
|
||||||
}
|
}
|
||||||
@@ -321,11 +340,10 @@ class StreamChatClient {
|
|||||||
|
|
||||||
Logger _detachedLogger(
|
Logger _detachedLogger(
|
||||||
String name,
|
String name,
|
||||||
) {
|
) =>
|
||||||
return Logger.detached(name)
|
Logger.detached(name)
|
||||||
..level = logLevel
|
..level = logLevel
|
||||||
..onRecord.listen(logHandlerFunction ?? _getDefaultLogHandler());
|
..onRecord.listen(logHandlerFunction ?? _getDefaultLogHandler());
|
||||||
}
|
|
||||||
|
|
||||||
void _setupLogger() {
|
void _setupLogger() {
|
||||||
logger.level = logLevel;
|
logger.level = logLevel;
|
||||||
@@ -386,7 +404,8 @@ class StreamChatClient {
|
|||||||
/// Set the current user using the [tokenProvider] to fetch the token.
|
/// Set the current user using the [tokenProvider] to fetch the token.
|
||||||
/// It returns a [Future] that resolves when the connection is setup.
|
/// It returns a [Future] that resolves when the connection is setup.
|
||||||
@Deprecated(
|
@Deprecated(
|
||||||
'Use `connectUserWithProvider` instead. Will be removed in Future releases')
|
'Use `connectUserWithProvider` instead. Will be removed in Future releases',
|
||||||
|
)
|
||||||
Future<Event> setUserWithProvider(User user) => connectUserWithProvider(user);
|
Future<Event> setUserWithProvider(User user) => connectUserWithProvider(user);
|
||||||
|
|
||||||
/// Connects the current user using the [tokenProvider] to fetch the token.
|
/// Connects the current user using the [tokenProvider] to fetch the token.
|
||||||
@@ -521,6 +540,7 @@ class StreamChatClient {
|
|||||||
}).catchError((err, stacktrace) {
|
}).catchError((err, stacktrace) {
|
||||||
logger.severe('error connecting ws', err, stacktrace);
|
logger.severe('error connecting ws', err, stacktrace);
|
||||||
if (err is Map) {
|
if (err is Map) {
|
||||||
|
// ignore: only_throw_errors
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -558,13 +578,12 @@ class StreamChatClient {
|
|||||||
res.events.sort((a, b) => a.createdAt.compareTo(b.createdAt));
|
res.events.sort((a, b) => a.createdAt.compareTo(b.createdAt));
|
||||||
|
|
||||||
res.events.forEach((element) {
|
res.events.forEach((element) {
|
||||||
logger.fine('element.type: ${element.type}');
|
logger
|
||||||
logger.fine('element.message.text: ${element.message?.text}');
|
..fine('element.type: ${element.type}')
|
||||||
|
..fine('element.message.text: ${element.message?.text}');
|
||||||
});
|
});
|
||||||
|
|
||||||
res.events.forEach((event) {
|
res.events.forEach(handleEvent);
|
||||||
handleEvent(event);
|
|
||||||
});
|
|
||||||
|
|
||||||
await chatPersistenceClient?.updateLastSyncAt(DateTime.now());
|
await chatPersistenceClient?.updateLastSyncAt(DateTime.now());
|
||||||
_synced = true;
|
_synced = true;
|
||||||
@@ -573,9 +592,7 @@ class StreamChatClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String _asMap(sort) {
|
String _asMap(sort) => sort?.map((s) => s.toJson().toString())?.join('');
|
||||||
return sort?.map((s) => s.toJson().toString())?.join('');
|
|
||||||
}
|
|
||||||
|
|
||||||
final _queryChannelsStreams = <String, Future<List<Channel>>>{};
|
final _queryChannelsStreams = <String, Future<List<Channel>>>{};
|
||||||
|
|
||||||
@@ -584,13 +601,14 @@ class StreamChatClient {
|
|||||||
Map<String, dynamic> filter,
|
Map<String, dynamic> filter,
|
||||||
List<SortOption<ChannelModel>> sort,
|
List<SortOption<ChannelModel>> sort,
|
||||||
Map<String, dynamic> options,
|
Map<String, dynamic> options,
|
||||||
PaginationParams paginationParams = const PaginationParams(limit: 10),
|
PaginationParams paginationParams = const PaginationParams(),
|
||||||
int messageLimit,
|
int messageLimit,
|
||||||
bool preferOffline = false,
|
bool preferOffline = false,
|
||||||
bool waitForConnect = true,
|
bool waitForConnect = true,
|
||||||
}) async* {
|
}) async* {
|
||||||
final hash = base64.encode(utf8.encode(
|
final hash = base64.encode(utf8.encode(
|
||||||
'$filter${_asMap(sort)}$options${paginationParams?.toJson()}$messageLimit$preferOffline',
|
'$filter${_asMap(sort)}$options${paginationParams?.toJson()}'
|
||||||
|
'$messageLimit$preferOffline',
|
||||||
));
|
));
|
||||||
|
|
||||||
if (_queryChannelsStreams.containsKey(hash)) {
|
if (_queryChannelsStreams.containsKey(hash)) {
|
||||||
@@ -627,7 +645,7 @@ class StreamChatClient {
|
|||||||
List<SortOption<ChannelModel>> sort,
|
List<SortOption<ChannelModel>> sort,
|
||||||
Map<String, dynamic> options,
|
Map<String, dynamic> options,
|
||||||
int messageLimit,
|
int messageLimit,
|
||||||
PaginationParams paginationParams = const PaginationParams(limit: 10),
|
PaginationParams paginationParams = const PaginationParams(),
|
||||||
bool waitForConnect = true,
|
bool waitForConnect = true,
|
||||||
}) async {
|
}) async {
|
||||||
if (waitForConnect) {
|
if (waitForConnect) {
|
||||||
@@ -650,7 +668,7 @@ class StreamChatClient {
|
|||||||
'presence': false,
|
'presence': false,
|
||||||
};
|
};
|
||||||
|
|
||||||
var payload = <String, dynamic>{
|
final payload = <String, dynamic>{
|
||||||
'filter_conditions': filter,
|
'filter_conditions': filter,
|
||||||
'sort': sort,
|
'sort': sort,
|
||||||
};
|
};
|
||||||
@@ -682,9 +700,12 @@ class StreamChatClient {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if ((res.channels ?? []).isEmpty && (paginationParams?.offset ?? 0) == 0) {
|
if ((res.channels ?? []).isEmpty && (paginationParams?.offset ?? 0) == 0) {
|
||||||
logger.warning('''We could not find any channel for this query.
|
logger.warning(
|
||||||
|
'''
|
||||||
|
We could not find any channel for this query.
|
||||||
Please make sure to take a look at the Flutter tutorial: https://getstream.io/chat/flutter/tutorial
|
Please make sure to take a look at the Flutter tutorial: https://getstream.io/chat/flutter/tutorial
|
||||||
If your application already has users and channels, you might need to adjust your query channel as explained in the docs https://getstream.io/chat/docs/query_channels/?language=dart''');
|
If your application already has users and channels, you might need to adjust your query channel as explained in the docs https://getstream.io/chat/docs/query_channels/?language=dart''',
|
||||||
|
);
|
||||||
return <Channel>[];
|
return <Channel>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -715,7 +736,7 @@ class StreamChatClient {
|
|||||||
Future<List<Channel>> queryChannelsOffline({
|
Future<List<Channel>> queryChannelsOffline({
|
||||||
@required Map<String, dynamic> filter,
|
@required Map<String, dynamic> filter,
|
||||||
@required List<SortOption<ChannelModel>> sort,
|
@required List<SortOption<ChannelModel>> sort,
|
||||||
PaginationParams paginationParams = const PaginationParams(limit: 10),
|
PaginationParams paginationParams = const PaginationParams(),
|
||||||
}) async {
|
}) async {
|
||||||
final offlineChannels = await chatPersistenceClient?.getChannelStates(
|
final offlineChannels = await chatPersistenceClient?.getChannelStates(
|
||||||
filter: filter,
|
filter: filter,
|
||||||
@@ -771,6 +792,7 @@ class StreamChatClient {
|
|||||||
);
|
);
|
||||||
return response;
|
return response;
|
||||||
} on DioError catch (error) {
|
} on DioError catch (error) {
|
||||||
|
// ignore: only_throw_errors
|
||||||
throw _parseError(error);
|
throw _parseError(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -791,6 +813,7 @@ class StreamChatClient {
|
|||||||
);
|
);
|
||||||
return response;
|
return response;
|
||||||
} on DioError catch (error) {
|
} on DioError catch (error) {
|
||||||
|
// ignore: only_throw_errors
|
||||||
throw _parseError(error);
|
throw _parseError(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -809,6 +832,7 @@ class StreamChatClient {
|
|||||||
);
|
);
|
||||||
return response;
|
return response;
|
||||||
} on DioError catch (error) {
|
} on DioError catch (error) {
|
||||||
|
// ignore: only_throw_errors
|
||||||
throw _parseError(error);
|
throw _parseError(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -827,6 +851,7 @@ class StreamChatClient {
|
|||||||
);
|
);
|
||||||
return response;
|
return response;
|
||||||
} on DioError catch (error) {
|
} on DioError catch (error) {
|
||||||
|
// ignore: only_throw_errors
|
||||||
throw _parseError(error);
|
throw _parseError(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -845,6 +870,7 @@ class StreamChatClient {
|
|||||||
);
|
);
|
||||||
return response;
|
return response;
|
||||||
} on DioError catch (error) {
|
} on DioError catch (error) {
|
||||||
|
// ignore: only_throw_errors
|
||||||
throw _parseError(error);
|
throw _parseError(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -864,8 +890,8 @@ class StreamChatClient {
|
|||||||
|
|
||||||
String get _authType => _anonymous ? 'anonymous' : 'jwt';
|
String get _authType => _anonymous ? 'anonymous' : 'jwt';
|
||||||
|
|
||||||
String get _userAgent =>
|
String get _userAgent => 'stream-chat-dart-client-${CurrentPlatform.name}-'
|
||||||
'stream-chat-dart-client-${CurrentPlatform.name}-${PACKAGE_VERSION.split('+')[0]}';
|
'${PACKAGE_VERSION.split('+')[0]}';
|
||||||
|
|
||||||
Map<String, String> get _commonQueryParams => {
|
Map<String, String> get _commonQueryParams => {
|
||||||
'user_id': state.user?.id,
|
'user_id': state.user?.id,
|
||||||
@@ -873,14 +899,15 @@ class StreamChatClient {
|
|||||||
'connection_id': _connectionId,
|
'connection_id': _connectionId,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Set the current user with an anonymous id, this triggers a connection to the API.
|
/// Set the current user with an anonymous id, this triggers a connection to
|
||||||
/// It returns a [Future] that resolves when the connection is setup.
|
/// the API. It returns a [Future] that resolves when the connection is setup.
|
||||||
@Deprecated(
|
@Deprecated(
|
||||||
'Use `connectAnonymousUser` instead. Will be removed in Future releases')
|
'Use `connectAnonymousUser` instead. Will be removed in Future releases')
|
||||||
Future<Event> setAnonymousUser() => connectAnonymousUser();
|
Future<Event> setAnonymousUser() => connectAnonymousUser();
|
||||||
|
|
||||||
/// Connects the current user with an anonymous id, this triggers a connection to the API.
|
/// Connects the current user with an anonymous id, this triggers a connection
|
||||||
/// It returns a [Future] that resolves when the connection is setup.
|
/// to the API. It returns a [Future] that resolves when the connection is
|
||||||
|
/// setup.
|
||||||
Future<Event> connectAnonymousUser() async {
|
Future<Event> connectAnonymousUser() async {
|
||||||
if (_connectCompleter != null && !_connectCompleter.isCompleted) {
|
if (_connectCompleter != null && !_connectCompleter.isCompleted) {
|
||||||
logger.warning('Already connecting');
|
logger.warning('Already connecting');
|
||||||
@@ -923,14 +950,14 @@ class StreamChatClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Closes the websocket connection and resets the client
|
/// Closes the websocket connection and resets the client
|
||||||
/// If [flushChatPersistence] is true the client deletes all offline user's data
|
/// If [flushChatPersistence] is true the client deletes all offline
|
||||||
/// If [clearUser] is true the client unsets the current user
|
/// user's data. If [clearUser] is true the client unsets the current user
|
||||||
Future<void> disconnect({
|
Future<void> disconnect({
|
||||||
bool flushChatPersistence = false,
|
bool flushChatPersistence = false,
|
||||||
bool clearUser = false,
|
bool clearUser = false,
|
||||||
}) async {
|
}) async {
|
||||||
logger.info(
|
logger.info('Disconnecting flushOfflineStorage: $flushChatPersistence; '
|
||||||
'Disconnecting flushOfflineStorage: $flushChatPersistence; clearUser: $clearUser');
|
'clearUser: $clearUser');
|
||||||
|
|
||||||
await chatPersistenceClient?.disconnect(flush: flushChatPersistence);
|
await chatPersistenceClient?.disconnect(flush: flushChatPersistence);
|
||||||
chatPersistenceClient = null;
|
chatPersistenceClient = null;
|
||||||
@@ -966,9 +993,7 @@ class StreamChatClient {
|
|||||||
final payload = <String, dynamic>{
|
final payload = <String, dynamic>{
|
||||||
'filter_conditions': filter ?? {},
|
'filter_conditions': filter ?? {},
|
||||||
'sort': sort,
|
'sort': sort,
|
||||||
};
|
}..addAll(defaultOptions);
|
||||||
|
|
||||||
payload.addAll(defaultOptions);
|
|
||||||
|
|
||||||
if (pagination != null) {
|
if (pagination != null) {
|
||||||
payload.addAll(pagination.toJson());
|
payload.addAll(pagination.toJson());
|
||||||
@@ -1016,7 +1041,7 @@ class StreamChatClient {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}());
|
}(), 'Check incoming params.');
|
||||||
|
|
||||||
final payload = {
|
final payload = {
|
||||||
'filter_conditions': filters,
|
'filter_conditions': filters,
|
||||||
@@ -1041,15 +1066,14 @@ class StreamChatClient {
|
|||||||
String channelType, {
|
String channelType, {
|
||||||
ProgressCallback onSendProgress,
|
ProgressCallback onSendProgress,
|
||||||
CancelToken cancelToken,
|
CancelToken cancelToken,
|
||||||
}) {
|
}) =>
|
||||||
return attachmentFileUploader.sendFile(
|
attachmentFileUploader.sendFile(
|
||||||
file,
|
file,
|
||||||
channelId,
|
channelId,
|
||||||
channelType,
|
channelType,
|
||||||
onSendProgress: onSendProgress,
|
onSendProgress: onSendProgress,
|
||||||
cancelToken: cancelToken,
|
cancelToken: cancelToken,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
/// Send a [image] to the [channelId] of type [channelType]
|
/// Send a [image] to the [channelId] of type [channelType]
|
||||||
Future<SendImageResponse> sendImage(
|
Future<SendImageResponse> sendImage(
|
||||||
@@ -1058,15 +1082,14 @@ class StreamChatClient {
|
|||||||
String channelType, {
|
String channelType, {
|
||||||
ProgressCallback onSendProgress,
|
ProgressCallback onSendProgress,
|
||||||
CancelToken cancelToken,
|
CancelToken cancelToken,
|
||||||
}) {
|
}) =>
|
||||||
return attachmentFileUploader.sendImage(
|
attachmentFileUploader.sendImage(
|
||||||
image,
|
image,
|
||||||
channelId,
|
channelId,
|
||||||
channelType,
|
channelType,
|
||||||
onSendProgress: onSendProgress,
|
onSendProgress: onSendProgress,
|
||||||
cancelToken: cancelToken,
|
cancelToken: cancelToken,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
/// Delete a file from this channel
|
/// Delete a file from this channel
|
||||||
Future<EmptyResponse> deleteFile(
|
Future<EmptyResponse> deleteFile(
|
||||||
@@ -1074,14 +1097,13 @@ class StreamChatClient {
|
|||||||
String channelId,
|
String channelId,
|
||||||
String channelType, {
|
String channelType, {
|
||||||
CancelToken cancelToken,
|
CancelToken cancelToken,
|
||||||
}) {
|
}) =>
|
||||||
return attachmentFileUploader.deleteFile(
|
attachmentFileUploader.deleteFile(
|
||||||
url,
|
url,
|
||||||
channelId,
|
channelId,
|
||||||
channelType,
|
channelType,
|
||||||
cancelToken: cancelToken,
|
cancelToken: cancelToken,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
/// Delete an image from this channel
|
/// Delete an image from this channel
|
||||||
Future<EmptyResponse> deleteImage(
|
Future<EmptyResponse> deleteImage(
|
||||||
@@ -1089,14 +1111,13 @@ class StreamChatClient {
|
|||||||
String channelId,
|
String channelId,
|
||||||
String channelType, {
|
String channelType, {
|
||||||
CancelToken cancelToken,
|
CancelToken cancelToken,
|
||||||
}) {
|
}) =>
|
||||||
return attachmentFileUploader.deleteImage(
|
attachmentFileUploader.deleteImage(
|
||||||
url,
|
url,
|
||||||
channelId,
|
channelId,
|
||||||
channelType,
|
channelType,
|
||||||
cancelToken: cancelToken,
|
cancelToken: cancelToken,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a device for Push Notifications.
|
/// Add a device for Push Notifications.
|
||||||
Future<EmptyResponse> addDevice(String id, PushProvider pushProvider) async {
|
Future<EmptyResponse> addDevice(String id, PushProvider pushProvider) async {
|
||||||
@@ -1146,9 +1167,8 @@ class StreamChatClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Update or Create the given user object.
|
/// Update or Create the given user object.
|
||||||
Future<UpdateUsersResponse> updateUser(User user) async {
|
Future<UpdateUsersResponse> updateUser(User user) async =>
|
||||||
return updateUsers([user]);
|
updateUsers([user]);
|
||||||
}
|
|
||||||
|
|
||||||
/// Batch update a list of users
|
/// Batch update a list of users
|
||||||
Future<UpdateUsersResponse> updateUsers(List<User> users) async {
|
Future<UpdateUsersResponse> updateUsers(List<User> users) async {
|
||||||
@@ -1197,23 +1217,21 @@ class StreamChatClient {
|
|||||||
Future<EmptyResponse> shadowBan(
|
Future<EmptyResponse> shadowBan(
|
||||||
String targetID, [
|
String targetID, [
|
||||||
Map<String, dynamic> options = const {},
|
Map<String, dynamic> options = const {},
|
||||||
]) async {
|
]) async =>
|
||||||
return banUser(targetID, {
|
banUser(targetID, {
|
||||||
'shadow': true,
|
'shadow': true,
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
/// Removes shadow ban from a user
|
/// Removes shadow ban from a user
|
||||||
Future<EmptyResponse> removeShadowBan(
|
Future<EmptyResponse> removeShadowBan(
|
||||||
String targetID, [
|
String targetID, [
|
||||||
Map<String, dynamic> options = const {},
|
Map<String, dynamic> options = const {},
|
||||||
]) async {
|
]) async =>
|
||||||
return unbanUser(targetID, {
|
unbanUser(targetID, {
|
||||||
'shadow': true,
|
'shadow': true,
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
/// Mutes a user
|
/// Mutes a user
|
||||||
Future<EmptyResponse> muteUser(String targetID) async {
|
Future<EmptyResponse> muteUser(String targetID) async {
|
||||||
@@ -1312,7 +1330,7 @@ class StreamChatClient {
|
|||||||
throw ArgumentError('Invalid timeout or Expiration date');
|
throw ArgumentError('Invalid timeout or Expiration date');
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}());
|
}(), 'Check whether time out is valid');
|
||||||
|
|
||||||
DateTime pinExpires;
|
DateTime pinExpires;
|
||||||
if (timeoutOrExpirationDate is DateTime) {
|
if (timeoutOrExpirationDate is DateTime) {
|
||||||
@@ -1328,15 +1346,12 @@ class StreamChatClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Unpins provided message
|
/// Unpins provided message
|
||||||
Future<UpdateMessageResponse> unpinMessage(Message message) {
|
Future<UpdateMessageResponse> unpinMessage(Message message) =>
|
||||||
return updateMessage(message.copyWith(pinned: false));
|
updateMessage(message.copyWith(pinned: false));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The class that handles the state of the channel listening to the events
|
/// The class that handles the state of the channel listening to the events
|
||||||
class ClientState {
|
class ClientState {
|
||||||
final _subscriptions = <StreamSubscription>[];
|
|
||||||
|
|
||||||
/// Creates a new instance listening to events and updating the state
|
/// Creates a new instance listening to events and updating the state
|
||||||
ClientState(this._client) {
|
ClientState(this._client) {
|
||||||
_subscriptions.addAll([
|
_subscriptions.addAll([
|
||||||
@@ -1358,16 +1373,12 @@ class ClientState {
|
|||||||
.on()
|
.on()
|
||||||
.where((event) => event.unreadChannels != null)
|
.where((event) => event.unreadChannels != null)
|
||||||
.map((e) => e.unreadChannels)
|
.map((e) => e.unreadChannels)
|
||||||
.listen((unreadChannels) {
|
.listen(_unreadChannelsController.add),
|
||||||
_unreadChannelsController.add(unreadChannels);
|
|
||||||
}),
|
|
||||||
_client
|
_client
|
||||||
.on()
|
.on()
|
||||||
.where((event) => event.totalUnreadCount != null)
|
.where((event) => event.totalUnreadCount != null)
|
||||||
.map((e) => e.totalUnreadCount)
|
.map((e) => e.totalUnreadCount)
|
||||||
.listen((totalUnreadCount) {
|
.listen(_totalUnreadCountController.add),
|
||||||
_totalUnreadCountController.add(totalUnreadCount);
|
|
||||||
}),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
_listenChannelDeleted();
|
_listenChannelDeleted();
|
||||||
@@ -1377,6 +1388,8 @@ class ClientState {
|
|||||||
_listenUserUpdated();
|
_listenUserUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final _subscriptions = <StreamSubscription>[];
|
||||||
|
|
||||||
/// Used internally for optimistic update of unread count
|
/// Used internally for optimistic update of unread count
|
||||||
set totalUnreadCount(int unreadCount) {
|
set totalUnreadCount(int unreadCount) {
|
||||||
_totalUnreadCountController?.add(unreadCount ?? 0);
|
_totalUnreadCountController?.add(unreadCount ?? 0);
|
||||||
|
|||||||
@@ -101,18 +101,17 @@ abstract class ChatPersistenceClient {
|
|||||||
Future<void> updateChannelQueries(
|
Future<void> updateChannelQueries(
|
||||||
Map<String, dynamic> filter,
|
Map<String, dynamic> filter,
|
||||||
List<String> cids,
|
List<String> cids,
|
||||||
|
// ignore: avoid_positional_boolean_parameters
|
||||||
bool clearQueryCache,
|
bool clearQueryCache,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Remove a message by [messageId]
|
/// Remove a message by [messageId]
|
||||||
Future<void> deleteMessageById(String messageId) {
|
Future<void> deleteMessageById(String messageId) =>
|
||||||
return deleteMessageByIds([messageId]);
|
deleteMessageByIds([messageId]);
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove a pinned message by [messageId]
|
/// Remove a pinned message by [messageId]
|
||||||
Future<void> deletePinnedMessageById(String messageId) {
|
Future<void> deletePinnedMessageById(String messageId) =>
|
||||||
return deletePinnedMessageByIds([messageId]);
|
deletePinnedMessageByIds([messageId]);
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove a message by [messageIds]
|
/// Remove a message by [messageIds]
|
||||||
Future<void> deleteMessageByIds(List<String> messageIds);
|
Future<void> deleteMessageByIds(List<String> messageIds);
|
||||||
@@ -121,14 +120,11 @@ abstract class ChatPersistenceClient {
|
|||||||
Future<void> deletePinnedMessageByIds(List<String> messageIds);
|
Future<void> deletePinnedMessageByIds(List<String> messageIds);
|
||||||
|
|
||||||
/// Remove a message by channel [cid]
|
/// Remove a message by channel [cid]
|
||||||
Future<void> deleteMessageByCid(String cid) {
|
Future<void> deleteMessageByCid(String cid) => deleteMessageByCids([cid]);
|
||||||
return deleteMessageByCids([cid]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove a pinned message by channel [cid]
|
/// Remove a pinned message by channel [cid]
|
||||||
Future<void> deletePinnedMessageByCid(String cid) {
|
Future<void> deletePinnedMessageByCid(String cid) async =>
|
||||||
return deletePinnedMessageByCids([cid]);
|
deletePinnedMessageByCids([cid]);
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove a message by message [cids]
|
/// Remove a message by message [cids]
|
||||||
Future<void> deleteMessageByCids(List<String> cids);
|
Future<void> deleteMessageByCids(List<String> cids);
|
||||||
@@ -175,9 +171,8 @@ abstract class ChatPersistenceClient {
|
|||||||
Future<void> deleteMembersByCids(List<String> cids);
|
Future<void> deleteMembersByCids(List<String> cids);
|
||||||
|
|
||||||
/// Update the channel state data using [channelState]
|
/// Update the channel state data using [channelState]
|
||||||
Future<void> updateChannelState(ChannelState channelState) {
|
Future<void> updateChannelState(ChannelState channelState) =>
|
||||||
return updateChannelStates([channelState]);
|
updateChannelStates([channelState]);
|
||||||
}
|
|
||||||
|
|
||||||
/// Update list of channel states
|
/// Update list of channel states
|
||||||
Future<void> updateChannelStates(List<ChannelState> channelStates) async {
|
Future<void> updateChannelStates(List<ChannelState> channelStates) async {
|
||||||
@@ -195,31 +190,31 @@ abstract class ChatPersistenceClient {
|
|||||||
deleteMembers,
|
deleteMembers,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
final channels = channelStates.map((it) {
|
final channels =
|
||||||
return it.channel;
|
channelStates.map((it) => it.channel).where((it) => it != null);
|
||||||
}).where((it) => it != null);
|
|
||||||
|
|
||||||
final reactions = channelStates.expand((it) => it.messages).expand((it) {
|
final reactions = channelStates
|
||||||
return [
|
.expand((it) => it.messages)
|
||||||
if (it.ownReactions != null)
|
.expand((it) => [
|
||||||
...it.ownReactions.where((r) => r.userId != null),
|
if (it.ownReactions != null)
|
||||||
if (it.latestReactions != null)
|
...it.ownReactions.where((r) => r.userId != null),
|
||||||
...it.latestReactions.where((r) => r.userId != null)
|
if (it.latestReactions != null)
|
||||||
];
|
...it.latestReactions.where((r) => r.userId != null)
|
||||||
}).where((it) => it != null);
|
])
|
||||||
|
.where((it) => it != null);
|
||||||
|
|
||||||
final users = channelStates
|
final users = channelStates
|
||||||
.map((cs) => [
|
.map((cs) => [
|
||||||
cs.channel?.createdBy,
|
cs.channel?.createdBy,
|
||||||
...cs.messages?.map((m) {
|
...cs.messages
|
||||||
return [
|
?.map((m) => [
|
||||||
m.user,
|
m.user,
|
||||||
if (m.latestReactions != null)
|
if (m.latestReactions != null)
|
||||||
...m.latestReactions.map((r) => r.user),
|
...m.latestReactions.map((r) => r.user),
|
||||||
if (m.ownReactions != null)
|
if (m.ownReactions != null)
|
||||||
...m.ownReactions.map((r) => r.user),
|
...m.ownReactions.map((r) => r.user),
|
||||||
];
|
])
|
||||||
})?.expand((v) => v),
|
?.expand((v) => v),
|
||||||
if (cs.read != null) ...cs.read.map((r) => r.user),
|
if (cs.read != null) ...cs.read.map((r) => r.user),
|
||||||
if (cs.members != null) ...cs.members.map((m) => m.user),
|
if (cs.members != null) ...cs.members.map((m) => m.user),
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -2,6 +2,13 @@ import 'dart:convert';
|
|||||||
|
|
||||||
/// Exception related to api calls
|
/// Exception related to api calls
|
||||||
class ApiError extends Error {
|
class ApiError extends Error {
|
||||||
|
/// Creates a new ApiError instance using the response body and status code
|
||||||
|
ApiError(this.body, this.status) : jsonData = _decode(body) {
|
||||||
|
if (jsonData != null && jsonData.containsKey('code')) {
|
||||||
|
_code = jsonData['code'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Raw body of the response
|
/// Raw body of the response
|
||||||
final String body;
|
final String body;
|
||||||
|
|
||||||
@@ -26,13 +33,6 @@ class ApiError extends Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new ApiError instance using the response body and status code
|
|
||||||
ApiError(this.body, this.status) : jsonData = _decode(body) {
|
|
||||||
if (jsonData != null && jsonData.containsKey('code')) {
|
|
||||||
_code = jsonData['code'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) =>
|
bool operator ==(Object other) =>
|
||||||
identical(this, other) ||
|
identical(this, other) ||
|
||||||
@@ -48,7 +48,6 @@ class ApiError extends Error {
|
|||||||
body.hashCode ^ jsonData.hashCode ^ status.hashCode ^ _code.hashCode;
|
body.hashCode ^ jsonData.hashCode ^ status.hashCode ^ _code.hashCode;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() => 'ApiError{body: $body, jsonData: $jsonData, '
|
||||||
return 'ApiError{body: $body, jsonData: $jsonData, status: $status, code: $_code}';
|
'status: $status, code: $_code}';
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
/// Useful extension functions for [Map]
|
/// Useful extension functions for [Map]
|
||||||
extension MapX on Map {
|
extension MapX on Map {
|
||||||
/// Returns a new map with null keys or values removed
|
/// Returns a new map with null keys or values removed
|
||||||
Map<String, dynamic> get nullProtected {
|
Map<String, dynamic> get nullProtected =>
|
||||||
return {...this}..removeWhere((key, value) => key == null || value == null);
|
{...this}..removeWhere((key, value) => key == null || value == null);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,12 @@ part 'action.g.dart';
|
|||||||
/// The class that contains the information about an action
|
/// The class that contains the information about an action
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class Action {
|
class Action {
|
||||||
|
/// Constructor used for json serialization
|
||||||
|
Action({this.name, this.style, this.text, this.type, this.value});
|
||||||
|
|
||||||
|
/// Create a new instance from a json
|
||||||
|
factory Action.fromJson(Map<String, dynamic> json) => _$ActionFromJson(json);
|
||||||
|
|
||||||
/// The name of the action
|
/// The name of the action
|
||||||
final String name;
|
final String name;
|
||||||
|
|
||||||
@@ -20,12 +26,6 @@ class Action {
|
|||||||
/// The value of the action
|
/// The value of the action
|
||||||
final String value;
|
final String value;
|
||||||
|
|
||||||
/// Constructor used for json serialization
|
|
||||||
Action({this.name, this.style, this.text, this.type, this.value});
|
|
||||||
|
|
||||||
/// Create a new instance from a json
|
|
||||||
factory Action.fromJson(Map<String, dynamic> json) => _$ActionFromJson(json);
|
|
||||||
|
|
||||||
/// Serialize to json
|
/// Serialize to json
|
||||||
Map<String, dynamic> toJson() => _$ActionToJson(this);
|
Map<String, dynamic> toJson() => _$ActionToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,61 @@
|
|||||||
// ignore_for_file: public_member_api_docs
|
// ignore_for_file: public_member_api_docs
|
||||||
|
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
import 'package:stream_chat/src/models/action.dart';
|
||||||
import 'package:stream_chat/src/models/attachment_file.dart';
|
import 'package:stream_chat/src/models/attachment_file.dart';
|
||||||
|
import 'package:stream_chat/src/models/serialization.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
import 'action.dart';
|
|
||||||
import 'serialization.dart';
|
|
||||||
|
|
||||||
part 'attachment.g.dart';
|
part 'attachment.g.dart';
|
||||||
|
|
||||||
/// The class that contains the information about an attachment
|
/// The class that contains the information about an attachment
|
||||||
@JsonSerializable(includeIfNull: false)
|
@JsonSerializable(includeIfNull: false)
|
||||||
class Attachment {
|
class Attachment {
|
||||||
///The attachment type based on the URL resource. This can be: audio, image or video
|
/// Constructor used for json serialization
|
||||||
|
Attachment({
|
||||||
|
String id,
|
||||||
|
this.type,
|
||||||
|
this.titleLink,
|
||||||
|
String title,
|
||||||
|
this.thumbUrl,
|
||||||
|
this.text,
|
||||||
|
this.pretext,
|
||||||
|
this.ogScrapeUrl,
|
||||||
|
this.imageUrl,
|
||||||
|
this.footerIcon,
|
||||||
|
this.footer,
|
||||||
|
this.fields,
|
||||||
|
this.fallback,
|
||||||
|
this.color,
|
||||||
|
this.authorName,
|
||||||
|
this.authorLink,
|
||||||
|
this.authorIcon,
|
||||||
|
this.assetUrl,
|
||||||
|
this.actions,
|
||||||
|
this.extraData,
|
||||||
|
this.file,
|
||||||
|
UploadState uploadState,
|
||||||
|
}) : id = id ?? Uuid().v4(),
|
||||||
|
title = title ?? file?.name,
|
||||||
|
localUri = file?.path != null ? Uri.parse(file.path) : null {
|
||||||
|
this.uploadState = uploadState ??
|
||||||
|
((assetUrl != null || imageUrl != null)
|
||||||
|
? const UploadState.success()
|
||||||
|
: const UploadState.preparing());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new instance from a json
|
||||||
|
factory Attachment.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$AttachmentFromJson(
|
||||||
|
Serialization.moveToExtraDataFromRoot(json, topLevelFields));
|
||||||
|
|
||||||
|
/// Create a new instance from a db data
|
||||||
|
factory Attachment.fromData(Map<String, dynamic> json) =>
|
||||||
|
_$AttachmentFromJson(Serialization.moveToExtraDataFromRoot(
|
||||||
|
json, topLevelFields + dbSpecificTopLevelFields));
|
||||||
|
|
||||||
|
///The attachment type based on the URL resource. This can be: audio,
|
||||||
|
///image or video
|
||||||
final String type;
|
final String type;
|
||||||
|
|
||||||
///The link to which the attachment message points to.
|
///The link to which the attachment message points to.
|
||||||
@@ -21,10 +64,12 @@ class Attachment {
|
|||||||
/// The attachment title
|
/// The attachment title
|
||||||
final String title;
|
final String title;
|
||||||
|
|
||||||
/// The URL to the attached file thumbnail. You can use this to represent the attached link.
|
/// The URL to the attached file thumbnail. You can use this to represent the
|
||||||
|
/// attached link.
|
||||||
final String thumbUrl;
|
final String thumbUrl;
|
||||||
|
|
||||||
/// The attachment text. It will be displayed in the channel next to the original message.
|
/// The attachment text. It will be displayed in the channel next to the
|
||||||
|
/// original message.
|
||||||
final String text;
|
final String text;
|
||||||
|
|
||||||
/// Optional text that appears above the attachment block
|
/// Optional text that appears above the attachment block
|
||||||
@@ -33,7 +78,8 @@ class Attachment {
|
|||||||
/// The original URL that was used to scrape this attachment.
|
/// The original URL that was used to scrape this attachment.
|
||||||
final String ogScrapeUrl;
|
final String ogScrapeUrl;
|
||||||
|
|
||||||
/// The URL to the attached image. This is present for URL pointing to an image article (eg. Unsplash)
|
/// The URL to the attached image. This is present for URL pointing to an
|
||||||
|
/// image article (eg. Unsplash)
|
||||||
final String imageUrl;
|
final String imageUrl;
|
||||||
final String footerIcon;
|
final String footerIcon;
|
||||||
final String footer;
|
final String footer;
|
||||||
@@ -100,56 +146,11 @@ class Attachment {
|
|||||||
'file',
|
'file',
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Constructor used for json serialization
|
|
||||||
Attachment({
|
|
||||||
String id,
|
|
||||||
this.type,
|
|
||||||
this.titleLink,
|
|
||||||
String title,
|
|
||||||
this.thumbUrl,
|
|
||||||
this.text,
|
|
||||||
this.pretext,
|
|
||||||
this.ogScrapeUrl,
|
|
||||||
this.imageUrl,
|
|
||||||
this.footerIcon,
|
|
||||||
this.footer,
|
|
||||||
this.fields,
|
|
||||||
this.fallback,
|
|
||||||
this.color,
|
|
||||||
this.authorName,
|
|
||||||
this.authorLink,
|
|
||||||
this.authorIcon,
|
|
||||||
this.assetUrl,
|
|
||||||
this.actions,
|
|
||||||
this.extraData,
|
|
||||||
this.file,
|
|
||||||
UploadState uploadState,
|
|
||||||
}) : id = id ?? Uuid().v4(),
|
|
||||||
title = title ?? file?.name,
|
|
||||||
localUri = file?.path != null ? Uri.parse(file.path) : null {
|
|
||||||
this.uploadState = uploadState ??
|
|
||||||
((assetUrl != null || imageUrl != null)
|
|
||||||
? UploadState.success()
|
|
||||||
: UploadState.preparing());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new instance from a json
|
|
||||||
factory Attachment.fromJson(Map<String, dynamic> json) {
|
|
||||||
return _$AttachmentFromJson(
|
|
||||||
Serialization.moveToExtraDataFromRoot(json, topLevelFields));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Serialize to json
|
/// Serialize to json
|
||||||
Map<String, dynamic> toJson() => Serialization.moveFromExtraDataToRoot(
|
Map<String, dynamic> toJson() => Serialization.moveFromExtraDataToRoot(
|
||||||
_$AttachmentToJson(this), topLevelFields)
|
_$AttachmentToJson(this), topLevelFields)
|
||||||
..removeWhere((key, value) => dbSpecificTopLevelFields.contains(key));
|
..removeWhere((key, value) => dbSpecificTopLevelFields.contains(key));
|
||||||
|
|
||||||
/// Create a new instance from a db data
|
|
||||||
factory Attachment.fromData(Map<String, dynamic> json) {
|
|
||||||
return _$AttachmentFromJson(Serialization.moveToExtraDataFromRoot(
|
|
||||||
json, topLevelFields + dbSpecificTopLevelFields));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Serialize to db data
|
/// Serialize to db data
|
||||||
Map<String, dynamic> toData() => Serialization.moveFromExtraDataToRoot(
|
Map<String, dynamic> toData() => Serialization.moveFromExtraDataToRoot(
|
||||||
_$AttachmentToJson(this), topLevelFields + dbSpecificTopLevelFields);
|
_$AttachmentToJson(this), topLevelFields + dbSpecificTopLevelFields);
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:meta/meta.dart';
|
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
import 'package:meta/meta.dart';
|
||||||
|
|
||||||
part 'attachment_file.freezed.dart';
|
part 'attachment_file.freezed.dart';
|
||||||
|
|
||||||
part 'attachment_file.g.dart';
|
part 'attachment_file.g.dart';
|
||||||
|
|
||||||
/// Union class to hold various [UploadState] of a attachment.
|
/// Union class to hold various [UploadState] of a attachment.
|
||||||
@@ -57,8 +56,12 @@ class AttachmentFile {
|
|||||||
this.size,
|
this.size,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// The absolute path for a cached copy of this file. It can be used to create a
|
/// Create a new instance from a json
|
||||||
/// file instance with a descriptor for the given path.
|
factory AttachmentFile.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$AttachmentFileFromJson(json);
|
||||||
|
|
||||||
|
/// The absolute path for a cached copy of this file. It can be used to
|
||||||
|
/// create a file instance with a descriptor for the given path.
|
||||||
/// ```
|
/// ```
|
||||||
/// final File myFile = File(platformFile.path);
|
/// final File myFile = File(platformFile.path);
|
||||||
/// ```
|
/// ```
|
||||||
@@ -67,8 +70,8 @@ class AttachmentFile {
|
|||||||
/// File name including its extension.
|
/// File name including its extension.
|
||||||
final String name;
|
final String name;
|
||||||
|
|
||||||
/// Byte data for this file. Particularly useful if you want to manipulate its data
|
/// Byte data for this file. Particularly useful if you want to manipulate
|
||||||
/// or easily upload to somewhere else.
|
/// its data or easily upload to somewhere else.
|
||||||
@JsonKey(toJson: _toString, fromJson: _fromString)
|
@JsonKey(toJson: _toString, fromJson: _fromString)
|
||||||
final Uint8List bytes;
|
final Uint8List bytes;
|
||||||
|
|
||||||
@@ -78,11 +81,6 @@ class AttachmentFile {
|
|||||||
/// File extension for this file.
|
/// File extension for this file.
|
||||||
String get extension => name?.split('.')?.last;
|
String get extension => name?.split('.')?.last;
|
||||||
|
|
||||||
/// Create a new instance from a json
|
|
||||||
factory AttachmentFile.fromJson(Map<String, dynamic> json) {
|
|
||||||
return _$AttachmentFileFromJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Serialize to json
|
/// Serialize to json
|
||||||
Map<String, dynamic> toJson() => _$AttachmentFileToJson(this);
|
Map<String, dynamic> toJson() => _$AttachmentFileToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,34 @@
|
|||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
import 'package:stream_chat/src/models/command.dart';
|
||||||
import 'command.dart';
|
|
||||||
|
|
||||||
part 'channel_config.g.dart';
|
part 'channel_config.g.dart';
|
||||||
|
|
||||||
/// The class that contains the information about the configuration of a channel
|
/// The class that contains the information about the configuration of a channel
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class ChannelConfig {
|
class ChannelConfig {
|
||||||
|
/// Constructor used for json serialization
|
||||||
|
ChannelConfig({
|
||||||
|
this.automod,
|
||||||
|
this.commands,
|
||||||
|
this.connectEvents,
|
||||||
|
this.createdAt,
|
||||||
|
this.updatedAt,
|
||||||
|
this.maxMessageLength,
|
||||||
|
this.messageRetention,
|
||||||
|
this.mutes,
|
||||||
|
this.name,
|
||||||
|
this.reactions,
|
||||||
|
this.readEvents,
|
||||||
|
this.replies,
|
||||||
|
this.search,
|
||||||
|
this.typingEvents,
|
||||||
|
this.uploads,
|
||||||
|
this.urlEnrichment,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Create a new instance from a json
|
||||||
|
factory ChannelConfig.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$ChannelConfigFromJson(json);
|
||||||
|
|
||||||
/// Moderation configuration
|
/// Moderation configuration
|
||||||
final String automod;
|
final String automod;
|
||||||
|
|
||||||
@@ -55,30 +77,6 @@ class ChannelConfig {
|
|||||||
/// True if urls appears as attachments
|
/// True if urls appears as attachments
|
||||||
final bool urlEnrichment;
|
final bool urlEnrichment;
|
||||||
|
|
||||||
/// Constructor used for json serialization
|
|
||||||
ChannelConfig({
|
|
||||||
this.automod,
|
|
||||||
this.commands,
|
|
||||||
this.connectEvents,
|
|
||||||
this.createdAt,
|
|
||||||
this.updatedAt,
|
|
||||||
this.maxMessageLength,
|
|
||||||
this.messageRetention,
|
|
||||||
this.mutes,
|
|
||||||
this.name,
|
|
||||||
this.reactions,
|
|
||||||
this.readEvents,
|
|
||||||
this.replies,
|
|
||||||
this.search,
|
|
||||||
this.typingEvents,
|
|
||||||
this.uploads,
|
|
||||||
this.urlEnrichment,
|
|
||||||
});
|
|
||||||
|
|
||||||
/// Create a new instance from a json
|
|
||||||
factory ChannelConfig.fromJson(Map<String, dynamic> json) =>
|
|
||||||
_$ChannelConfigFromJson(json);
|
|
||||||
|
|
||||||
/// Serialize to json
|
/// Serialize to json
|
||||||
Map<String, dynamic> toJson() => _$ChannelConfigToJson(this);
|
Map<String, dynamic> toJson() => _$ChannelConfigToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,35 @@
|
|||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
import 'package:stream_chat/src/models/channel_config.dart';
|
||||||
import 'channel_config.dart';
|
import 'package:stream_chat/src/models/serialization.dart';
|
||||||
import 'serialization.dart';
|
import 'package:stream_chat/src/models/user.dart';
|
||||||
import 'user.dart';
|
|
||||||
|
|
||||||
part 'channel_model.g.dart';
|
part 'channel_model.g.dart';
|
||||||
|
|
||||||
/// The class that contains the information about a channel
|
/// The class that contains the information about a channel
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class ChannelModel {
|
class ChannelModel {
|
||||||
|
/// Constructor used for json serialization
|
||||||
|
ChannelModel({
|
||||||
|
this.id,
|
||||||
|
this.type,
|
||||||
|
this.cid,
|
||||||
|
this.config,
|
||||||
|
this.createdBy,
|
||||||
|
this.frozen,
|
||||||
|
this.lastMessageAt,
|
||||||
|
this.createdAt,
|
||||||
|
this.updatedAt,
|
||||||
|
this.deletedAt,
|
||||||
|
this.memberCount,
|
||||||
|
this.extraData,
|
||||||
|
this.team,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Create a new instance from a json
|
||||||
|
factory ChannelModel.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$ChannelModelFromJson(
|
||||||
|
Serialization.moveToExtraDataFromRoot(json, topLevelFields));
|
||||||
|
|
||||||
/// The id of this channel
|
/// The id of this channel
|
||||||
final String id;
|
final String id;
|
||||||
|
|
||||||
@@ -76,40 +97,15 @@ class ChannelModel {
|
|||||||
'team',
|
'team',
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Constructor used for json serialization
|
|
||||||
ChannelModel({
|
|
||||||
this.id,
|
|
||||||
this.type,
|
|
||||||
this.cid,
|
|
||||||
this.config,
|
|
||||||
this.createdBy,
|
|
||||||
this.frozen,
|
|
||||||
this.lastMessageAt,
|
|
||||||
this.createdAt,
|
|
||||||
this.updatedAt,
|
|
||||||
this.deletedAt,
|
|
||||||
this.memberCount,
|
|
||||||
this.extraData,
|
|
||||||
this.team,
|
|
||||||
});
|
|
||||||
|
|
||||||
/// Shortcut for channel name
|
/// Shortcut for channel name
|
||||||
String get name =>
|
String get name =>
|
||||||
extraData?.containsKey('name') == true ? extraData['name'] : cid;
|
extraData?.containsKey('name') == true ? extraData['name'] : cid;
|
||||||
|
|
||||||
/// Create a new instance from a json
|
|
||||||
factory ChannelModel.fromJson(Map<String, dynamic> json) {
|
|
||||||
return _$ChannelModelFromJson(
|
|
||||||
Serialization.moveToExtraDataFromRoot(json, topLevelFields));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Serialize to json
|
/// Serialize to json
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() => Serialization.moveFromExtraDataToRoot(
|
||||||
return Serialization.moveFromExtraDataToRoot(
|
_$ChannelModelToJson(this),
|
||||||
_$ChannelModelToJson(this),
|
topLevelFields,
|
||||||
topLevelFields,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a copy of [ChannelModel] with specified attributes overridden.
|
/// Creates a copy of [ChannelModel] with specified attributes overridden.
|
||||||
ChannelModel copyWith({
|
ChannelModel copyWith({
|
||||||
@@ -143,8 +139,8 @@ class ChannelModel {
|
|||||||
team: team ?? this.team,
|
team: team ?? this.team,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Returns a new [ChannelModel] that is a combination of this channelModel and the given
|
/// Returns a new [ChannelModel] that is a combination of this channelModel
|
||||||
/// [other] channelModel.
|
/// and the given [other] channelModel.
|
||||||
ChannelModel merge(ChannelModel other) {
|
ChannelModel merge(ChannelModel other) {
|
||||||
if (other == null) return this;
|
if (other == null) return this;
|
||||||
return copyWith(
|
return copyWith(
|
||||||
|
|||||||
@@ -1,16 +1,26 @@
|
|||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
import 'package:stream_chat/src/models/channel_model.dart';
|
||||||
import '../models/read.dart';
|
import 'package:stream_chat/src/models/member.dart';
|
||||||
import '../models/user.dart';
|
import 'package:stream_chat/src/models/message.dart';
|
||||||
import 'channel_model.dart';
|
import 'package:stream_chat/src/models/read.dart';
|
||||||
import 'member.dart';
|
import 'package:stream_chat/src/models/user.dart';
|
||||||
import 'message.dart';
|
|
||||||
|
|
||||||
part 'channel_state.g.dart';
|
part 'channel_state.g.dart';
|
||||||
|
|
||||||
/// The class that contains the information about a channel
|
/// The class that contains the information about a channel
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class ChannelState {
|
class ChannelState {
|
||||||
|
/// Constructor used for json serialization
|
||||||
|
ChannelState({
|
||||||
|
this.channel,
|
||||||
|
this.messages = const [],
|
||||||
|
this.members = const [],
|
||||||
|
this.pinnedMessages = const [],
|
||||||
|
this.watcherCount,
|
||||||
|
this.watchers = const [],
|
||||||
|
this.read = const [],
|
||||||
|
});
|
||||||
|
|
||||||
/// The channel to which this state belongs
|
/// The channel to which this state belongs
|
||||||
final ChannelModel channel;
|
final ChannelModel channel;
|
||||||
|
|
||||||
@@ -32,17 +42,6 @@ class ChannelState {
|
|||||||
/// The list of channel reads
|
/// The list of channel reads
|
||||||
final List<Read> read;
|
final List<Read> read;
|
||||||
|
|
||||||
/// Constructor used for json serialization
|
|
||||||
ChannelState({
|
|
||||||
this.channel,
|
|
||||||
this.messages = const [],
|
|
||||||
this.members = const [],
|
|
||||||
this.pinnedMessages = const [],
|
|
||||||
this.watcherCount,
|
|
||||||
this.watchers = const [],
|
|
||||||
this.read = const [],
|
|
||||||
});
|
|
||||||
|
|
||||||
/// Create a new instance from a json
|
/// Create a new instance from a json
|
||||||
static ChannelState fromJson(Map<String, dynamic> json) =>
|
static ChannelState fromJson(Map<String, dynamic> json) =>
|
||||||
_$ChannelStateFromJson(json);
|
_$ChannelStateFromJson(json);
|
||||||
|
|||||||
@@ -5,15 +5,6 @@ part 'command.g.dart';
|
|||||||
/// The class that contains the information about a command
|
/// The class that contains the information about a command
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class Command {
|
class Command {
|
||||||
/// The name of the command
|
|
||||||
final String name;
|
|
||||||
|
|
||||||
/// The description explaining the command
|
|
||||||
final String description;
|
|
||||||
|
|
||||||
/// The arguments of the command
|
|
||||||
final String args;
|
|
||||||
|
|
||||||
/// Constructor used for json serialization
|
/// Constructor used for json serialization
|
||||||
Command({
|
Command({
|
||||||
this.name,
|
this.name,
|
||||||
@@ -25,6 +16,15 @@ class Command {
|
|||||||
factory Command.fromJson(Map<String, dynamic> json) =>
|
factory Command.fromJson(Map<String, dynamic> json) =>
|
||||||
_$CommandFromJson(json);
|
_$CommandFromJson(json);
|
||||||
|
|
||||||
|
/// The name of the command
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
/// The description explaining the command
|
||||||
|
final String description;
|
||||||
|
|
||||||
|
/// The arguments of the command
|
||||||
|
final String args;
|
||||||
|
|
||||||
/// Serialize to json
|
/// Serialize to json
|
||||||
Map<String, dynamic> toJson() => _$CommandToJson(this);
|
Map<String, dynamic> toJson() => _$CommandToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,6 @@ part 'device.g.dart';
|
|||||||
/// The class that contains the information about a device
|
/// The class that contains the information about a device
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class Device {
|
class Device {
|
||||||
/// The id of the device
|
|
||||||
final String id;
|
|
||||||
|
|
||||||
/// The notification push provider
|
|
||||||
final String pushProvider;
|
|
||||||
|
|
||||||
/// Constructor used for json serialization
|
/// Constructor used for json serialization
|
||||||
Device({
|
Device({
|
||||||
this.id,
|
this.id,
|
||||||
@@ -20,6 +14,12 @@ class Device {
|
|||||||
/// Create a new instance from a json
|
/// Create a new instance from a json
|
||||||
factory Device.fromJson(Map<String, dynamic> json) => _$DeviceFromJson(json);
|
factory Device.fromJson(Map<String, dynamic> json) => _$DeviceFromJson(json);
|
||||||
|
|
||||||
|
/// The id of the device
|
||||||
|
final String id;
|
||||||
|
|
||||||
|
/// The notification push provider
|
||||||
|
final String pushProvider;
|
||||||
|
|
||||||
/// Serialize to json
|
/// Serialize to json
|
||||||
Map<String, dynamic> toJson() => _$DeviceToJson(this);
|
Map<String, dynamic> toJson() => _$DeviceToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,17 +4,40 @@ import 'package:stream_chat/src/models/message.dart';
|
|||||||
import 'package:stream_chat/src/models/serialization.dart';
|
import 'package:stream_chat/src/models/serialization.dart';
|
||||||
import 'package:stream_chat/stream_chat.dart';
|
import 'package:stream_chat/stream_chat.dart';
|
||||||
|
|
||||||
import '../event_type.dart';
|
|
||||||
import 'member.dart';
|
|
||||||
import 'own_user.dart';
|
|
||||||
import 'reaction.dart';
|
|
||||||
import 'user.dart';
|
|
||||||
|
|
||||||
part 'event.g.dart';
|
part 'event.g.dart';
|
||||||
|
|
||||||
/// The class that contains the information about an event
|
/// The class that contains the information about an event
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class Event {
|
class Event {
|
||||||
|
/// Constructor used for json serialization
|
||||||
|
Event({
|
||||||
|
this.type,
|
||||||
|
this.cid,
|
||||||
|
this.connectionId,
|
||||||
|
this.createdAt,
|
||||||
|
this.me,
|
||||||
|
this.user,
|
||||||
|
this.message,
|
||||||
|
this.totalUnreadCount,
|
||||||
|
this.unreadChannels,
|
||||||
|
this.reaction,
|
||||||
|
this.online,
|
||||||
|
this.channel,
|
||||||
|
this.member,
|
||||||
|
this.channelId,
|
||||||
|
this.channelType,
|
||||||
|
this.parentId,
|
||||||
|
this.extraData,
|
||||||
|
}) : isLocal = true;
|
||||||
|
|
||||||
|
/// Create a new instance from a json
|
||||||
|
factory Event.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$EventFromJson(Serialization.moveToExtraDataFromRoot(
|
||||||
|
json,
|
||||||
|
topLevelFields,
|
||||||
|
))
|
||||||
|
..isLocal = false;
|
||||||
|
|
||||||
/// The type of the event
|
/// The type of the event
|
||||||
/// [EventType] contains some predefined constant types
|
/// [EventType] contains some predefined constant types
|
||||||
final String type;
|
final String type;
|
||||||
@@ -71,27 +94,6 @@ class Event {
|
|||||||
@JsonKey(includeIfNull: false)
|
@JsonKey(includeIfNull: false)
|
||||||
final Map<String, dynamic> extraData;
|
final Map<String, dynamic> extraData;
|
||||||
|
|
||||||
/// Constructor used for json serialization
|
|
||||||
Event({
|
|
||||||
this.type,
|
|
||||||
this.cid,
|
|
||||||
this.connectionId,
|
|
||||||
this.createdAt,
|
|
||||||
this.me,
|
|
||||||
this.user,
|
|
||||||
this.message,
|
|
||||||
this.totalUnreadCount,
|
|
||||||
this.unreadChannels,
|
|
||||||
this.reaction,
|
|
||||||
this.online,
|
|
||||||
this.channel,
|
|
||||||
this.member,
|
|
||||||
this.channelId,
|
|
||||||
this.channelType,
|
|
||||||
this.parentId,
|
|
||||||
this.extraData,
|
|
||||||
}) : isLocal = true;
|
|
||||||
|
|
||||||
/// Known top level fields.
|
/// Known top level fields.
|
||||||
/// Useful for [Serialization] methods.
|
/// Useful for [Serialization] methods.
|
||||||
static final topLevelFields = [
|
static final topLevelFields = [
|
||||||
@@ -114,15 +116,6 @@ class Event {
|
|||||||
'is_local',
|
'is_local',
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Create a new instance from a json
|
|
||||||
factory Event.fromJson(Map<String, dynamic> json) {
|
|
||||||
return _$EventFromJson(Serialization.moveToExtraDataFromRoot(
|
|
||||||
json,
|
|
||||||
topLevelFields,
|
|
||||||
))
|
|
||||||
..isLocal = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Serialize to json
|
/// Serialize to json
|
||||||
Map<String, dynamic> toJson() => Serialization.moveFromExtraDataToRoot(
|
Map<String, dynamic> toJson() => Serialization.moveFromExtraDataToRoot(
|
||||||
_$EventToJson(this),
|
_$EventToJson(this),
|
||||||
@@ -133,16 +126,6 @@ class Event {
|
|||||||
/// The channel embedded in the event object
|
/// The channel embedded in the event object
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class EventChannel extends ChannelModel {
|
class EventChannel extends ChannelModel {
|
||||||
/// A paginated list of channel members
|
|
||||||
final List<Member> members;
|
|
||||||
|
|
||||||
/// Known top level fields.
|
|
||||||
/// Useful for [Serialization] methods.
|
|
||||||
static final topLevelFields = [
|
|
||||||
'members',
|
|
||||||
...ChannelModel.topLevelFields,
|
|
||||||
];
|
|
||||||
|
|
||||||
/// Constructor used for json serialization
|
/// Constructor used for json serialization
|
||||||
EventChannel({
|
EventChannel({
|
||||||
this.members,
|
this.members,
|
||||||
@@ -174,12 +157,21 @@ class EventChannel extends ChannelModel {
|
|||||||
);
|
);
|
||||||
|
|
||||||
/// Create a new instance from a json
|
/// Create a new instance from a json
|
||||||
factory EventChannel.fromJson(Map<String, dynamic> json) {
|
factory EventChannel.fromJson(Map<String, dynamic> json) =>
|
||||||
return _$EventChannelFromJson(Serialization.moveToExtraDataFromRoot(
|
_$EventChannelFromJson(Serialization.moveToExtraDataFromRoot(
|
||||||
json,
|
json,
|
||||||
topLevelFields,
|
topLevelFields,
|
||||||
));
|
));
|
||||||
}
|
|
||||||
|
/// A paginated list of channel members
|
||||||
|
final List<Member> members;
|
||||||
|
|
||||||
|
/// Known top level fields.
|
||||||
|
/// Useful for [Serialization] methods.
|
||||||
|
static final topLevelFields = [
|
||||||
|
'members',
|
||||||
|
...ChannelModel.topLevelFields,
|
||||||
|
];
|
||||||
|
|
||||||
/// Serialize to json
|
/// Serialize to json
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -1,12 +1,35 @@
|
|||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
import 'package:stream_chat/src/models/user.dart';
|
||||||
import '../models/user.dart';
|
|
||||||
|
|
||||||
part 'member.g.dart';
|
part 'member.g.dart';
|
||||||
|
|
||||||
/// The class that contains the information about the user membership in a channel
|
/// The class that contains the information about the user membership
|
||||||
|
/// in a channel
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class Member {
|
class Member {
|
||||||
|
/// Constructor used for json serialization
|
||||||
|
Member({
|
||||||
|
this.user,
|
||||||
|
this.inviteAcceptedAt,
|
||||||
|
this.inviteRejectedAt,
|
||||||
|
this.invited,
|
||||||
|
this.role,
|
||||||
|
this.userId,
|
||||||
|
this.isModerator,
|
||||||
|
this.createdAt,
|
||||||
|
this.updatedAt,
|
||||||
|
this.banned,
|
||||||
|
this.shadowBanned,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Create a new instance from a json
|
||||||
|
factory Member.fromJson(Map<String, dynamic> json) {
|
||||||
|
final member = _$MemberFromJson(json);
|
||||||
|
return member.copyWith(
|
||||||
|
userId: member.user?.id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// The interested user
|
/// The interested user
|
||||||
final User user;
|
final User user;
|
||||||
|
|
||||||
@@ -40,29 +63,6 @@ class Member {
|
|||||||
/// The last date of update
|
/// The last date of update
|
||||||
final DateTime updatedAt;
|
final DateTime updatedAt;
|
||||||
|
|
||||||
/// Constructor used for json serialization
|
|
||||||
Member({
|
|
||||||
this.user,
|
|
||||||
this.inviteAcceptedAt,
|
|
||||||
this.inviteRejectedAt,
|
|
||||||
this.invited,
|
|
||||||
this.role,
|
|
||||||
this.userId,
|
|
||||||
this.isModerator,
|
|
||||||
this.createdAt,
|
|
||||||
this.updatedAt,
|
|
||||||
this.banned,
|
|
||||||
this.shadowBanned,
|
|
||||||
});
|
|
||||||
|
|
||||||
/// Create a new instance from a json
|
|
||||||
factory Member.fromJson(Map<String, dynamic> json) {
|
|
||||||
final member = _$MemberFromJson(json);
|
|
||||||
return member.copyWith(
|
|
||||||
userId: member.user?.id,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a copy of [Member] with specified attributes overridden.
|
/// Creates a copy of [Member] with specified attributes overridden.
|
||||||
Member copyWith({
|
Member copyWith({
|
||||||
User user,
|
User user,
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
import 'package:stream_chat/src/models/attachment.dart';
|
||||||
|
import 'package:stream_chat/src/models/reaction.dart';
|
||||||
|
import 'package:stream_chat/src/models/serialization.dart';
|
||||||
|
import 'package:stream_chat/src/models/user.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
import 'attachment.dart';
|
|
||||||
import 'reaction.dart';
|
|
||||||
import 'serialization.dart';
|
|
||||||
import 'user.dart';
|
|
||||||
|
|
||||||
part 'message.g.dart';
|
part 'message.g.dart';
|
||||||
|
|
||||||
class _PinExpires {
|
class _PinExpires {
|
||||||
@@ -29,9 +28,11 @@ enum MessageSendingStatus {
|
|||||||
failed,
|
failed,
|
||||||
|
|
||||||
/// Message failed to updated
|
/// Message failed to updated
|
||||||
|
// ignore: constant_identifier_names
|
||||||
failed_update,
|
failed_update,
|
||||||
|
|
||||||
/// Message failed to delete
|
/// Message failed to delete
|
||||||
|
// ignore: constant_identifier_names
|
||||||
failed_delete,
|
failed_delete,
|
||||||
|
|
||||||
/// Message correctly sent
|
/// Message correctly sent
|
||||||
@@ -41,7 +42,45 @@ enum MessageSendingStatus {
|
|||||||
/// The class that contains the information about a message
|
/// The class that contains the information about a message
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class Message {
|
class Message {
|
||||||
/// The message ID. This is either created by Stream or set client side when the message is added.
|
/// Constructor used for json serialization
|
||||||
|
Message({
|
||||||
|
String id,
|
||||||
|
this.text,
|
||||||
|
this.type,
|
||||||
|
this.attachments,
|
||||||
|
this.mentionedUsers,
|
||||||
|
this.silent,
|
||||||
|
this.shadowed,
|
||||||
|
this.reactionCounts,
|
||||||
|
this.reactionScores,
|
||||||
|
this.latestReactions,
|
||||||
|
this.ownReactions,
|
||||||
|
this.parentId,
|
||||||
|
this.quotedMessage,
|
||||||
|
this.quotedMessageId,
|
||||||
|
this.replyCount = 0,
|
||||||
|
this.threadParticipants,
|
||||||
|
this.showInChannel,
|
||||||
|
this.command,
|
||||||
|
this.createdAt,
|
||||||
|
this.updatedAt,
|
||||||
|
this.user,
|
||||||
|
this.pinned = false,
|
||||||
|
this.pinnedAt,
|
||||||
|
DateTime pinExpires,
|
||||||
|
this.pinnedBy,
|
||||||
|
this.extraData,
|
||||||
|
this.deletedAt,
|
||||||
|
this.status = MessageSendingStatus.sent,
|
||||||
|
}) : id = id ?? Uuid().v4(),
|
||||||
|
pinExpires = pinExpires?.toUtc();
|
||||||
|
|
||||||
|
/// Create a new instance from a json
|
||||||
|
factory Message.fromJson(Map<String, dynamic> json) => _$MessageFromJson(
|
||||||
|
Serialization.moveToExtraDataFromRoot(json, topLevelFields));
|
||||||
|
|
||||||
|
/// The message ID. This is either created by Stream or set client side when
|
||||||
|
/// the message is added.
|
||||||
final String id;
|
final String id;
|
||||||
|
|
||||||
/// The text of this message
|
/// The text of this message
|
||||||
@@ -55,7 +94,8 @@ class Message {
|
|||||||
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
|
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
|
||||||
final String type;
|
final String type;
|
||||||
|
|
||||||
/// The list of attachments, either provided by the user or generated from a command or as a result of URL scraping.
|
/// The list of attachments, either provided by the user or generated from a
|
||||||
|
/// command or as a result of URL scraping.
|
||||||
@JsonKey(includeIfNull: false)
|
@JsonKey(includeIfNull: false)
|
||||||
final List<Attachment> attachments;
|
final List<Attachment> attachments;
|
||||||
|
|
||||||
@@ -188,43 +228,6 @@ class Message {
|
|||||||
'pinned_by',
|
'pinned_by',
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Constructor used for json serialization
|
|
||||||
Message({
|
|
||||||
String id,
|
|
||||||
this.text,
|
|
||||||
this.type,
|
|
||||||
this.attachments,
|
|
||||||
this.mentionedUsers,
|
|
||||||
this.silent,
|
|
||||||
this.shadowed,
|
|
||||||
this.reactionCounts,
|
|
||||||
this.reactionScores,
|
|
||||||
this.latestReactions,
|
|
||||||
this.ownReactions,
|
|
||||||
this.parentId,
|
|
||||||
this.quotedMessage,
|
|
||||||
this.quotedMessageId,
|
|
||||||
this.replyCount = 0,
|
|
||||||
this.threadParticipants,
|
|
||||||
this.showInChannel,
|
|
||||||
this.command,
|
|
||||||
this.createdAt,
|
|
||||||
this.updatedAt,
|
|
||||||
this.user,
|
|
||||||
this.pinned = false,
|
|
||||||
this.pinnedAt,
|
|
||||||
DateTime pinExpires,
|
|
||||||
this.pinnedBy,
|
|
||||||
this.extraData,
|
|
||||||
this.deletedAt,
|
|
||||||
this.status = MessageSendingStatus.sent,
|
|
||||||
}) : id = id ?? Uuid().v4(),
|
|
||||||
pinExpires = pinExpires?.toUtc();
|
|
||||||
|
|
||||||
/// Create a new instance from a json
|
|
||||||
factory Message.fromJson(Map<String, dynamic> json) => _$MessageFromJson(
|
|
||||||
Serialization.moveToExtraDataFromRoot(json, topLevelFields));
|
|
||||||
|
|
||||||
/// Serialize to json
|
/// Serialize to json
|
||||||
Map<String, dynamic> toJson() => Serialization.moveFromExtraDataToRoot(
|
Map<String, dynamic> toJson() => Serialization.moveFromExtraDataToRoot(
|
||||||
_$MessageToJson(this), topLevelFields);
|
_$MessageToJson(this), topLevelFields);
|
||||||
@@ -267,7 +270,7 @@ class Message {
|
|||||||
throw ArgumentError('`pinExpires` can only be set as DateTime or null');
|
throw ArgumentError('`pinExpires` can only be set as DateTime or null');
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}());
|
}(), 'Validate type for pinExpires');
|
||||||
return Message(
|
return Message(
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
text: text ?? this.text,
|
text: text ?? this.text,
|
||||||
@@ -300,8 +303,8 @@ class Message {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a new [Message] that is a combination of this message and the given
|
/// Returns a new [Message] that is a combination of this message and the
|
||||||
/// [other] message.
|
/// given [other] message.
|
||||||
Message merge(Message other) {
|
Message merge(Message other) {
|
||||||
if (other == null) return this;
|
if (other == null) return this;
|
||||||
return copyWith(
|
return copyWith(
|
||||||
@@ -344,6 +347,12 @@ class TranslatedMessage extends Message {
|
|||||||
/// Constructor used for json serialization
|
/// Constructor used for json serialization
|
||||||
TranslatedMessage(this.i18n);
|
TranslatedMessage(this.i18n);
|
||||||
|
|
||||||
|
/// Create a new instance from a json
|
||||||
|
factory TranslatedMessage.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$TranslatedMessageFromJson(
|
||||||
|
Serialization.moveToExtraDataFromRoot(json, topLevelFields),
|
||||||
|
);
|
||||||
|
|
||||||
/// A Map of
|
/// A Map of
|
||||||
final Map<String, String> i18n;
|
final Map<String, String> i18n;
|
||||||
|
|
||||||
@@ -354,13 +363,6 @@ class TranslatedMessage extends Message {
|
|||||||
...Message.topLevelFields,
|
...Message.topLevelFields,
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Create a new instance from a json
|
|
||||||
factory TranslatedMessage.fromJson(Map<String, dynamic> json) {
|
|
||||||
return _$TranslatedMessageFromJson(
|
|
||||||
Serialization.moveToExtraDataFromRoot(json, topLevelFields),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Serialize to json
|
/// Serialize to json
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> toJson() => Serialization.moveFromExtraDataToRoot(
|
Map<String, dynamic> toJson() => Serialization.moveFromExtraDataToRoot(
|
||||||
|
|||||||
@@ -1,14 +1,19 @@
|
|||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
import 'package:stream_chat/src/models/channel_model.dart';
|
import 'package:stream_chat/src/models/channel_model.dart';
|
||||||
|
import 'package:stream_chat/src/models/serialization.dart';
|
||||||
import 'serialization.dart';
|
import 'package:stream_chat/src/models/user.dart';
|
||||||
import 'user.dart';
|
|
||||||
|
|
||||||
part 'mute.g.dart';
|
part 'mute.g.dart';
|
||||||
|
|
||||||
/// The class that contains the information about a muted user
|
/// The class that contains the information about a muted user
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class Mute {
|
class Mute {
|
||||||
|
/// Constructor used for json serialization
|
||||||
|
Mute({this.user, this.channel, this.createdAt, this.updatedAt});
|
||||||
|
|
||||||
|
/// Create a new instance from a json
|
||||||
|
factory Mute.fromJson(Map<String, dynamic> json) => _$MuteFromJson(json);
|
||||||
|
|
||||||
/// The user that performed the muting action
|
/// The user that performed the muting action
|
||||||
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
|
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
|
||||||
final User user;
|
final User user;
|
||||||
@@ -25,12 +30,6 @@ class Mute {
|
|||||||
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
|
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
|
||||||
final DateTime updatedAt;
|
final DateTime updatedAt;
|
||||||
|
|
||||||
/// Constructor used for json serialization
|
|
||||||
Mute({this.user, this.channel, this.createdAt, this.updatedAt});
|
|
||||||
|
|
||||||
/// Create a new instance from a json
|
|
||||||
factory Mute.fromJson(Map<String, dynamic> json) => _$MuteFromJson(json);
|
|
||||||
|
|
||||||
/// Serialize to json
|
/// Serialize to json
|
||||||
Map<String, dynamic> toJson() => _$MuteToJson(this);
|
Map<String, dynamic> toJson() => _$MuteToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
import 'package:stream_chat/src/models/device.dart';
|
||||||
import 'device.dart';
|
import 'package:stream_chat/src/models/mute.dart';
|
||||||
import 'mute.dart';
|
import 'package:stream_chat/src/models/serialization.dart';
|
||||||
import 'serialization.dart';
|
import 'package:stream_chat/src/models/user.dart';
|
||||||
import 'user.dart';
|
|
||||||
|
|
||||||
part 'own_user.g.dart';
|
part 'own_user.g.dart';
|
||||||
|
|
||||||
@@ -11,6 +10,36 @@ part 'own_user.g.dart';
|
|||||||
/// This object can be found in [Event]
|
/// This object can be found in [Event]
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class OwnUser extends User {
|
class OwnUser extends User {
|
||||||
|
/// Constructor used for json serialization
|
||||||
|
OwnUser({
|
||||||
|
this.devices,
|
||||||
|
this.mutes,
|
||||||
|
this.totalUnreadCount,
|
||||||
|
this.unreadChannels,
|
||||||
|
this.channelMutes,
|
||||||
|
String id,
|
||||||
|
String role,
|
||||||
|
DateTime createdAt,
|
||||||
|
DateTime updatedAt,
|
||||||
|
DateTime lastActive,
|
||||||
|
bool online,
|
||||||
|
Map<String, dynamic> extraData,
|
||||||
|
bool banned,
|
||||||
|
}) : super(
|
||||||
|
id: id,
|
||||||
|
role: role,
|
||||||
|
createdAt: createdAt,
|
||||||
|
updatedAt: updatedAt,
|
||||||
|
lastActive: lastActive,
|
||||||
|
online: online,
|
||||||
|
extraData: extraData,
|
||||||
|
banned: banned,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Create a new instance from a json
|
||||||
|
factory OwnUser.fromJson(Map<String, dynamic> json) => _$OwnUserFromJson(
|
||||||
|
Serialization.moveToExtraDataFromRoot(json, topLevelFields));
|
||||||
|
|
||||||
/// List of user devices
|
/// List of user devices
|
||||||
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
|
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
|
||||||
final List<Device> devices;
|
final List<Device> devices;
|
||||||
@@ -42,42 +71,8 @@ class OwnUser extends User {
|
|||||||
...User.topLevelFields,
|
...User.topLevelFields,
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Constructor used for json serialization
|
|
||||||
OwnUser({
|
|
||||||
this.devices,
|
|
||||||
this.mutes,
|
|
||||||
this.totalUnreadCount,
|
|
||||||
this.unreadChannels,
|
|
||||||
this.channelMutes,
|
|
||||||
String id,
|
|
||||||
String role,
|
|
||||||
DateTime createdAt,
|
|
||||||
DateTime updatedAt,
|
|
||||||
DateTime lastActive,
|
|
||||||
bool online,
|
|
||||||
Map<String, dynamic> extraData,
|
|
||||||
bool banned,
|
|
||||||
}) : super(
|
|
||||||
id: id,
|
|
||||||
role: role,
|
|
||||||
createdAt: createdAt,
|
|
||||||
updatedAt: updatedAt,
|
|
||||||
lastActive: lastActive,
|
|
||||||
online: online,
|
|
||||||
extraData: extraData,
|
|
||||||
banned: banned,
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Create a new instance from a json
|
|
||||||
factory OwnUser.fromJson(Map<String, dynamic> json) {
|
|
||||||
return _$OwnUserFromJson(
|
|
||||||
Serialization.moveToExtraDataFromRoot(json, topLevelFields));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Serialize to json
|
/// Serialize to json
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() => Serialization.moveFromExtraDataToRoot(
|
||||||
return Serialization.moveFromExtraDataToRoot(
|
_$OwnUserToJson(this), topLevelFields);
|
||||||
_$OwnUserToJson(this), topLevelFields);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,27 @@
|
|||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
import 'package:stream_chat/src/models/serialization.dart';
|
||||||
import 'serialization.dart';
|
import 'package:stream_chat/src/models/user.dart';
|
||||||
import 'user.dart';
|
|
||||||
|
|
||||||
part 'reaction.g.dart';
|
part 'reaction.g.dart';
|
||||||
|
|
||||||
/// The class that defines a reaction
|
/// The class that defines a reaction
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class Reaction {
|
class Reaction {
|
||||||
|
/// Constructor used for json serialization
|
||||||
|
Reaction({
|
||||||
|
this.messageId,
|
||||||
|
this.createdAt,
|
||||||
|
this.type,
|
||||||
|
this.user,
|
||||||
|
String userId,
|
||||||
|
this.score,
|
||||||
|
this.extraData,
|
||||||
|
}) : userId = userId ?? user?.id;
|
||||||
|
|
||||||
|
/// Create a new instance from a json
|
||||||
|
factory Reaction.fromJson(Map<String, dynamic> json) => _$ReactionFromJson(
|
||||||
|
Serialization.moveToExtraDataFromRoot(json, topLevelFields));
|
||||||
|
|
||||||
/// The messageId to which the reaction belongs
|
/// The messageId to which the reaction belongs
|
||||||
final String messageId;
|
final String messageId;
|
||||||
|
|
||||||
@@ -43,28 +57,9 @@ class Reaction {
|
|||||||
'score',
|
'score',
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Constructor used for json serialization
|
|
||||||
Reaction({
|
|
||||||
this.messageId,
|
|
||||||
this.createdAt,
|
|
||||||
this.type,
|
|
||||||
this.user,
|
|
||||||
String userId,
|
|
||||||
this.score,
|
|
||||||
this.extraData,
|
|
||||||
}) : userId = userId ?? user?.id;
|
|
||||||
|
|
||||||
/// Create a new instance from a json
|
|
||||||
factory Reaction.fromJson(Map<String, dynamic> json) {
|
|
||||||
return _$ReactionFromJson(
|
|
||||||
Serialization.moveToExtraDataFromRoot(json, topLevelFields));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Serialize to json
|
/// Serialize to json
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() => Serialization.moveFromExtraDataToRoot(
|
||||||
return Serialization.moveFromExtraDataToRoot(
|
_$ReactionToJson(this), topLevelFields);
|
||||||
_$ReactionToJson(this), topLevelFields);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a copy of [Reaction] with specified attributes overridden.
|
/// Creates a copy of [Reaction] with specified attributes overridden.
|
||||||
Reaction copyWith({
|
Reaction copyWith({
|
||||||
@@ -75,20 +70,19 @@ class Reaction {
|
|||||||
String userId,
|
String userId,
|
||||||
int score,
|
int score,
|
||||||
Map<String, dynamic> extraData,
|
Map<String, dynamic> extraData,
|
||||||
}) {
|
}) =>
|
||||||
return Reaction(
|
Reaction(
|
||||||
messageId: messageId ?? this.messageId,
|
messageId: messageId ?? this.messageId,
|
||||||
createdAt: createdAt ?? this.createdAt,
|
createdAt: createdAt ?? this.createdAt,
|
||||||
type: type ?? this.type,
|
type: type ?? this.type,
|
||||||
user: user ?? this.user,
|
user: user ?? this.user,
|
||||||
userId: userId ?? this.userId,
|
userId: userId ?? this.userId,
|
||||||
score: score ?? this.score,
|
score: score ?? this.score,
|
||||||
extraData: extraData ?? this.extraData,
|
extraData: extraData ?? this.extraData,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a new [Reaction] that is a combination of this reaction and the given
|
/// Returns a new [Reaction] that is a combination of this reaction and the
|
||||||
/// [other] reaction.
|
/// given [other] reaction.
|
||||||
Reaction merge(Reaction other) {
|
Reaction merge(Reaction other) {
|
||||||
if (other == null) return this;
|
if (other == null) return this;
|
||||||
return copyWith(
|
return copyWith(
|
||||||
|
|||||||
@@ -1,21 +1,11 @@
|
|||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
import 'package:stream_chat/src/models/user.dart';
|
||||||
import 'user.dart';
|
|
||||||
|
|
||||||
part 'read.g.dart';
|
part 'read.g.dart';
|
||||||
|
|
||||||
/// The class that defines a read event
|
/// The class that defines a read event
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class Read {
|
class Read {
|
||||||
/// Date of the read event
|
|
||||||
final DateTime lastRead;
|
|
||||||
|
|
||||||
/// User who sent the event
|
|
||||||
final User user;
|
|
||||||
|
|
||||||
/// Number of unread messages
|
|
||||||
final int unreadMessages;
|
|
||||||
|
|
||||||
/// Constructor used for json serialization
|
/// Constructor used for json serialization
|
||||||
Read({
|
Read({
|
||||||
this.lastRead,
|
this.lastRead,
|
||||||
@@ -26,6 +16,15 @@ class Read {
|
|||||||
/// Create a new instance from a json
|
/// Create a new instance from a json
|
||||||
factory Read.fromJson(Map<String, dynamic> json) => _$ReadFromJson(json);
|
factory Read.fromJson(Map<String, dynamic> json) => _$ReadFromJson(json);
|
||||||
|
|
||||||
|
/// Date of the read event
|
||||||
|
final DateTime lastRead;
|
||||||
|
|
||||||
|
/// User who sent the event
|
||||||
|
final User user;
|
||||||
|
|
||||||
|
/// Number of unread messages
|
||||||
|
final int unreadMessages;
|
||||||
|
|
||||||
/// Serialize to json
|
/// Serialize to json
|
||||||
Map<String, dynamic> toJson() => _$ReadToJson(this);
|
Map<String, dynamic> toJson() => _$ReadToJson(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'user.dart';
|
import 'package:stream_chat/src/models/user.dart';
|
||||||
|
|
||||||
/// Used to avoid to serialize properties to json
|
/// Used to avoid to serialize properties to json
|
||||||
|
// ignore: prefer_void_to_null
|
||||||
Null readonly(_) => null;
|
Null readonly(_) => null;
|
||||||
|
|
||||||
/// Helper class for serialization to and from json
|
/// Helper class for serialization to and from json
|
||||||
@@ -9,9 +10,8 @@ class Serialization {
|
|||||||
static const Function readOnly = readonly;
|
static const Function readOnly = readonly;
|
||||||
|
|
||||||
/// List of users to list of userIds
|
/// List of users to list of userIds
|
||||||
static List<String> userIds(List<User> users) {
|
static List<String> userIds(List<User> users) =>
|
||||||
return users?.map((u) => u.id)?.toList();
|
users?.map((u) => u.id)?.toList();
|
||||||
}
|
|
||||||
|
|
||||||
/// Takes unknown json keys and puts them in the `extra_data` key
|
/// Takes unknown json keys and puts them in the `extra_data` key
|
||||||
static Map<String, dynamic> moveToExtraDataFromRoot(
|
static Map<String, dynamic> moveToExtraDataFromRoot(
|
||||||
@@ -34,7 +34,8 @@ class Serialization {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Takes values in `extra_data` key and puts them on the root level of the json map
|
/// Takes values in `extra_data` key and puts them on the root level of
|
||||||
|
/// the json map
|
||||||
static Map<String, dynamic> moveFromExtraDataToRoot(
|
static Map<String, dynamic> moveFromExtraDataToRoot(
|
||||||
Map<String, dynamic> json,
|
Map<String, dynamic> json,
|
||||||
List<String> topLevelFields,
|
List<String> topLevelFields,
|
||||||
|
|||||||
@@ -1,12 +1,53 @@
|
|||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
import 'package:stream_chat/src/models/serialization.dart';
|
||||||
import 'serialization.dart';
|
|
||||||
|
|
||||||
part 'user.g.dart';
|
part 'user.g.dart';
|
||||||
|
|
||||||
/// The class that defines the user model
|
/// The class that defines the user model
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class User {
|
class User {
|
||||||
|
/// Constructor used for json serialization
|
||||||
|
User({
|
||||||
|
this.id,
|
||||||
|
this.role,
|
||||||
|
this.createdAt,
|
||||||
|
this.updatedAt,
|
||||||
|
this.lastActive,
|
||||||
|
this.online,
|
||||||
|
this.extraData,
|
||||||
|
this.banned,
|
||||||
|
this.teams,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Create a new instance from a json
|
||||||
|
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(
|
||||||
|
Serialization.moveToExtraDataFromRoot(json, topLevelFields));
|
||||||
|
|
||||||
|
/// Use this named constructor to create a new user instance
|
||||||
|
User.init(
|
||||||
|
this.id, {
|
||||||
|
this.online,
|
||||||
|
this.extraData,
|
||||||
|
}) : createdAt = null,
|
||||||
|
updatedAt = null,
|
||||||
|
lastActive = null,
|
||||||
|
banned = null,
|
||||||
|
teams = null,
|
||||||
|
role = null;
|
||||||
|
|
||||||
|
/// Known top level fields.
|
||||||
|
/// Useful for [Serialization] methods.
|
||||||
|
static const topLevelFields = [
|
||||||
|
'id',
|
||||||
|
'role',
|
||||||
|
'created_at',
|
||||||
|
'updated_at',
|
||||||
|
'last_active',
|
||||||
|
'online',
|
||||||
|
'banned',
|
||||||
|
'teams',
|
||||||
|
];
|
||||||
|
|
||||||
/// User id
|
/// User id
|
||||||
final String id;
|
final String id;
|
||||||
|
|
||||||
@@ -42,43 +83,8 @@ class User {
|
|||||||
@JsonKey(includeIfNull: false)
|
@JsonKey(includeIfNull: false)
|
||||||
final Map<String, dynamic> extraData;
|
final Map<String, dynamic> extraData;
|
||||||
|
|
||||||
/// Known top level fields.
|
@override
|
||||||
/// Useful for [Serialization] methods.
|
int get hashCode => id.hashCode;
|
||||||
static const topLevelFields = [
|
|
||||||
'id',
|
|
||||||
'role',
|
|
||||||
'created_at',
|
|
||||||
'updated_at',
|
|
||||||
'last_active',
|
|
||||||
'online',
|
|
||||||
'banned',
|
|
||||||
'teams',
|
|
||||||
];
|
|
||||||
|
|
||||||
/// Use this named constructor to create a new user instance
|
|
||||||
User.init(
|
|
||||||
this.id, {
|
|
||||||
this.online,
|
|
||||||
this.extraData,
|
|
||||||
}) : createdAt = null,
|
|
||||||
updatedAt = null,
|
|
||||||
lastActive = null,
|
|
||||||
banned = null,
|
|
||||||
teams = null,
|
|
||||||
role = null;
|
|
||||||
|
|
||||||
/// Constructor used for json serialization
|
|
||||||
User({
|
|
||||||
this.id,
|
|
||||||
this.role,
|
|
||||||
this.createdAt,
|
|
||||||
this.updatedAt,
|
|
||||||
this.lastActive,
|
|
||||||
this.online,
|
|
||||||
this.extraData,
|
|
||||||
this.banned,
|
|
||||||
this.teams,
|
|
||||||
});
|
|
||||||
|
|
||||||
/// Shortcut for user name
|
/// Shortcut for user name
|
||||||
String get name =>
|
String get name =>
|
||||||
@@ -86,23 +92,12 @@ class User {
|
|||||||
? extraData['name']
|
? extraData['name']
|
||||||
: id;
|
: id;
|
||||||
|
|
||||||
/// Create a new instance from a json
|
|
||||||
factory User.fromJson(Map<String, dynamic> json) {
|
|
||||||
return _$UserFromJson(
|
|
||||||
Serialization.moveToExtraDataFromRoot(json, topLevelFields));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Serialize to json
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return Serialization.moveFromExtraDataToRoot(
|
|
||||||
_$UserToJson(this), topLevelFields);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) =>
|
bool operator ==(Object other) =>
|
||||||
identical(this, other) ||
|
identical(this, other) ||
|
||||||
other is User && runtimeType == other.runtimeType && id == other.id;
|
other is User && runtimeType == other.runtimeType && id == other.id;
|
||||||
|
|
||||||
@override
|
/// Serialize to json
|
||||||
int get hashCode => id.hashCode;
|
Map<String, dynamic> toJson() =>
|
||||||
|
Serialization.moveFromExtraDataToRoot(_$UserToJson(this), topLevelFields);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,29 @@
|
|||||||
import 'platform_detector_stub.dart'
|
import 'package:stream_chat/src/platform_detector/platform_detector_stub.dart'
|
||||||
if (dart.library.html) 'platform_detector_web.dart'
|
if (dart.library.html) 'platform_detector_web.dart'
|
||||||
if (dart.library.io) 'platform_detector_io.dart';
|
if (dart.library.io) 'platform_detector_io.dart';
|
||||||
|
|
||||||
/// Possible platforms
|
/// Possible platforms
|
||||||
enum PlatformType {
|
enum PlatformType {
|
||||||
///
|
///
|
||||||
Android,
|
android,
|
||||||
|
|
||||||
///
|
///
|
||||||
Ios,
|
ios,
|
||||||
|
|
||||||
///
|
///
|
||||||
Web,
|
web,
|
||||||
|
|
||||||
///
|
///
|
||||||
MacOS,
|
macOS,
|
||||||
|
|
||||||
///
|
///
|
||||||
Windows,
|
windows,
|
||||||
|
|
||||||
///
|
///
|
||||||
Linux,
|
linux,
|
||||||
|
|
||||||
///
|
///
|
||||||
Fuchsia,
|
fuchsia,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Utility class that provides information on the current platform
|
/// Utility class that provides information on the current platform
|
||||||
@@ -31,42 +31,42 @@ class CurrentPlatform {
|
|||||||
CurrentPlatform._();
|
CurrentPlatform._();
|
||||||
|
|
||||||
/// True if the app is running on android
|
/// True if the app is running on android
|
||||||
static bool get isAndroid => type == PlatformType.Android;
|
static bool get isAndroid => type == PlatformType.android;
|
||||||
|
|
||||||
/// True if the app is running on ios
|
/// True if the app is running on ios
|
||||||
static bool get isIos => type == PlatformType.Ios;
|
static bool get isIos => type == PlatformType.ios;
|
||||||
|
|
||||||
/// True if the app is running on web
|
/// True if the app is running on web
|
||||||
static bool get isWeb => type == PlatformType.Web;
|
static bool get isWeb => type == PlatformType.web;
|
||||||
|
|
||||||
/// True if the app is running on macos
|
/// True if the app is running on macos
|
||||||
static bool get isMacOS => type == PlatformType.MacOS;
|
static bool get isMacOS => type == PlatformType.macOS;
|
||||||
|
|
||||||
/// True if the app is running on windows
|
/// True if the app is running on windows
|
||||||
static bool get isWindows => type == PlatformType.Windows;
|
static bool get isWindows => type == PlatformType.windows;
|
||||||
|
|
||||||
/// True if the app is running on linux
|
/// True if the app is running on linux
|
||||||
static bool get isLinux => type == PlatformType.Linux;
|
static bool get isLinux => type == PlatformType.linux;
|
||||||
|
|
||||||
/// True if the app is running on fuchsia
|
/// True if the app is running on fuchsia
|
||||||
static bool get isFuchsia => type == PlatformType.Fuchsia;
|
static bool get isFuchsia => type == PlatformType.fuchsia;
|
||||||
|
|
||||||
/// Returns a string version of the platform
|
/// Returns a string version of the platform
|
||||||
static String get name {
|
static String get name {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case PlatformType.Android:
|
case PlatformType.android:
|
||||||
return 'android';
|
return 'android';
|
||||||
case PlatformType.Ios:
|
case PlatformType.ios:
|
||||||
return 'ios';
|
return 'ios';
|
||||||
case PlatformType.Web:
|
case PlatformType.web:
|
||||||
return 'web';
|
return 'web';
|
||||||
case PlatformType.MacOS:
|
case PlatformType.macOS:
|
||||||
return 'macos';
|
return 'macos';
|
||||||
case PlatformType.Windows:
|
case PlatformType.windows:
|
||||||
return 'windows';
|
return 'windows';
|
||||||
case PlatformType.Linux:
|
case PlatformType.linux:
|
||||||
return 'linux';
|
return 'linux';
|
||||||
case PlatformType.Fuchsia:
|
case PlatformType.fuchsia:
|
||||||
return 'fuchsia';
|
return 'fuchsia';
|
||||||
default:
|
default:
|
||||||
return '';
|
return '';
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'platform_detector.dart';
|
import 'package:stream_chat/src/platform_detector/platform_detector.dart';
|
||||||
|
|
||||||
/// Version running on native systems
|
/// Version running on native systems
|
||||||
PlatformType get currentPlatform {
|
PlatformType get currentPlatform {
|
||||||
if (Platform.isWindows) return PlatformType.Windows;
|
if (Platform.isWindows) return PlatformType.windows;
|
||||||
if (Platform.isFuchsia) return PlatformType.Fuchsia;
|
if (Platform.isFuchsia) return PlatformType.fuchsia;
|
||||||
if (Platform.isMacOS) return PlatformType.MacOS;
|
if (Platform.isMacOS) return PlatformType.macOS;
|
||||||
if (Platform.isLinux) return PlatformType.Linux;
|
if (Platform.isLinux) return PlatformType.linux;
|
||||||
if (Platform.isIOS) return PlatformType.Ios;
|
if (Platform.isIOS) return PlatformType.ios;
|
||||||
return PlatformType.Android;
|
return PlatformType.android;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import 'platform_detector.dart';
|
import 'package:stream_chat/src/platform_detector/platform_detector.dart';
|
||||||
|
|
||||||
/// Stub implementation
|
/// Stub implementation
|
||||||
PlatformType get currentPlatform {
|
PlatformType get currentPlatform {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import 'platform_detector.dart';
|
import 'package:stream_chat/src/platform_detector/platform_detector.dart';
|
||||||
|
|
||||||
/// Version running on web
|
/// Version running on web
|
||||||
PlatformType get currentPlatform => PlatformType.Web;
|
PlatformType get currentPlatform => PlatformType.web;
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ export './src/api/requests.dart';
|
|||||||
export './src/api/responses.dart';
|
export './src/api/responses.dart';
|
||||||
export './src/attachment_file_uploader.dart' show AttachmentFileUploader;
|
export './src/attachment_file_uploader.dart' show AttachmentFileUploader;
|
||||||
export './src/client.dart';
|
export './src/client.dart';
|
||||||
|
export './src/db/chat_persistence_client.dart';
|
||||||
export './src/event_type.dart';
|
export './src/event_type.dart';
|
||||||
|
export './src/extensions/string_extension.dart';
|
||||||
export './src/models/action.dart';
|
export './src/models/action.dart';
|
||||||
export './src/models/attachment.dart';
|
export './src/models/attachment.dart';
|
||||||
export './src/models/attachment_file.dart';
|
export './src/models/attachment_file.dart';
|
||||||
@@ -29,5 +31,3 @@ export './src/models/own_user.dart';
|
|||||||
export './src/models/reaction.dart';
|
export './src/models/reaction.dart';
|
||||||
export './src/models/read.dart';
|
export './src/models/read.dart';
|
||||||
export './src/models/user.dart';
|
export './src/models/user.dart';
|
||||||
export './src/extensions/string_extension.dart';
|
|
||||||
export './src/db/chat_persistence_client.dart';
|
|
||||||
|
|||||||
@@ -2,4 +2,5 @@ import 'package:stream_chat/src/client.dart';
|
|||||||
|
|
||||||
/// Current package version
|
/// Current package version
|
||||||
/// Used in [StreamChatClient] to build the `x-stream-client` header
|
/// Used in [StreamChatClient] to build the `x-stream-client` header
|
||||||
|
// ignore: constant_identifier_names
|
||||||
const PACKAGE_VERSION = '1.3.2+1-beta';
|
const PACKAGE_VERSION = '1.3.2+1-beta';
|
||||||
|
|||||||
@@ -9,23 +9,24 @@ environment:
|
|||||||
sdk: ">=2.7.0 <3.0.0"
|
sdk: ">=2.7.0 <3.0.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
json_annotation: ^3.0.1
|
|
||||||
logging: ^0.11.4
|
|
||||||
dio: ^3.0.10
|
|
||||||
web_socket_channel: ^1.1.0
|
|
||||||
uuid: ^2.2.2
|
|
||||||
async: ^2.4.2
|
async: ^2.4.2
|
||||||
rxdart: ^0.25.0
|
|
||||||
collection: ^1.14.13
|
collection: ^1.14.13
|
||||||
pedantic: ^1.9.2
|
dio: ^3.0.10
|
||||||
meta: ^1.2.4
|
|
||||||
mime: ^0.9.7
|
|
||||||
freezed_annotation: ^0.12.0
|
freezed_annotation: ^0.12.0
|
||||||
http_parser: ^3.1.4
|
http_parser: ^3.1.4
|
||||||
|
json_annotation: ^3.0.1
|
||||||
|
logging: ^0.11.4
|
||||||
|
meta: ^1.2.4
|
||||||
|
mime: ^0.9.7
|
||||||
|
rxdart: ^0.25.0
|
||||||
|
uuid: ^2.2.2
|
||||||
|
web_socket_channel: ^1.1.0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
build_runner: ^1.10.0
|
build_runner: ^1.10.0
|
||||||
json_serializable: ^3.3.0
|
|
||||||
test: ^1.15.7
|
|
||||||
mockito: ^4.1.1
|
|
||||||
freezed: ^0.12.7
|
freezed: ^0.12.7
|
||||||
|
json_serializable: ^3.3.0
|
||||||
|
mockito: ^4.1.1
|
||||||
|
test: ^1.15.7
|
||||||
@@ -188,7 +188,7 @@ void main() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
final query = 'hello';
|
const query = 'hello';
|
||||||
|
|
||||||
final queryParams = {
|
final queryParams = {
|
||||||
'payload': json.encode({
|
'payload': json.encode({
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:jiffy/jiffy.dart';
|
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
/// It shows a date divider depending on the date difference
|
/// It shows a date divider depending on the date difference
|
||||||
|
|||||||
Reference in New Issue
Block a user