From c5252ab4d43bf6b447da73571a49b8ebe7f81803 Mon Sep 17 00:00:00 2001 From: "Neevash Ramdial (Nash)" Date: Wed, 3 Mar 2021 04:44:48 -0400 Subject: [PATCH] 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 --- packages/stream_chat/analysis_options.yaml | 180 +++++++--- packages/stream_chat/lib/src/api/channel.dart | 333 +++++++++--------- .../stream_chat/lib/src/api/requests.dart | 72 ++-- .../stream_chat/lib/src/api/responses.dart | 15 +- .../stream_chat/lib/src/api/retry_policy.dart | 3 +- .../stream_chat/lib/src/api/retry_queue.dart | 14 +- .../stream_chat/lib/src/api/websocket.dart | 77 ++-- .../lib/src/attachment_file_uploader.dart | 8 +- packages/stream_chat/lib/src/client.dart | 271 +++++++------- .../lib/src/db/chat_persistence_client.dart | 65 ++-- packages/stream_chat/lib/src/exceptions.dart | 19 +- .../lib/src/extensions/map_extension.dart | 5 +- .../stream_chat/lib/src/models/action.dart | 12 +- .../lib/src/models/attachment.dart | 105 +++--- .../lib/src/models/attachment_file.dart | 20 +- .../lib/src/models/channel_config.dart | 52 ++- .../lib/src/models/channel_model.dart | 66 ++-- .../lib/src/models/channel_state.dart | 33 +- .../stream_chat/lib/src/models/command.dart | 18 +- .../stream_chat/lib/src/models/device.dart | 12 +- .../stream_chat/lib/src/models/event.dart | 96 +++-- .../stream_chat/lib/src/models/member.dart | 52 +-- .../stream_chat/lib/src/models/message.dart | 110 +++--- packages/stream_chat/lib/src/models/mute.dart | 17 +- .../stream_chat/lib/src/models/own_user.dart | 77 ++-- .../stream_chat/lib/src/models/reaction.dart | 68 ++-- packages/stream_chat/lib/src/models/read.dart | 21 +- .../lib/src/models/serialization.dart | 11 +- packages/stream_chat/lib/src/models/user.dart | 101 +++--- .../platform_detector/platform_detector.dart | 44 +-- .../platform_detector_io.dart | 14 +- .../platform_detector_stub.dart | 2 +- .../platform_detector_web.dart | 4 +- packages/stream_chat/lib/stream_chat.dart | 4 +- packages/stream_chat/lib/version.dart | 1 + packages/stream_chat/pubspec.yaml | 25 +- .../stream_chat/test/src/client_test.dart | 2 +- .../lib/src/system_message.dart | 1 - 38 files changed, 1051 insertions(+), 979 deletions(-) diff --git a/packages/stream_chat/analysis_options.yaml b/packages/stream_chat/analysis_options.yaml index 24e35a1f..7e4b00fa 100644 --- a/packages/stream_chat/analysis_options.yaml +++ b/packages/stream_chat/analysis_options.yaml @@ -1,63 +1,147 @@ -include: package:pedantic/analysis_options.yaml - analyzer: - exclude: + exclude: - lib/**/*.g.dart - lib/**/*.freezed.dart - example/* - test/* - -linter: - rules: - # these rules are documented on and in the same order as - # 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 +linter: + rules: + - always_use_package_imports - avoid_empty_else - - avoid_init_to_null - - avoid_return_types_on_setters - - avoid_web_libraries_in_flutter - - await_only_futures - - camel_case_types + - avoid_relative_lib_imports + - avoid_slow_async_io + - avoid_types_as_parameter_names - cancel_subscriptions - 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 - - empty_constructor_bodies + - diagnostic_describe_all_properties - empty_statements - hash_and_equals - - implementation_imports - # - invariant_booleans - # - iterable_contains_unrelated_type - - library_names - # - library_prefixes - # - list_remove_unrelated_type - # - literal_only_boolean_expressions - - non_constant_identifier_names - # - 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 + - invariant_booleans + - iterable_contains_unrelated_type + - list_remove_unrelated_type + - literal_only_boolean_expressions + - no_adjacent_strings_in_list + - no_duplicate_case_values + - no_logic_in_create_state + - prefer_void_to_null - test_types_in_equals - 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 - unrelated_type_equality_checks - - valid_regexps \ No newline at end of file + - omit_local_variable_types + - use_key_in_widget_constructors + - 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 \ No newline at end of file diff --git a/packages/stream_chat/lib/src/api/channel.dart b/packages/stream_chat/lib/src/api/channel.dart index 8814bfb0..ed80ba57 100644 --- a/packages/stream_chat/lib/src/api/channel.dart +++ b/packages/stream_chat/lib/src/api/channel.dart @@ -2,9 +2,9 @@ import 'dart:async'; import 'dart:convert'; import 'dart:math'; -import 'package:pedantic/pedantic.dart' show unawaited; import 'package:dio/dio.dart'; import 'package:logging/logging.dart'; +import 'package:pedantic/pedantic.dart' show unawaited; import 'package:rxdart/rxdart.dart'; import 'package:stream_chat/src/api/retry_queue.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/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. class Channel { /// Create a channel client instance. @@ -57,7 +50,8 @@ class Channel { set extraData(Map extraData) { if (_initializedCompleter.isCompleted) { 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; } @@ -168,14 +162,15 @@ class Channel { final Completer _initializedCompleter = Completer(); /// 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 get initialized => _initializedCompleter.future; final _cancelableAttachmentUploadRequest = {}; final _messageAttachmentsUploadCompleter = {}; - /// Cancels [attachmentId] upload request. Throws exception if the request hasn't - /// even started yet, Already completed or Already cancelled. + /// Cancels [attachmentId] upload request. Throws exception if the request + /// hasn't even started yet, Already completed or Already cancelled. /// /// Optionally, provide a [reason] for the cancellation. void cancelAttachmentUpload( @@ -185,7 +180,8 @@ class Channel { final cancelToken = _cancelableAttachmentUploadRequest[attachmentId]; if (cancelToken == null) { 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'); @@ -193,15 +189,14 @@ class Channel { } /// Retries the failed [attachmentId] upload request. - Future retryAttachmentUpload(String messageId, String attachmentId) { - return _uploadAttachments(messageId, [attachmentId]); - } + Future retryAttachmentUpload(String messageId, String attachmentId) => + _uploadAttachments(messageId, [attachmentId]); Future _uploadAttachments( String messageId, Iterable attachmentIds, ) { - var message = state.messages.firstWhere( + final message = state.messages.firstWhere( (it) => it.id == messageId, orElse: () => null, ); @@ -228,9 +223,8 @@ class Channel { client.logger.info('Uploading ${it.id} attachment...'); void updateAttachment(Attachment attachment) { - final index = message.attachments.indexWhere((it) { - return it.id == attachment.id; - }); + final index = + message.attachments.indexWhere((it) => it.id == attachment.id); if (index != -1) { message.attachments[index] = attachment; state?.addMessage(message); @@ -239,7 +233,7 @@ class Channel { void onSendProgress(int sent, int total) { debounce( - timeout: Duration(seconds: 1), + timeout: const Duration(seconds: 1), target: updateAttachment, positionalArguments: [ it.copyWith( @@ -270,11 +264,17 @@ class Channel { client.logger.info('Attachment ${it.id} uploaded successfully...'); if (isImage) { updateAttachment( - it.copyWith(imageUrl: url, uploadState: UploadState.success()), + it.copyWith( + imageUrl: url, + uploadState: const UploadState.success(), + ), ); } else { updateAttachment( - it.copyWith(assetUrl: url, uploadState: UploadState.success()), + it.copyWith( + assetUrl: url, + uploadState: const UploadState.success(), + ), ); } }).catchError((e, stk) { @@ -305,6 +305,7 @@ class Channel { (m) => m.id == message?.quotedMessageId, orElse: () => null, ); + // ignore: parameter_assignments message = message.copyWith( createdAt: message.createdAt ?? DateTime.now(), user: _client.state.user, @@ -313,7 +314,7 @@ class Channel { attachments: message.attachments?.map( (it) { if (it.uploadState.isSuccess) return it; - return it.copyWith(uploadState: UploadState.preparing()); + return it.copyWith(uploadState: const UploadState.preparing()); }, )?.toList(), ); @@ -340,6 +341,7 @@ class Channel { message.attachments.map((it) => it.id), )); + // ignore: parameter_assignments message = await attachmentsUploadCompleter.future; } @@ -364,13 +366,14 @@ class Channel { .remove(message.id) ?.completeError('Message Cancelled'); + // ignore: parameter_assignments message = message.copyWith( status: MessageSendingStatus.updating, updatedAt: message.updatedAt ?? DateTime.now(), attachments: message.attachments?.map( (it) { if (it.uploadState.isSuccess) return it; - return it.copyWith(uploadState: UploadState.preparing()); + return it.copyWith(uploadState: const UploadState.preparing()); }, )?.toList(), ); @@ -388,6 +391,7 @@ class Channel { message.attachments.map((it) => it.id), )); + // ignore: parameter_assignments message = await attachmentsUploadCompleter.future; } @@ -423,6 +427,7 @@ class Channel { } try { + // ignore: parameter_assignments message = message.copyWith( type: 'deleted', status: MessageSendingStatus.deleting, @@ -456,7 +461,7 @@ class Channel { throw ArgumentError('Invalid timeout or Expiration date'); } return true; - }()); + }(), 'Check for invalid token or expiration date'); DateTime pinExpires; if (timeoutOrExpirationDate is DateTime) { @@ -475,39 +480,36 @@ class Channel { } /// Unpins provided message - Future unpinMessage(Message message) { - return updateMessage(message.copyWith(pinned: false)); - } + Future unpinMessage(Message message) => + updateMessage(message.copyWith(pinned: false)); /// Send a file to this channel Future sendFile( AttachmentFile file, { ProgressCallback onSendProgress, CancelToken cancelToken, - }) { - return _client.sendFile( - file, - id, - type, - onSendProgress: onSendProgress, - cancelToken: cancelToken, - ); - } + }) => + _client.sendFile( + file, + id, + type, + onSendProgress: onSendProgress, + cancelToken: cancelToken, + ); /// Send an image to this channel Future sendImage( AttachmentFile file, { ProgressCallback onSendProgress, CancelToken cancelToken, - }) { - return _client.sendImage( - file, - id, - type, - onSendProgress: onSendProgress, - cancelToken: cancelToken, - ); - } + }) => + _client.sendImage( + file, + id, + type, + onSendProgress: onSendProgress, + cancelToken: cancelToken, + ); /// A message search. Future search({ @@ -515,35 +517,32 @@ class Channel { Map messageFilters, List sort, PaginationParams paginationParams, - }) { - return _client.search( - { - 'cid': { - r'$in': [cid], + }) => + _client.search( + { + 'cid': { + r'$in': [cid], + }, }, - }, - sort: sort, - query: query, - paginationParams: paginationParams, - messageFilters: messageFilters, - ); - } + sort: sort, + query: query, + paginationParams: paginationParams, + messageFilters: messageFilters, + ); /// Delete a file from this channel Future deleteFile( String url, { CancelToken cancelToken, - }) { - return _client.deleteFile(url, id, type, cancelToken: cancelToken); - } + }) => + _client.deleteFile(url, id, type, cancelToken: cancelToken); /// Delete an image from this channel Future deleteImage( String url, { CancelToken cancelToken, - }) { - return _client.deleteImage(url, id, type, cancelToken: cancelToken); - } + }) => + _client.deleteImage(url, id, type, cancelToken: cancelToken); /// Send an event on this channel Future sendEvent(Event event) { @@ -551,9 +550,7 @@ class Channel { return _client.post( '$_channelURL/event', data: {'event': event.toJson()}, - ).then((res) { - return _client.decode(res.data, EmptyResponse.fromJson); - }); + ).then((res) => _client.decode(res.data, EmptyResponse.fromJson)); } /// Send a reaction to this channel @@ -643,11 +640,10 @@ class Channel { } final latestReactions = [...message.latestReactions ?? []] - ..removeWhere((r) { - return r.userId == reaction.userId && - r.type == reaction.type && - r.messageId == reaction.messageId; - }); + ..removeWhere((r) => + r.userId == reaction.userId && + r.type == reaction.type && + r.messageId == reaction.messageId); final ownReactions = [...latestReactions ?? []] ..removeWhere((it) => it.userId != user.id); @@ -825,7 +821,7 @@ class Channel { }) ..addAll(options); - var response; + ChannelState response; try { response = await query(options: watchOptions); @@ -861,7 +857,8 @@ class Channel { } /// 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 getReplies( String parentId, PaginationParams options, { @@ -940,16 +937,15 @@ class Channel { } /// Creates a new channel - Future create() async { - return query(options: { - 'watch': false, - 'state': false, - 'presence': false, - }); - } + Future create() async => query(options: { + 'watch': false, + 'state': false, + 'presence': false, + }); /// 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 query({ Map options = const {}, PaginationParams messagesPagination, @@ -1112,8 +1108,9 @@ class Channel { }); } - /// Hides the channel from [StreamChatClient.queryChannels] for the user until a message is added - /// If [clearHistory] is set to true - all messages will be removed for the user + /// Hides the channel from [StreamChatClient.queryChannels] for the user + /// until a message is added If [clearHistory] is set to true - all messages + /// will be removed for the user Future hide({bool clearHistory = false}) async { _checkInitialized(); final response = await _client @@ -1134,28 +1131,28 @@ class Channel { return _client.decode(response.data, EmptyResponse.fromJson); } - /// Stream of [Event] coming from websocket connection specific for the channel - /// Pass an eventType as parameter in order to filter just a type of event + /// Stream of [Event] coming from websocket connection specific for the + /// channel. Pass an eventType as parameter in order to filter just a type + /// of event Stream on([ String eventType, String eventType2, String eventType3, String eventType4, - ]) { - return _client - .on( - eventType, - eventType2, - eventType3, - eventType4, - ) - .where((e) => e.cid == cid); - } + ]) => + _client + .on( + eventType, + eventType2, + eventType3, + eventType4, + ) + .where((e) => e.cid == cid); DateTime _lastTypingEvent; - /// First of the [EventType.typingStart] and [EventType.typingStop] events based on the users keystrokes. - /// Call this on every keystroke. + /// First of the [EventType.typingStart] and [EventType.typingStop] events + /// based on the users keystrokes. Call this on every keystroke. Future keyStroke([String parentId]) async { if (config?.typingEvents == false) { return; @@ -1196,15 +1193,14 @@ class Channel { void _checkInitialized() { if (!_initializedCompleter.isCompleted) { 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 class ChannelClientState { - final _subscriptions = []; - /// Creates a new instance listening to events and updating the state ChannelClientState(this._channel, ChannelState channelState) { retryQueue = RetryQueue( @@ -1252,14 +1248,16 @@ class ChannelClientState { _channel._client.chatPersistenceClient ?.getChannelStateByCid(_channel.cid) ?.then((state) { - // Replacing the persistence state members with the latest `channelState.members` - // as they may have changes over the time. + // Replacing the persistence state members with the latest + // `channelState.members` as they may have changes over the time. updateChannelState(state.copyWith(members: channelState.members)); retryFailedMessages(); }); }); } + final _subscriptions = []; + void _computeInitialUnread() { final userRead = channelState?.read?.firstWhere( (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. /// This flag should be managed by UI sdks. - /// When false, any new message (received by WebSocket event - [EventType.messageNew]) will not - /// be pushed on to message list. + /// When false, any new message (received by WebSocket event + /// - [EventType.messageNew]) will not be pushed on to message list. bool get isUpToDate => _isUpToDateController.value; set isUpToDate(bool isUpToDate) => _isUpToDateController.add(isUpToDate); @@ -1357,12 +1355,18 @@ class ChannelClientState { Future retryFailedMessages() async { final failedMessages = [...messages, ...threads.values.expand((v) => v)] - .where((message) => - message.status != null && - message.status != MessageSendingStatus.sent && - message.createdAt.isBefore(DateTime.now().subtract(Duration( - seconds: 1, - )))) + .where( + (message) => + message.status != null && + message.status != MessageSendingStatus.sent && + message.createdAt.isBefore( + DateTime.now().subtract( + const Duration( + seconds: 1, + ), + ), + ), + ) .toList(); retryQueue.add(failedMessages); @@ -1471,27 +1475,32 @@ class ChannelClientState { return; } - _subscriptions.add(_channel - .on( - EventType.messageRead, - EventType.notificationMarkRead, - ) - .listen((event) { - final readList = List.from(_channelState?.read ?? []); - final userReadIndex = read?.indexWhere((r) => r.user.id == event.user.id); + _subscriptions.add( + _channel + .on( + EventType.messageRead, + EventType.notificationMarkRead, + ) + .listen( + (event) { + final readList = List.from(_channelState?.read ?? []); + final userReadIndex = + read?.indexWhere((r) => r.user.id == event.user.id); - if (userReadIndex != null && userReadIndex != -1) { - final userRead = readList.removeAt(userReadIndex); - if (userRead.user?.id == _channel._client.state.user.id) { - _unreadCountController.add(0); - } - readList.add(Read( - user: event.user, - lastRead: event.createdAt, - )); - _channelState = _channelState.copyWith(read: readList); - } - })); + if (userReadIndex != null && userReadIndex != -1) { + final userRead = readList.removeAt(userReadIndex); + if (userRead.user?.id == _channel._client.state.user.id) { + _unreadCountController.add(0); + } + readList.add(Read( + user: event.user, + lastRead: event.createdAt, + )); + _channelState = _channelState.copyWith(read: readList); + } + }, + ), + ); } /// Channel message list @@ -1527,11 +1536,8 @@ class ChannelClientState { List, Map, List>( channelStateStream.map((cs) => cs.members), _channel.client.state.usersStream, - (members, users) { - return members - .map((e) => e.copyWith(user: users[e.user.id])) - .toList(); - }, + (members, users) => + members.map((e) => e.copyWith(user: users[e.user.id])).toList(), ); /// Channel watcher count @@ -1551,9 +1557,7 @@ class ChannelClientState { CombineLatestStream.combine2, Map, List>( channelStateStream.map((cs) => cs.watchers), _channel.client.state.usersStream, - (watchers, users) { - return watchers.map((e) => users[e.id] ?? e).toList(); - }, + (watchers, users) => watchers.map((e) => users[e.id] ?? e).toList(), ); /// Channel read list @@ -1625,9 +1629,7 @@ class ChannelClientState { true) ?.toList() ?? [], - ]; - - newMessages.sort(_sortByCreatedAt); + ]..sort(_sortByCreatedAt); final newWatchers = [ ...updatedState?.watchers ?? [], @@ -1692,10 +1694,9 @@ class ChannelClientState { set _channelState(ChannelState v) { _channelStateController.add(v); - if (_channel._client.persistenceEnabled) { debounce( - timeout: Duration(milliseconds: 500), + timeout: const Duration(milliseconds: 500), target: _channel._client.chatPersistenceClient?.updateChannelState, positionalArguments: [v], ); @@ -1735,28 +1736,37 @@ class ChannelClientState { return; } - _subscriptions.add(_channel.on(EventType.typingStart).listen((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()); - } - })); + _subscriptions + ..add( + _channel.on(EventType.typingStart).listen( + (event) { + if (event.user.id != _channel.client.state.user.id) { + _typings[event.user] = DateTime.now(); + _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; + void _startCleaning() { if (_channel.config?.typingEvents == false) { return; } - _cleaningTimer = Timer.periodic(Duration(seconds: 1), (_) { + _cleaningTimer = Timer.periodic(const Duration(seconds: 1), (_) { final now = DateTime.now(); if (_channel._lastTypingEvent != null && @@ -1769,8 +1779,9 @@ class ChannelClientState { } Timer _pinnedMessagesTimer; + void _startCleaningPinnedMessages() { - _pinnedMessagesTimer = Timer.periodic(Duration(seconds: 30), (_) { + _pinnedMessagesTimer = Timer.periodic(const Duration(seconds: 30), (_) { final now = DateTime.now(); var expiredMessages = channelState.pinnedMessages ?.where((m) => m.pinExpires?.isBefore(now) == true) @@ -1781,8 +1792,6 @@ class ChannelClientState { .map((m) => m.copyWith( pinExpires: null, pinned: false, - pinnedAt: null, - pinnedBy: null, )) .toList(); diff --git a/packages/stream_chat/lib/src/api/requests.dart b/packages/stream_chat/lib/src/api/requests.dart index 414113de..764d249a 100644 --- a/packages/stream_chat/lib/src/api/requests.dart +++ b/packages/stream_chat/lib/src/api/requests.dart @@ -5,22 +5,6 @@ part 'requests.g.dart'; /// Sorting options @JsonSerializable(createFactory: false) class SortOption { - /// 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 comparator; - /// Creates a new SortOption instance /// /// For example: @@ -34,6 +18,24 @@ class SortOption { 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 comparator; + /// Serialize model to json Map toJson() => _$SortOptionToJson(this); } @@ -41,6 +43,25 @@ class SortOption { /// Pagination options. @JsonSerializable(createFactory: false, includeIfNull: false) 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. final int limit; @@ -63,25 +84,6 @@ class PaginationParams { @JsonKey(name: 'id_lte') 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 Map toJson() => _$PaginationParamsToJson(this); diff --git a/packages/stream_chat/lib/src/api/responses.dart b/packages/stream_chat/lib/src/api/responses.dart index a1e928d6..431a57e7 100644 --- a/packages/stream_chat/lib/src/api/responses.dart +++ b/packages/stream_chat/lib/src/api/responses.dart @@ -1,15 +1,14 @@ import 'package:json_annotation/json_annotation.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/event.dart'; - -import '../models/channel_model.dart'; -import '../models/channel_state.dart'; -import '../models/member.dart'; -import '../models/message.dart'; -import '../models/reaction.dart'; -import '../models/read.dart'; -import '../models/user.dart'; +import 'package:stream_chat/src/models/member.dart'; +import 'package:stream_chat/src/models/message.dart'; +import 'package:stream_chat/src/models/reaction.dart'; +import 'package:stream_chat/src/models/read.dart'; +import 'package:stream_chat/src/models/user.dart'; part 'responses.g.dart'; diff --git a/packages/stream_chat/lib/src/api/retry_policy.dart b/packages/stream_chat/lib/src/api/retry_policy.dart index 2c59ec48..a4d1e7bd 100644 --- a/packages/stream_chat/lib/src/api/retry_policy.dart +++ b/packages/stream_chat/lib/src/api/retry_policy.dart @@ -18,7 +18,8 @@ class RetryPolicy { final bool Function(StreamChatClient client, int attempt, ApiError apiError) 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( StreamChatClient client, int attempt, ApiError apiError) retryTimeout; diff --git a/packages/stream_chat/lib/src/api/retry_queue.dart b/packages/stream_chat/lib/src/api/retry_queue.dart index 34046545..ac0e14d7 100644 --- a/packages/stream_chat/lib/src/api/retry_queue.dart +++ b/packages/stream_chat/lib/src/api/retry_queue.dart @@ -1,8 +1,8 @@ import 'dart:async'; import 'package:collection/collection.dart'; -import 'package:meta/meta.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/retry_policy.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 class RetryQueue { - /// The channel of this queue - final Channel channel; - - /// The logger associated to this queue - final Logger logger; - /// Instantiate a new RetryQueue object RetryQueue({ @required this.channel, @@ -30,6 +24,12 @@ class RetryQueue { _listenFailedEvents(); } + /// The channel of this queue + final Channel channel; + + /// The logger associated to this queue + final Logger logger; + final _subscriptions = []; void _listenConnectionRecovered() { diff --git a/packages/stream_chat/lib/src/api/websocket.dart b/packages/stream_chat/lib/src/api/websocket.dart index 15102c6e..ea2bb291 100644 --- a/packages/stream_chat/lib/src/api/websocket.dart +++ b/packages/stream_chat/lib/src/api/websocket.dart @@ -2,28 +2,29 @@ import 'dart:async'; import 'dart:convert'; import 'dart:math'; -import 'package:meta/meta.dart'; import 'package:logging/logging.dart'; +import 'package:meta/meta.dart'; import 'package:rxdart/rxdart.dart'; -import 'package:web_socket_channel/web_socket_channel.dart'; - -import '../models/event.dart'; -import '../models/user.dart'; -import 'connection_status.dart'; -import 'web_socket_channel_stub.dart' +import 'package:stream_chat/src/api/connection_status.dart'; +import 'package:stream_chat/src/api/web_socket_channel_stub.dart' if (dart.library.html) 'web_socket_channel_html.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 EventHandler = void Function(Event); -/// Typedef used for connecting to a websocket. Method returns a [WebSocketChannel] -/// and accepts a connection [url] and an optional [Iterable] of `protocols`. +/// Typedef used for connecting to a websocket. Method returns a +/// [WebSocketChannel] and accepts a connection [url] and an optional +/// [Iterable] of `protocols`. typedef ConnectWebSocket = WebSocketChannel Function(String url, {Iterable protocols}); // 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. class WebSocket { /// Creates a new websocket @@ -75,7 +76,8 @@ class WebSocket { /// WS connection payload final Map 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; /// A WS specific logger instance @@ -87,8 +89,9 @@ class WebSocket { final ConnectWebSocket connectFunc; /// Interval of the reconnection monitor timer - /// This checks that it received a new event in the last [reconnectionMonitorTimeout] seconds, - /// otherwise it considers the connection unhealthy and reconnects the WS + /// This checks that it received a new event in the last + /// [reconnectionMonitorTimeout] seconds, otherwise it considers the + /// connection unhealthy and reconnects the WS final int reconnectionMonitorInterval; /// Interval of the health event sending timer @@ -96,7 +99,8 @@ class WebSocket { /// make the server aware that the client is still listening 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 _connectionStatusController = @@ -121,9 +125,7 @@ class WebSocket { _connecting = false, _reconnecting = false; - Event _decodeEvent(String source) { - return Event.fromJson(json.decode(source)); - } + Event _decodeEvent(String source) => Event.fromJson(json.decode(source)); Completer _connectionCompleter = Completer(); @@ -166,8 +168,8 @@ class WebSocket { return; } - logger.info( - 'connection closed | closeCode: ${_channel.closeCode} | closedReason: ${_channel.closeReason}'); + logger.info('connection closed | closeCode: ${_channel.closeCode} | ' + 'closedReason: ${_channel.closeReason}'); if (!_reconnecting) { _reconnect(); @@ -200,8 +202,7 @@ class WebSocket { } Future _onConnectionError(error, [stacktrace]) async { - logger.severe('error connecting'); - logger.severe(error); + logger..severe('error connecting')..severe(error); if (stacktrace != null) { logger.severe(stacktrace); } @@ -219,21 +220,21 @@ class WebSocket { } } - void _startReconnectionMonitor() { - final reconnectionTimer = (_) { - final now = DateTime.now(); - if (_lastEventAt != null && - now.difference(_lastEventAt).inSeconds > reconnectionMonitorTimeout) { - _channel.sink.close(); - } - }; + void _reconnectionTimer(_) { + final now = DateTime.now(); + if (_lastEventAt != null && + now.difference(_lastEventAt).inSeconds > reconnectionMonitorTimeout) { + _channel.sink.close(); + } + } + void _startReconnectionMonitor() { _reconnectionMonitor = Timer.periodic( Duration(seconds: reconnectionMonitorInterval), - reconnectionTimer, + _reconnectionTimer, ); - reconnectionTimer(_reconnectionMonitor); + _reconnectionTimer(_reconnectionMonitor); } void _reconnectTimer() async { @@ -283,20 +284,20 @@ class WebSocket { } } + void _healthCheckTimer(_) { + logger.info('sending health.check'); + _channel.sink.add("{'type': 'health.check'}"); + } + void _startHealthCheck() { logger.info('start health check monitor'); - final healthCheckTimer = (_) { - logger.info('sending health.check'); - _channel.sink.add("{'type': 'health.check'}"); - }; - _healthCheck = Timer.periodic( Duration(seconds: healthCheckInterval), - healthCheckTimer, + _healthCheckTimer, ); - healthCheckTimer(_healthCheck); + _healthCheckTimer(_healthCheck); } /// Disconnects the WS and releases eventual resources diff --git a/packages/stream_chat/lib/src/attachment_file_uploader.dart b/packages/stream_chat/lib/src/attachment_file_uploader.dart index c420728f..33d9b351 100644 --- a/packages/stream_chat/lib/src/attachment_file_uploader.dart +++ b/packages/stream_chat/lib/src/attachment_file_uploader.dart @@ -1,8 +1,8 @@ import 'package:dio/dio.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 'client.dart'; -import 'extensions/string_extension.dart'; +import 'package:stream_chat/src/extensions/string_extension.dart'; /// Class responsible for uploading images and files to a given channel abstract class AttachmentFileUploader { @@ -57,11 +57,11 @@ abstract class AttachmentFileUploader { /// Stream's default implementation of [AttachmentFileUploader] class StreamAttachmentFileUploader implements AttachmentFileUploader { - final StreamChatClient _client; - /// Creates a new [StreamAttachmentFileUploader] instance. const StreamAttachmentFileUploader(this._client); + final StreamChatClient _client; + @override Future sendImage( AttachmentFile file, diff --git a/packages/stream_chat/lib/src/client.dart b/packages/stream_chat/lib/src/client.dart index 9aed9aa3..d4ae119c 100644 --- a/packages/stream_chat/lib/src/client.dart +++ b/packages/stream_chat/lib/src/client.dart @@ -1,36 +1,35 @@ import 'dart:async'; import 'dart:convert'; +import 'package:stream_chat/src/extensions/map_extension.dart'; import 'package:dio/dio.dart'; import 'package:logging/logging.dart'; import 'package:meta/meta.dart'; import 'package:pedantic/pedantic.dart' show unawaited; 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/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/exceptions.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_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/user.dart'; import 'package:stream_chat/src/platform_detector/platform_detector.dart'; import 'package:stream_chat/version.dart'; import 'package:uuid/uuid.dart'; -import 'attachment_file_uploader.dart'; -import 'api/channel.dart'; -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. +/// Handler function used for logging records. Function requires a single +/// [LogRecord] as the only parameter. typedef LogHandlerFunction = void Function(LogRecord record); /// Used for decoding [Map] data to a generic type `T`. @@ -62,7 +61,9 @@ extension on PushProvider { /// The official Dart client for Stream Chat, /// 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/ /// /// The Chat client will manage API call, event handling and manage the @@ -73,7 +74,8 @@ extension on PushProvider { /// ``` class StreamChatClient { /// 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( this.apiKey, { this.tokenProvider, @@ -83,6 +85,7 @@ class StreamChatClient { Duration connectTimeout = const Duration(seconds: 6), Duration receiveTimeout = const Duration(seconds: 6), Dio httpClient, + // ignore: avoid_unused_constructor_parameters RetryPolicy retryPolicy, this.attachmentFileUploader, }) { @@ -122,30 +125,39 @@ class StreamChatClient { /// This client state ClientState state; - /// By default the Chat client will write all messages with level Warn or Error to stdout. - /// During development you might want to enable more logging information, you can change the default log level when constructing the client. + /// By default the Chat client will write all messages with level Warn or + /// 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 - /// final client = StreamChatClient("stream-chat-api-key", logLevel: Level.INFO); + /// final client = StreamChatClient("stream-chat-api-key", + /// logLevel: Level.INFO); /// ``` final Level logLevel; /// 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('📡'); /// A function that has a parameter of type [LogRecord]. /// This is called on every new log record. - /// By default the client will use the handler returned by [_getDefaultLogHandler]. - /// 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. + /// By default the client will use the handler returned by + /// [_getDefaultLogHandler]. + /// 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 /// myLogHandlerFunction = (LogRecord record) { /// // 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; @@ -156,14 +168,18 @@ class StreamChatClient { /// Your project Stream Chat base url. 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. - /// 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; /// [Dio] httpClient - /// It's be chosen because it's easy to use and supports interesting features out of the box - /// (Interceptors, Global configuration, FormData, File downloading etc.) + /// It's be chosen because it's easy to use and supports interesting features + /// out of the box (Interceptors, Global configuration, FormData, + /// File downloading etc.) @visibleForTesting Dio httpClient = Dio(); @@ -234,7 +250,7 @@ class StreamChatClient { (options.data is Map || options.data == null)) { options.data = { 'connection_id': _connectionId, - ...(options.data ?? {}), + ...options.data ?? {}, }; } @@ -278,7 +294,7 @@ class StreamChatClient { await _disconnect(); final newToken = await tokenProvider(userId); - await Future.delayed(Duration(seconds: 4)); + await Future.delayed(const Duration(seconds: 4)); token = newToken; httpClient.unlock(); @@ -312,7 +328,10 @@ class StreamChatClient { }; return (LogRecord record) { 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) { print(record.stackTrace); } @@ -321,11 +340,10 @@ class StreamChatClient { Logger _detachedLogger( String name, - ) { - return Logger.detached(name) - ..level = logLevel - ..onRecord.listen(logHandlerFunction ?? _getDefaultLogHandler()); - } + ) => + Logger.detached(name) + ..level = logLevel + ..onRecord.listen(logHandlerFunction ?? _getDefaultLogHandler()); void _setupLogger() { logger.level = logLevel; @@ -386,7 +404,8 @@ class StreamChatClient { /// Set the current user using the [tokenProvider] to fetch the token. /// It returns a [Future] that resolves when the connection is setup. @Deprecated( - 'Use `connectUserWithProvider` instead. Will be removed in Future releases') + 'Use `connectUserWithProvider` instead. Will be removed in Future releases', + ) Future setUserWithProvider(User user) => connectUserWithProvider(user); /// Connects the current user using the [tokenProvider] to fetch the token. @@ -521,6 +540,7 @@ class StreamChatClient { }).catchError((err, stacktrace) { logger.severe('error connecting ws', err, stacktrace); if (err is Map) { + // ignore: only_throw_errors throw err; } }); @@ -558,13 +578,12 @@ class StreamChatClient { res.events.sort((a, b) => a.createdAt.compareTo(b.createdAt)); res.events.forEach((element) { - logger.fine('element.type: ${element.type}'); - logger.fine('element.message.text: ${element.message?.text}'); + logger + ..fine('element.type: ${element.type}') + ..fine('element.message.text: ${element.message?.text}'); }); - res.events.forEach((event) { - handleEvent(event); - }); + res.events.forEach(handleEvent); await chatPersistenceClient?.updateLastSyncAt(DateTime.now()); _synced = true; @@ -573,9 +592,7 @@ class StreamChatClient { } } - String _asMap(sort) { - return sort?.map((s) => s.toJson().toString())?.join(''); - } + String _asMap(sort) => sort?.map((s) => s.toJson().toString())?.join(''); final _queryChannelsStreams = >>{}; @@ -584,13 +601,14 @@ class StreamChatClient { Map filter, List> sort, Map options, - PaginationParams paginationParams = const PaginationParams(limit: 10), + PaginationParams paginationParams = const PaginationParams(), int messageLimit, bool preferOffline = false, bool waitForConnect = true, }) async* { 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)) { @@ -627,7 +645,7 @@ class StreamChatClient { List> sort, Map options, int messageLimit, - PaginationParams paginationParams = const PaginationParams(limit: 10), + PaginationParams paginationParams = const PaginationParams(), bool waitForConnect = true, }) async { if (waitForConnect) { @@ -650,7 +668,7 @@ class StreamChatClient { 'presence': false, }; - var payload = { + final payload = { 'filter_conditions': filter, 'sort': sort, }; @@ -682,9 +700,12 @@ class StreamChatClient { ); 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 - 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 []; } @@ -715,7 +736,7 @@ class StreamChatClient { Future> queryChannelsOffline({ @required Map filter, @required List> sort, - PaginationParams paginationParams = const PaginationParams(limit: 10), + PaginationParams paginationParams = const PaginationParams(), }) async { final offlineChannels = await chatPersistenceClient?.getChannelStates( filter: filter, @@ -771,6 +792,7 @@ class StreamChatClient { ); return response; } on DioError catch (error) { + // ignore: only_throw_errors throw _parseError(error); } } @@ -791,6 +813,7 @@ class StreamChatClient { ); return response; } on DioError catch (error) { + // ignore: only_throw_errors throw _parseError(error); } } @@ -809,6 +832,7 @@ class StreamChatClient { ); return response; } on DioError catch (error) { + // ignore: only_throw_errors throw _parseError(error); } } @@ -827,6 +851,7 @@ class StreamChatClient { ); return response; } on DioError catch (error) { + // ignore: only_throw_errors throw _parseError(error); } } @@ -845,6 +870,7 @@ class StreamChatClient { ); return response; } on DioError catch (error) { + // ignore: only_throw_errors throw _parseError(error); } } @@ -864,8 +890,8 @@ class StreamChatClient { String get _authType => _anonymous ? 'anonymous' : 'jwt'; - String get _userAgent => - 'stream-chat-dart-client-${CurrentPlatform.name}-${PACKAGE_VERSION.split('+')[0]}'; + String get _userAgent => 'stream-chat-dart-client-${CurrentPlatform.name}-' + '${PACKAGE_VERSION.split('+')[0]}'; Map get _commonQueryParams => { 'user_id': state.user?.id, @@ -873,14 +899,15 @@ class StreamChatClient { 'connection_id': _connectionId, }; - /// Set the current user with an anonymous id, this triggers a connection to the API. - /// It returns a [Future] that resolves when the connection is setup. + /// Set the current user with an anonymous id, this triggers a connection to + /// the API. It returns a [Future] that resolves when the connection is setup. @Deprecated( 'Use `connectAnonymousUser` instead. Will be removed in Future releases') Future setAnonymousUser() => connectAnonymousUser(); - /// Connects the current user with an anonymous id, this triggers a connection to the API. - /// It returns a [Future] that resolves when the connection is setup. + /// Connects the current user with an anonymous id, this triggers a connection + /// to the API. It returns a [Future] that resolves when the connection is + /// setup. Future connectAnonymousUser() async { if (_connectCompleter != null && !_connectCompleter.isCompleted) { logger.warning('Already connecting'); @@ -923,14 +950,14 @@ class StreamChatClient { } /// Closes the websocket connection and resets the client - /// If [flushChatPersistence] is true the client deletes all offline user's data - /// If [clearUser] is true the client unsets the current user + /// If [flushChatPersistence] is true the client deletes all offline + /// user's data. If [clearUser] is true the client unsets the current user Future disconnect({ bool flushChatPersistence = false, bool clearUser = false, }) async { - logger.info( - 'Disconnecting flushOfflineStorage: $flushChatPersistence; clearUser: $clearUser'); + logger.info('Disconnecting flushOfflineStorage: $flushChatPersistence; ' + 'clearUser: $clearUser'); await chatPersistenceClient?.disconnect(flush: flushChatPersistence); chatPersistenceClient = null; @@ -966,9 +993,7 @@ class StreamChatClient { final payload = { 'filter_conditions': filter ?? {}, 'sort': sort, - }; - - payload.addAll(defaultOptions); + }..addAll(defaultOptions); if (pagination != null) { payload.addAll(pagination.toJson()); @@ -1016,7 +1041,7 @@ class StreamChatClient { ); } return true; - }()); + }(), 'Check incoming params.'); final payload = { 'filter_conditions': filters, @@ -1041,15 +1066,14 @@ class StreamChatClient { String channelType, { ProgressCallback onSendProgress, CancelToken cancelToken, - }) { - return attachmentFileUploader.sendFile( - file, - channelId, - channelType, - onSendProgress: onSendProgress, - cancelToken: cancelToken, - ); - } + }) => + attachmentFileUploader.sendFile( + file, + channelId, + channelType, + onSendProgress: onSendProgress, + cancelToken: cancelToken, + ); /// Send a [image] to the [channelId] of type [channelType] Future sendImage( @@ -1058,15 +1082,14 @@ class StreamChatClient { String channelType, { ProgressCallback onSendProgress, CancelToken cancelToken, - }) { - return attachmentFileUploader.sendImage( - image, - channelId, - channelType, - onSendProgress: onSendProgress, - cancelToken: cancelToken, - ); - } + }) => + attachmentFileUploader.sendImage( + image, + channelId, + channelType, + onSendProgress: onSendProgress, + cancelToken: cancelToken, + ); /// Delete a file from this channel Future deleteFile( @@ -1074,14 +1097,13 @@ class StreamChatClient { String channelId, String channelType, { CancelToken cancelToken, - }) { - return attachmentFileUploader.deleteFile( - url, - channelId, - channelType, - cancelToken: cancelToken, - ); - } + }) => + attachmentFileUploader.deleteFile( + url, + channelId, + channelType, + cancelToken: cancelToken, + ); /// Delete an image from this channel Future deleteImage( @@ -1089,14 +1111,13 @@ class StreamChatClient { String channelId, String channelType, { CancelToken cancelToken, - }) { - return attachmentFileUploader.deleteImage( - url, - channelId, - channelType, - cancelToken: cancelToken, - ); - } + }) => + attachmentFileUploader.deleteImage( + url, + channelId, + channelType, + cancelToken: cancelToken, + ); /// Add a device for Push Notifications. Future addDevice(String id, PushProvider pushProvider) async { @@ -1146,9 +1167,8 @@ class StreamChatClient { } /// Update or Create the given user object. - Future updateUser(User user) async { - return updateUsers([user]); - } + Future updateUser(User user) async => + updateUsers([user]); /// Batch update a list of users Future updateUsers(List users) async { @@ -1197,23 +1217,21 @@ class StreamChatClient { Future shadowBan( String targetID, [ Map options = const {}, - ]) async { - return banUser(targetID, { - 'shadow': true, - ...options, - }); - } + ]) async => + banUser(targetID, { + 'shadow': true, + ...options, + }); /// Removes shadow ban from a user Future removeShadowBan( String targetID, [ Map options = const {}, - ]) async { - return unbanUser(targetID, { - 'shadow': true, - ...options, - }); - } + ]) async => + unbanUser(targetID, { + 'shadow': true, + ...options, + }); /// Mutes a user Future muteUser(String targetID) async { @@ -1312,7 +1330,7 @@ class StreamChatClient { throw ArgumentError('Invalid timeout or Expiration date'); } return true; - }()); + }(), 'Check whether time out is valid'); DateTime pinExpires; if (timeoutOrExpirationDate is DateTime) { @@ -1328,15 +1346,12 @@ class StreamChatClient { } /// Unpins provided message - Future unpinMessage(Message message) { - return updateMessage(message.copyWith(pinned: false)); - } + Future unpinMessage(Message message) => + updateMessage(message.copyWith(pinned: false)); } /// The class that handles the state of the channel listening to the events class ClientState { - final _subscriptions = []; - /// Creates a new instance listening to events and updating the state ClientState(this._client) { _subscriptions.addAll([ @@ -1358,16 +1373,12 @@ class ClientState { .on() .where((event) => event.unreadChannels != null) .map((e) => e.unreadChannels) - .listen((unreadChannels) { - _unreadChannelsController.add(unreadChannels); - }), + .listen(_unreadChannelsController.add), _client .on() .where((event) => event.totalUnreadCount != null) .map((e) => e.totalUnreadCount) - .listen((totalUnreadCount) { - _totalUnreadCountController.add(totalUnreadCount); - }), + .listen(_totalUnreadCountController.add), ]); _listenChannelDeleted(); @@ -1377,6 +1388,8 @@ class ClientState { _listenUserUpdated(); } + final _subscriptions = []; + /// Used internally for optimistic update of unread count set totalUnreadCount(int unreadCount) { _totalUnreadCountController?.add(unreadCount ?? 0); diff --git a/packages/stream_chat/lib/src/db/chat_persistence_client.dart b/packages/stream_chat/lib/src/db/chat_persistence_client.dart index da99f34e..d90872a2 100644 --- a/packages/stream_chat/lib/src/db/chat_persistence_client.dart +++ b/packages/stream_chat/lib/src/db/chat_persistence_client.dart @@ -101,18 +101,17 @@ abstract class ChatPersistenceClient { Future updateChannelQueries( Map filter, List cids, + // ignore: avoid_positional_boolean_parameters bool clearQueryCache, ); /// Remove a message by [messageId] - Future deleteMessageById(String messageId) { - return deleteMessageByIds([messageId]); - } + Future deleteMessageById(String messageId) => + deleteMessageByIds([messageId]); /// Remove a pinned message by [messageId] - Future deletePinnedMessageById(String messageId) { - return deletePinnedMessageByIds([messageId]); - } + Future deletePinnedMessageById(String messageId) => + deletePinnedMessageByIds([messageId]); /// Remove a message by [messageIds] Future deleteMessageByIds(List messageIds); @@ -121,14 +120,11 @@ abstract class ChatPersistenceClient { Future deletePinnedMessageByIds(List messageIds); /// Remove a message by channel [cid] - Future deleteMessageByCid(String cid) { - return deleteMessageByCids([cid]); - } + Future deleteMessageByCid(String cid) => deleteMessageByCids([cid]); /// Remove a pinned message by channel [cid] - Future deletePinnedMessageByCid(String cid) { - return deletePinnedMessageByCids([cid]); - } + Future deletePinnedMessageByCid(String cid) async => + deletePinnedMessageByCids([cid]); /// Remove a message by message [cids] Future deleteMessageByCids(List cids); @@ -175,9 +171,8 @@ abstract class ChatPersistenceClient { Future deleteMembersByCids(List cids); /// Update the channel state data using [channelState] - Future updateChannelState(ChannelState channelState) { - return updateChannelStates([channelState]); - } + Future updateChannelState(ChannelState channelState) => + updateChannelStates([channelState]); /// Update list of channel states Future updateChannelStates(List channelStates) async { @@ -195,31 +190,31 @@ abstract class ChatPersistenceClient { deleteMembers, ]); - final channels = channelStates.map((it) { - return it.channel; - }).where((it) => it != null); + final channels = + channelStates.map((it) => it.channel).where((it) => it != null); - final reactions = channelStates.expand((it) => it.messages).expand((it) { - return [ - if (it.ownReactions != null) - ...it.ownReactions.where((r) => r.userId != null), - if (it.latestReactions != null) - ...it.latestReactions.where((r) => r.userId != null) - ]; - }).where((it) => it != null); + final reactions = channelStates + .expand((it) => it.messages) + .expand((it) => [ + if (it.ownReactions != null) + ...it.ownReactions.where((r) => r.userId != null), + if (it.latestReactions != null) + ...it.latestReactions.where((r) => r.userId != null) + ]) + .where((it) => it != null); final users = channelStates .map((cs) => [ cs.channel?.createdBy, - ...cs.messages?.map((m) { - return [ - m.user, - if (m.latestReactions != null) - ...m.latestReactions.map((r) => r.user), - if (m.ownReactions != null) - ...m.ownReactions.map((r) => r.user), - ]; - })?.expand((v) => v), + ...cs.messages + ?.map((m) => [ + m.user, + if (m.latestReactions != null) + ...m.latestReactions.map((r) => r.user), + if (m.ownReactions != null) + ...m.ownReactions.map((r) => r.user), + ]) + ?.expand((v) => v), if (cs.read != null) ...cs.read.map((r) => r.user), if (cs.members != null) ...cs.members.map((m) => m.user), ]) diff --git a/packages/stream_chat/lib/src/exceptions.dart b/packages/stream_chat/lib/src/exceptions.dart index 9ba27e79..60dd6bdf 100644 --- a/packages/stream_chat/lib/src/exceptions.dart +++ b/packages/stream_chat/lib/src/exceptions.dart @@ -2,6 +2,13 @@ import 'dart:convert'; /// Exception related to api calls 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 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 bool operator ==(Object other) => identical(this, other) || @@ -48,7 +48,6 @@ class ApiError extends Error { body.hashCode ^ jsonData.hashCode ^ status.hashCode ^ _code.hashCode; @override - String toString() { - return 'ApiError{body: $body, jsonData: $jsonData, status: $status, code: $_code}'; - } + String toString() => 'ApiError{body: $body, jsonData: $jsonData, ' + 'status: $status, code: $_code}'; } diff --git a/packages/stream_chat/lib/src/extensions/map_extension.dart b/packages/stream_chat/lib/src/extensions/map_extension.dart index 3028c20a..1a3376ad 100644 --- a/packages/stream_chat/lib/src/extensions/map_extension.dart +++ b/packages/stream_chat/lib/src/extensions/map_extension.dart @@ -1,7 +1,6 @@ /// Useful extension functions for [Map] extension MapX on Map { /// Returns a new map with null keys or values removed - Map get nullProtected { - return {...this}..removeWhere((key, value) => key == null || value == null); - } + Map get nullProtected => + {...this}..removeWhere((key, value) => key == null || value == null); } diff --git a/packages/stream_chat/lib/src/models/action.dart b/packages/stream_chat/lib/src/models/action.dart index 34bb8f7e..62d0f104 100644 --- a/packages/stream_chat/lib/src/models/action.dart +++ b/packages/stream_chat/lib/src/models/action.dart @@ -5,6 +5,12 @@ part 'action.g.dart'; /// The class that contains the information about an action @JsonSerializable() 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 json) => _$ActionFromJson(json); + /// The name of the action final String name; @@ -20,12 +26,6 @@ class Action { /// The value of the action 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 json) => _$ActionFromJson(json); - /// Serialize to json Map toJson() => _$ActionToJson(this); } diff --git a/packages/stream_chat/lib/src/models/attachment.dart b/packages/stream_chat/lib/src/models/attachment.dart index e2e81cd9..de5149db 100644 --- a/packages/stream_chat/lib/src/models/attachment.dart +++ b/packages/stream_chat/lib/src/models/attachment.dart @@ -1,18 +1,61 @@ // ignore_for_file: public_member_api_docs 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/serialization.dart'; import 'package:uuid/uuid.dart'; -import 'action.dart'; -import 'serialization.dart'; - part 'attachment.g.dart'; /// The class that contains the information about an attachment @JsonSerializable(includeIfNull: false) 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 json) => + _$AttachmentFromJson( + Serialization.moveToExtraDataFromRoot(json, topLevelFields)); + + /// Create a new instance from a db data + factory Attachment.fromData(Map 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; ///The link to which the attachment message points to. @@ -21,10 +64,12 @@ class Attachment { /// The attachment 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; - /// 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; /// Optional text that appears above the attachment block @@ -33,7 +78,8 @@ class Attachment { /// The original URL that was used to scrape this attachment. 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 footerIcon; final String footer; @@ -100,56 +146,11 @@ class Attachment { '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 json) { - return _$AttachmentFromJson( - Serialization.moveToExtraDataFromRoot(json, topLevelFields)); - } - /// Serialize to json Map toJson() => Serialization.moveFromExtraDataToRoot( _$AttachmentToJson(this), topLevelFields) ..removeWhere((key, value) => dbSpecificTopLevelFields.contains(key)); - /// Create a new instance from a db data - factory Attachment.fromData(Map json) { - return _$AttachmentFromJson(Serialization.moveToExtraDataFromRoot( - json, topLevelFields + dbSpecificTopLevelFields)); - } - /// Serialize to db data Map toData() => Serialization.moveFromExtraDataToRoot( _$AttachmentToJson(this), topLevelFields + dbSpecificTopLevelFields); diff --git a/packages/stream_chat/lib/src/models/attachment_file.dart b/packages/stream_chat/lib/src/models/attachment_file.dart index f1c10d5c..bae7810f 100644 --- a/packages/stream_chat/lib/src/models/attachment_file.dart +++ b/packages/stream_chat/lib/src/models/attachment_file.dart @@ -1,10 +1,9 @@ import 'dart:typed_data'; -import 'package:meta/meta.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:meta/meta.dart'; part 'attachment_file.freezed.dart'; - part 'attachment_file.g.dart'; /// Union class to hold various [UploadState] of a attachment. @@ -57,8 +56,12 @@ class AttachmentFile { this.size, }); - /// 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. + /// Create a new instance from a json + factory AttachmentFile.fromJson(Map 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); /// ``` @@ -67,8 +70,8 @@ class AttachmentFile { /// File name including its extension. final String name; - /// Byte data for this file. Particularly useful if you want to manipulate its data - /// or easily upload to somewhere else. + /// Byte data for this file. Particularly useful if you want to manipulate + /// its data or easily upload to somewhere else. @JsonKey(toJson: _toString, fromJson: _fromString) final Uint8List bytes; @@ -78,11 +81,6 @@ class AttachmentFile { /// File extension for this file. String get extension => name?.split('.')?.last; - /// Create a new instance from a json - factory AttachmentFile.fromJson(Map json) { - return _$AttachmentFileFromJson(json); - } - /// Serialize to json Map toJson() => _$AttachmentFileToJson(this); } diff --git a/packages/stream_chat/lib/src/models/channel_config.dart b/packages/stream_chat/lib/src/models/channel_config.dart index e455930f..3541573e 100644 --- a/packages/stream_chat/lib/src/models/channel_config.dart +++ b/packages/stream_chat/lib/src/models/channel_config.dart @@ -1,12 +1,34 @@ import 'package:json_annotation/json_annotation.dart'; - -import 'command.dart'; - +import 'package:stream_chat/src/models/command.dart'; part 'channel_config.g.dart'; /// The class that contains the information about the configuration of a channel @JsonSerializable() 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 json) => + _$ChannelConfigFromJson(json); + /// Moderation configuration final String automod; @@ -55,30 +77,6 @@ class ChannelConfig { /// True if urls appears as attachments 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 json) => - _$ChannelConfigFromJson(json); - /// Serialize to json Map toJson() => _$ChannelConfigToJson(this); } diff --git a/packages/stream_chat/lib/src/models/channel_model.dart b/packages/stream_chat/lib/src/models/channel_model.dart index 046e29c5..62ea1853 100644 --- a/packages/stream_chat/lib/src/models/channel_model.dart +++ b/packages/stream_chat/lib/src/models/channel_model.dart @@ -1,14 +1,35 @@ import 'package:json_annotation/json_annotation.dart'; - -import 'channel_config.dart'; -import 'serialization.dart'; -import 'user.dart'; +import 'package:stream_chat/src/models/channel_config.dart'; +import 'package:stream_chat/src/models/serialization.dart'; +import 'package:stream_chat/src/models/user.dart'; part 'channel_model.g.dart'; /// The class that contains the information about a channel @JsonSerializable() 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 json) => + _$ChannelModelFromJson( + Serialization.moveToExtraDataFromRoot(json, topLevelFields)); + /// The id of this channel final String id; @@ -76,40 +97,15 @@ class ChannelModel { '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 String get name => extraData?.containsKey('name') == true ? extraData['name'] : cid; - /// Create a new instance from a json - factory ChannelModel.fromJson(Map json) { - return _$ChannelModelFromJson( - Serialization.moveToExtraDataFromRoot(json, topLevelFields)); - } - /// Serialize to json - Map toJson() { - return Serialization.moveFromExtraDataToRoot( - _$ChannelModelToJson(this), - topLevelFields, - ); - } + Map toJson() => Serialization.moveFromExtraDataToRoot( + _$ChannelModelToJson(this), + topLevelFields, + ); /// Creates a copy of [ChannelModel] with specified attributes overridden. ChannelModel copyWith({ @@ -143,8 +139,8 @@ class ChannelModel { team: team ?? this.team, ); - /// Returns a new [ChannelModel] that is a combination of this channelModel and the given - /// [other] channelModel. + /// Returns a new [ChannelModel] that is a combination of this channelModel + /// and the given [other] channelModel. ChannelModel merge(ChannelModel other) { if (other == null) return this; return copyWith( diff --git a/packages/stream_chat/lib/src/models/channel_state.dart b/packages/stream_chat/lib/src/models/channel_state.dart index c2a3d48c..3a1f3178 100644 --- a/packages/stream_chat/lib/src/models/channel_state.dart +++ b/packages/stream_chat/lib/src/models/channel_state.dart @@ -1,16 +1,26 @@ import 'package:json_annotation/json_annotation.dart'; - -import '../models/read.dart'; -import '../models/user.dart'; -import 'channel_model.dart'; -import 'member.dart'; -import 'message.dart'; +import 'package:stream_chat/src/models/channel_model.dart'; +import 'package:stream_chat/src/models/member.dart'; +import 'package:stream_chat/src/models/message.dart'; +import 'package:stream_chat/src/models/read.dart'; +import 'package:stream_chat/src/models/user.dart'; part 'channel_state.g.dart'; /// The class that contains the information about a channel @JsonSerializable() 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 final ChannelModel channel; @@ -32,17 +42,6 @@ class ChannelState { /// The list of channel reads final List 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 static ChannelState fromJson(Map json) => _$ChannelStateFromJson(json); diff --git a/packages/stream_chat/lib/src/models/command.dart b/packages/stream_chat/lib/src/models/command.dart index 420f2195..a5ababd2 100644 --- a/packages/stream_chat/lib/src/models/command.dart +++ b/packages/stream_chat/lib/src/models/command.dart @@ -5,15 +5,6 @@ part 'command.g.dart'; /// The class that contains the information about a command @JsonSerializable() 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 Command({ this.name, @@ -25,6 +16,15 @@ class Command { factory Command.fromJson(Map 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 Map toJson() => _$CommandToJson(this); } diff --git a/packages/stream_chat/lib/src/models/device.dart b/packages/stream_chat/lib/src/models/device.dart index 97247222..150e6759 100644 --- a/packages/stream_chat/lib/src/models/device.dart +++ b/packages/stream_chat/lib/src/models/device.dart @@ -5,12 +5,6 @@ part 'device.g.dart'; /// The class that contains the information about a device @JsonSerializable() class Device { - /// The id of the device - final String id; - - /// The notification push provider - final String pushProvider; - /// Constructor used for json serialization Device({ this.id, @@ -20,6 +14,12 @@ class Device { /// Create a new instance from a json factory Device.fromJson(Map json) => _$DeviceFromJson(json); + /// The id of the device + final String id; + + /// The notification push provider + final String pushProvider; + /// Serialize to json Map toJson() => _$DeviceToJson(this); } diff --git a/packages/stream_chat/lib/src/models/event.dart b/packages/stream_chat/lib/src/models/event.dart index 03995d67..b1140ebc 100644 --- a/packages/stream_chat/lib/src/models/event.dart +++ b/packages/stream_chat/lib/src/models/event.dart @@ -4,17 +4,40 @@ import 'package:stream_chat/src/models/message.dart'; import 'package:stream_chat/src/models/serialization.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'; /// The class that contains the information about an event @JsonSerializable() 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 json) => + _$EventFromJson(Serialization.moveToExtraDataFromRoot( + json, + topLevelFields, + )) + ..isLocal = false; + /// The type of the event /// [EventType] contains some predefined constant types final String type; @@ -71,27 +94,6 @@ class Event { @JsonKey(includeIfNull: false) final Map 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. /// Useful for [Serialization] methods. static final topLevelFields = [ @@ -114,15 +116,6 @@ class Event { 'is_local', ]; - /// Create a new instance from a json - factory Event.fromJson(Map json) { - return _$EventFromJson(Serialization.moveToExtraDataFromRoot( - json, - topLevelFields, - )) - ..isLocal = false; - } - /// Serialize to json Map toJson() => Serialization.moveFromExtraDataToRoot( _$EventToJson(this), @@ -133,16 +126,6 @@ class Event { /// The channel embedded in the event object @JsonSerializable() class EventChannel extends ChannelModel { - /// A paginated list of channel members - final List members; - - /// Known top level fields. - /// Useful for [Serialization] methods. - static final topLevelFields = [ - 'members', - ...ChannelModel.topLevelFields, - ]; - /// Constructor used for json serialization EventChannel({ this.members, @@ -174,12 +157,21 @@ class EventChannel extends ChannelModel { ); /// Create a new instance from a json - factory EventChannel.fromJson(Map json) { - return _$EventChannelFromJson(Serialization.moveToExtraDataFromRoot( - json, - topLevelFields, - )); - } + factory EventChannel.fromJson(Map json) => + _$EventChannelFromJson(Serialization.moveToExtraDataFromRoot( + json, + topLevelFields, + )); + + /// A paginated list of channel members + final List members; + + /// Known top level fields. + /// Useful for [Serialization] methods. + static final topLevelFields = [ + 'members', + ...ChannelModel.topLevelFields, + ]; /// Serialize to json @override diff --git a/packages/stream_chat/lib/src/models/member.dart b/packages/stream_chat/lib/src/models/member.dart index 511b7a7b..e99d8d34 100644 --- a/packages/stream_chat/lib/src/models/member.dart +++ b/packages/stream_chat/lib/src/models/member.dart @@ -1,12 +1,35 @@ import 'package:json_annotation/json_annotation.dart'; - -import '../models/user.dart'; +import 'package:stream_chat/src/models/user.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() 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 json) { + final member = _$MemberFromJson(json); + return member.copyWith( + userId: member.user?.id, + ); + } + /// The interested user final User user; @@ -40,29 +63,6 @@ class Member { /// The last date of update 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 json) { - final member = _$MemberFromJson(json); - return member.copyWith( - userId: member.user?.id, - ); - } - /// Creates a copy of [Member] with specified attributes overridden. Member copyWith({ User user, diff --git a/packages/stream_chat/lib/src/models/message.dart b/packages/stream_chat/lib/src/models/message.dart index df1906f3..d402836f 100644 --- a/packages/stream_chat/lib/src/models/message.dart +++ b/packages/stream_chat/lib/src/models/message.dart @@ -1,11 +1,10 @@ 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 'attachment.dart'; -import 'reaction.dart'; -import 'serialization.dart'; -import 'user.dart'; - part 'message.g.dart'; class _PinExpires { @@ -29,9 +28,11 @@ enum MessageSendingStatus { failed, /// Message failed to updated + // ignore: constant_identifier_names failed_update, /// Message failed to delete + // ignore: constant_identifier_names failed_delete, /// Message correctly sent @@ -41,7 +42,45 @@ enum MessageSendingStatus { /// The class that contains the information about a message @JsonSerializable() 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 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; /// The text of this message @@ -55,7 +94,8 @@ class Message { @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) 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) final List attachments; @@ -188,43 +228,6 @@ class Message { '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 json) => _$MessageFromJson( - Serialization.moveToExtraDataFromRoot(json, topLevelFields)); - /// Serialize to json Map toJson() => Serialization.moveFromExtraDataToRoot( _$MessageToJson(this), topLevelFields); @@ -267,7 +270,7 @@ class Message { throw ArgumentError('`pinExpires` can only be set as DateTime or null'); } return true; - }()); + }(), 'Validate type for pinExpires'); return Message( id: id ?? this.id, text: text ?? this.text, @@ -300,8 +303,8 @@ class Message { ); } - /// Returns a new [Message] that is a combination of this message and the given - /// [other] message. + /// Returns a new [Message] that is a combination of this message and the + /// given [other] message. Message merge(Message other) { if (other == null) return this; return copyWith( @@ -344,6 +347,12 @@ class TranslatedMessage extends Message { /// Constructor used for json serialization TranslatedMessage(this.i18n); + /// Create a new instance from a json + factory TranslatedMessage.fromJson(Map json) => + _$TranslatedMessageFromJson( + Serialization.moveToExtraDataFromRoot(json, topLevelFields), + ); + /// A Map of final Map i18n; @@ -354,13 +363,6 @@ class TranslatedMessage extends Message { ...Message.topLevelFields, ]; - /// Create a new instance from a json - factory TranslatedMessage.fromJson(Map json) { - return _$TranslatedMessageFromJson( - Serialization.moveToExtraDataFromRoot(json, topLevelFields), - ); - } - /// Serialize to json @override Map toJson() => Serialization.moveFromExtraDataToRoot( diff --git a/packages/stream_chat/lib/src/models/mute.dart b/packages/stream_chat/lib/src/models/mute.dart index f33a9956..e3d5e1a0 100644 --- a/packages/stream_chat/lib/src/models/mute.dart +++ b/packages/stream_chat/lib/src/models/mute.dart @@ -1,14 +1,19 @@ import 'package:json_annotation/json_annotation.dart'; import 'package:stream_chat/src/models/channel_model.dart'; - -import 'serialization.dart'; -import 'user.dart'; +import 'package:stream_chat/src/models/serialization.dart'; +import 'package:stream_chat/src/models/user.dart'; part 'mute.g.dart'; /// The class that contains the information about a muted user @JsonSerializable() 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 json) => _$MuteFromJson(json); + /// The user that performed the muting action @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) final User user; @@ -25,12 +30,6 @@ class Mute { @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) 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 json) => _$MuteFromJson(json); - /// Serialize to json Map toJson() => _$MuteToJson(this); } diff --git a/packages/stream_chat/lib/src/models/own_user.dart b/packages/stream_chat/lib/src/models/own_user.dart index 3246788c..18ee2abf 100644 --- a/packages/stream_chat/lib/src/models/own_user.dart +++ b/packages/stream_chat/lib/src/models/own_user.dart @@ -1,9 +1,8 @@ import 'package:json_annotation/json_annotation.dart'; - -import 'device.dart'; -import 'mute.dart'; -import 'serialization.dart'; -import 'user.dart'; +import 'package:stream_chat/src/models/device.dart'; +import 'package:stream_chat/src/models/mute.dart'; +import 'package:stream_chat/src/models/serialization.dart'; +import 'package:stream_chat/src/models/user.dart'; part 'own_user.g.dart'; @@ -11,6 +10,36 @@ part 'own_user.g.dart'; /// This object can be found in [Event] @JsonSerializable() 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 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 json) => _$OwnUserFromJson( + Serialization.moveToExtraDataFromRoot(json, topLevelFields)); + /// List of user devices @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) final List devices; @@ -42,42 +71,8 @@ class OwnUser extends User { ...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 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 json) { - return _$OwnUserFromJson( - Serialization.moveToExtraDataFromRoot(json, topLevelFields)); - } - /// Serialize to json @override - Map toJson() { - return Serialization.moveFromExtraDataToRoot( - _$OwnUserToJson(this), topLevelFields); - } + Map toJson() => Serialization.moveFromExtraDataToRoot( + _$OwnUserToJson(this), topLevelFields); } diff --git a/packages/stream_chat/lib/src/models/reaction.dart b/packages/stream_chat/lib/src/models/reaction.dart index f0957761..6792bf3f 100644 --- a/packages/stream_chat/lib/src/models/reaction.dart +++ b/packages/stream_chat/lib/src/models/reaction.dart @@ -1,13 +1,27 @@ import 'package:json_annotation/json_annotation.dart'; - -import 'serialization.dart'; -import 'user.dart'; +import 'package:stream_chat/src/models/serialization.dart'; +import 'package:stream_chat/src/models/user.dart'; part 'reaction.g.dart'; /// The class that defines a reaction @JsonSerializable() 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 json) => _$ReactionFromJson( + Serialization.moveToExtraDataFromRoot(json, topLevelFields)); + /// The messageId to which the reaction belongs final String messageId; @@ -43,28 +57,9 @@ class Reaction { '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 json) { - return _$ReactionFromJson( - Serialization.moveToExtraDataFromRoot(json, topLevelFields)); - } - /// Serialize to json - Map toJson() { - return Serialization.moveFromExtraDataToRoot( - _$ReactionToJson(this), topLevelFields); - } + Map toJson() => Serialization.moveFromExtraDataToRoot( + _$ReactionToJson(this), topLevelFields); /// Creates a copy of [Reaction] with specified attributes overridden. Reaction copyWith({ @@ -75,20 +70,19 @@ class Reaction { String userId, int score, Map extraData, - }) { - return Reaction( - messageId: messageId ?? this.messageId, - createdAt: createdAt ?? this.createdAt, - type: type ?? this.type, - user: user ?? this.user, - userId: userId ?? this.userId, - score: score ?? this.score, - extraData: extraData ?? this.extraData, - ); - } + }) => + Reaction( + messageId: messageId ?? this.messageId, + createdAt: createdAt ?? this.createdAt, + type: type ?? this.type, + user: user ?? this.user, + userId: userId ?? this.userId, + score: score ?? this.score, + extraData: extraData ?? this.extraData, + ); - /// Returns a new [Reaction] that is a combination of this reaction and the given - /// [other] reaction. + /// Returns a new [Reaction] that is a combination of this reaction and the + /// given [other] reaction. Reaction merge(Reaction other) { if (other == null) return this; return copyWith( diff --git a/packages/stream_chat/lib/src/models/read.dart b/packages/stream_chat/lib/src/models/read.dart index ae85293d..2be639d5 100644 --- a/packages/stream_chat/lib/src/models/read.dart +++ b/packages/stream_chat/lib/src/models/read.dart @@ -1,21 +1,11 @@ import 'package:json_annotation/json_annotation.dart'; - -import 'user.dart'; +import 'package:stream_chat/src/models/user.dart'; part 'read.g.dart'; /// The class that defines a read event @JsonSerializable() 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 Read({ this.lastRead, @@ -26,6 +16,15 @@ class Read { /// Create a new instance from a json factory Read.fromJson(Map 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 Map toJson() => _$ReadToJson(this); } diff --git a/packages/stream_chat/lib/src/models/serialization.dart b/packages/stream_chat/lib/src/models/serialization.dart index 239ccb50..18cdd545 100644 --- a/packages/stream_chat/lib/src/models/serialization.dart +++ b/packages/stream_chat/lib/src/models/serialization.dart @@ -1,6 +1,7 @@ -import 'user.dart'; +import 'package:stream_chat/src/models/user.dart'; /// Used to avoid to serialize properties to json +// ignore: prefer_void_to_null Null readonly(_) => null; /// Helper class for serialization to and from json @@ -9,9 +10,8 @@ class Serialization { static const Function readOnly = readonly; /// List of users to list of userIds - static List userIds(List users) { - return users?.map((u) => u.id)?.toList(); - } + static List userIds(List users) => + users?.map((u) => u.id)?.toList(); /// Takes unknown json keys and puts them in the `extra_data` key static Map 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 moveFromExtraDataToRoot( Map json, List topLevelFields, diff --git a/packages/stream_chat/lib/src/models/user.dart b/packages/stream_chat/lib/src/models/user.dart index 00ed1200..618c1986 100644 --- a/packages/stream_chat/lib/src/models/user.dart +++ b/packages/stream_chat/lib/src/models/user.dart @@ -1,12 +1,53 @@ import 'package:json_annotation/json_annotation.dart'; - -import 'serialization.dart'; +import 'package:stream_chat/src/models/serialization.dart'; part 'user.g.dart'; /// The class that defines the user model @JsonSerializable() 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 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 final String id; @@ -42,43 +83,8 @@ class User { @JsonKey(includeIfNull: false) final Map extraData; - /// Known top level fields. - /// Useful for [Serialization] methods. - 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, - }); + @override + int get hashCode => id.hashCode; /// Shortcut for user name String get name => @@ -86,23 +92,12 @@ class User { ? extraData['name'] : id; - /// Create a new instance from a json - factory User.fromJson(Map json) { - return _$UserFromJson( - Serialization.moveToExtraDataFromRoot(json, topLevelFields)); - } - - /// Serialize to json - Map toJson() { - return Serialization.moveFromExtraDataToRoot( - _$UserToJson(this), topLevelFields); - } - @override bool operator ==(Object other) => identical(this, other) || other is User && runtimeType == other.runtimeType && id == other.id; - @override - int get hashCode => id.hashCode; + /// Serialize to json + Map toJson() => + Serialization.moveFromExtraDataToRoot(_$UserToJson(this), topLevelFields); } diff --git a/packages/stream_chat/lib/src/platform_detector/platform_detector.dart b/packages/stream_chat/lib/src/platform_detector/platform_detector.dart index 32748256..58d86db4 100644 --- a/packages/stream_chat/lib/src/platform_detector/platform_detector.dart +++ b/packages/stream_chat/lib/src/platform_detector/platform_detector.dart @@ -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.io) 'platform_detector_io.dart'; /// Possible platforms 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 @@ -31,42 +31,42 @@ class CurrentPlatform { CurrentPlatform._(); /// 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 - static bool get isIos => type == PlatformType.Ios; + static bool get isIos => type == PlatformType.ios; /// 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 - static bool get isMacOS => type == PlatformType.MacOS; + static bool get isMacOS => type == PlatformType.macOS; /// 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 - static bool get isLinux => type == PlatformType.Linux; + static bool get isLinux => type == PlatformType.linux; /// 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 static String get name { switch (type) { - case PlatformType.Android: + case PlatformType.android: return 'android'; - case PlatformType.Ios: + case PlatformType.ios: return 'ios'; - case PlatformType.Web: + case PlatformType.web: return 'web'; - case PlatformType.MacOS: + case PlatformType.macOS: return 'macos'; - case PlatformType.Windows: + case PlatformType.windows: return 'windows'; - case PlatformType.Linux: + case PlatformType.linux: return 'linux'; - case PlatformType.Fuchsia: + case PlatformType.fuchsia: return 'fuchsia'; default: return ''; diff --git a/packages/stream_chat/lib/src/platform_detector/platform_detector_io.dart b/packages/stream_chat/lib/src/platform_detector/platform_detector_io.dart index df74f5d8..c7b4a0b7 100644 --- a/packages/stream_chat/lib/src/platform_detector/platform_detector_io.dart +++ b/packages/stream_chat/lib/src/platform_detector/platform_detector_io.dart @@ -1,12 +1,12 @@ import 'dart:io'; -import 'platform_detector.dart'; +import 'package:stream_chat/src/platform_detector/platform_detector.dart'; /// Version running on native systems PlatformType get currentPlatform { - if (Platform.isWindows) return PlatformType.Windows; - if (Platform.isFuchsia) return PlatformType.Fuchsia; - if (Platform.isMacOS) return PlatformType.MacOS; - if (Platform.isLinux) return PlatformType.Linux; - if (Platform.isIOS) return PlatformType.Ios; - return PlatformType.Android; + if (Platform.isWindows) return PlatformType.windows; + if (Platform.isFuchsia) return PlatformType.fuchsia; + if (Platform.isMacOS) return PlatformType.macOS; + if (Platform.isLinux) return PlatformType.linux; + if (Platform.isIOS) return PlatformType.ios; + return PlatformType.android; } diff --git a/packages/stream_chat/lib/src/platform_detector/platform_detector_stub.dart b/packages/stream_chat/lib/src/platform_detector/platform_detector_stub.dart index f9143deb..b9e13c2e 100644 --- a/packages/stream_chat/lib/src/platform_detector/platform_detector_stub.dart +++ b/packages/stream_chat/lib/src/platform_detector/platform_detector_stub.dart @@ -1,4 +1,4 @@ -import 'platform_detector.dart'; +import 'package:stream_chat/src/platform_detector/platform_detector.dart'; /// Stub implementation PlatformType get currentPlatform { diff --git a/packages/stream_chat/lib/src/platform_detector/platform_detector_web.dart b/packages/stream_chat/lib/src/platform_detector/platform_detector_web.dart index 4274bf71..ba5d04fc 100644 --- a/packages/stream_chat/lib/src/platform_detector/platform_detector_web.dart +++ b/packages/stream_chat/lib/src/platform_detector/platform_detector_web.dart @@ -1,4 +1,4 @@ -import 'platform_detector.dart'; +import 'package:stream_chat/src/platform_detector/platform_detector.dart'; /// Version running on web -PlatformType get currentPlatform => PlatformType.Web; +PlatformType get currentPlatform => PlatformType.web; diff --git a/packages/stream_chat/lib/stream_chat.dart b/packages/stream_chat/lib/stream_chat.dart index f0b3e60d..05e3a4a4 100644 --- a/packages/stream_chat/lib/stream_chat.dart +++ b/packages/stream_chat/lib/stream_chat.dart @@ -12,7 +12,9 @@ export './src/api/requests.dart'; export './src/api/responses.dart'; export './src/attachment_file_uploader.dart' show AttachmentFileUploader; export './src/client.dart'; +export './src/db/chat_persistence_client.dart'; export './src/event_type.dart'; +export './src/extensions/string_extension.dart'; export './src/models/action.dart'; export './src/models/attachment.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/read.dart'; export './src/models/user.dart'; -export './src/extensions/string_extension.dart'; -export './src/db/chat_persistence_client.dart'; diff --git a/packages/stream_chat/lib/version.dart b/packages/stream_chat/lib/version.dart index 27294084..39aa0571 100644 --- a/packages/stream_chat/lib/version.dart +++ b/packages/stream_chat/lib/version.dart @@ -2,4 +2,5 @@ import 'package:stream_chat/src/client.dart'; /// Current package version /// Used in [StreamChatClient] to build the `x-stream-client` header +// ignore: constant_identifier_names const PACKAGE_VERSION = '1.3.2+1-beta'; diff --git a/packages/stream_chat/pubspec.yaml b/packages/stream_chat/pubspec.yaml index 927a997e..668f5de3 100644 --- a/packages/stream_chat/pubspec.yaml +++ b/packages/stream_chat/pubspec.yaml @@ -9,23 +9,24 @@ environment: sdk: ">=2.7.0 <3.0.0" 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 - rxdart: ^0.25.0 collection: ^1.14.13 - pedantic: ^1.9.2 - meta: ^1.2.4 - mime: ^0.9.7 + dio: ^3.0.10 freezed_annotation: ^0.12.0 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: build_runner: ^1.10.0 - json_serializable: ^3.3.0 - test: ^1.15.7 - mockito: ^4.1.1 freezed: ^0.12.7 + json_serializable: ^3.3.0 + mockito: ^4.1.1 + test: ^1.15.7 \ No newline at end of file diff --git a/packages/stream_chat/test/src/client_test.dart b/packages/stream_chat/test/src/client_test.dart index e70724a3..21b9d09b 100644 --- a/packages/stream_chat/test/src/client_test.dart +++ b/packages/stream_chat/test/src/client_test.dart @@ -188,7 +188,7 @@ void main() { } }; - final query = 'hello'; + const query = 'hello'; final queryParams = { 'payload': json.encode({ diff --git a/packages/stream_chat_flutter/lib/src/system_message.dart b/packages/stream_chat_flutter/lib/src/system_message.dart index ddb49709..ac3176ce 100644 --- a/packages/stream_chat_flutter/lib/src/system_message.dart +++ b/packages/stream_chat_flutter/lib/src/system_message.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:jiffy/jiffy.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; /// It shows a date divider depending on the date difference