From c80fc0a6b4f0a2569f9edd7f106984a8826b3466 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Thu, 8 Apr 2021 15:03:16 +0530 Subject: [PATCH] added null safety for llc --- packages/stream_chat/lib/src/api/channel.dart | 591 +++++++++--------- .../stream_chat/lib/src/api/requests.dart | 22 +- .../stream_chat/lib/src/api/responses.dart | 204 +++--- .../stream_chat/lib/src/api/retry_policy.dart | 16 +- .../stream_chat/lib/src/api/retry_queue.dart | 43 +- .../lib/src/api/web_socket_channel_html.dart | 2 +- .../lib/src/api/web_socket_channel_io.dart | 2 +- .../lib/src/api/web_socket_channel_stub.dart | 6 +- .../stream_chat/lib/src/api/websocket.dart | 80 +-- .../lib/src/attachment_file_uploader.dart | 96 +-- packages/stream_chat/lib/src/client.dart | 390 ++++++------ .../lib/src/db/chat_persistence_client.dart | 102 +-- packages/stream_chat/lib/src/exceptions.dart | 16 +- .../lib/src/extensions/map_extension.dart | 4 +- .../lib/src/extensions/rate_limit.dart | 48 +- .../lib/src/extensions/string_extension.dart | 8 +- .../stream_chat/lib/src/models/action.dart | 10 +- .../lib/src/models/attachment.dart | 96 +-- .../lib/src/models/attachment_file.dart | 14 +- .../lib/src/models/channel_config.dart | 32 +- .../lib/src/models/channel_model.dart | 62 +- .../lib/src/models/channel_state.dart | 32 +- .../stream_chat/lib/src/models/command.dart | 6 +- .../stream_chat/lib/src/models/device.dart | 4 +- .../stream_chat/lib/src/models/event.dart | 104 +-- .../stream_chat/lib/src/models/member.dart | 44 +- .../stream_chat/lib/src/models/message.dart | 129 ++-- packages/stream_chat/lib/src/models/mute.dart | 8 +- .../stream_chat/lib/src/models/own_user.dart | 30 +- .../stream_chat/lib/src/models/reaction.dart | 34 +- packages/stream_chat/lib/src/models/read.dart | 12 +- .../lib/src/models/serialization.dart | 6 +- packages/stream_chat/lib/src/models/user.dart | 46 +- packages/stream_chat/pubspec.yaml | 20 +- .../test/src/api/channel_test.dart | 34 +- .../test/src/api/websocket_test.dart | 18 +- .../test/src/models/attachment_test.dart | 2 +- .../test/src/models/channel_state_test.dart | 43 +- .../test/src/models/channel_test.dart | 4 +- .../test/src/models/reaction_test.dart | 2 +- .../test/src/models/read_test.dart | 2 +- 41 files changed, 1222 insertions(+), 1202 deletions(-) diff --git a/packages/stream_chat/lib/src/api/channel.dart b/packages/stream_chat/lib/src/api/channel.dart index aeacaf7d..3b6848f6 100644 --- a/packages/stream_chat/lib/src/api/channel.dart +++ b/packages/stream_chat/lib/src/api/channel.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:math'; +import 'package:collection/collection.dart' show IterableExtension; import 'package:dio/dio.dart'; import 'package:logging/logging.dart'; import 'package:rxdart/rxdart.dart'; @@ -27,9 +28,9 @@ class Channel { /// Create a channel client instance from a [ChannelState] object Channel.fromState(this._client, ChannelState channelState) { - _cid = channelState.channel.cid; - _id = channelState.channel.id; - type = channelState.channel.type; + _cid = channelState.channel!.cid; + _id = channelState.channel!.id; + type = channelState.channel!.type; state = ChannelClientState(this, channelState); _initializedCompleter.complete(true); @@ -37,16 +38,16 @@ class Channel { } /// This client state - ChannelClientState state; + ChannelClientState? state; /// The channel type - String type; + String? type; - String _id; - String _cid; - Map _extraData; + String? _id; + String? _cid; + Map? _extraData; - set extraData(Map extraData) { + set extraData(Map? extraData) { if (_initializedCompleter.isCompleted) { throw Exception( 'Once the channel is initialized you should use channel.update ' @@ -57,13 +58,14 @@ class Channel { /// Returns true if the channel is muted bool get isMuted => - _client.state.user?.channelMutes - ?.any((element) => element.channel.cid == cid) == + _client.state!.user?.channelMutes + ?.any((element) => element.channel!.cid == cid) == true; /// Returns true if the channel is muted as a stream - Stream get isMutedStream => _client.state.userStream?.map((event) => - event.channelMutes?.any((element) => element.channel.cid == cid) == true); + Stream? get isMutedStream => _client.state!.userStream.map((event) => + event!.channelMutes?.any((element) => element.channel!.cid == cid) == + true); /// True if the channel is a group bool get isGroup => memberCount != 2; @@ -72,85 +74,85 @@ class Channel { bool get isDistinct => id?.startsWith('!members') == true; /// Channel configuration - ChannelConfig get config => state?._channelState?.channel?.config; + ChannelConfig? get config => state?._channelState?.channel?.config; /// Channel configuration as a stream - Stream get configStream => - state?.channelStateStream?.map((cs) => cs.channel?.config); + Stream? get configStream => + state?.channelStateStream?.map((cs) => cs!.channel?.config); /// Channel user creator - User get createdBy => state?._channelState?.channel?.createdBy; + User? get createdBy => state?._channelState?.channel?.createdBy; /// Channel user creator as a stream - Stream get createdByStream => - state?.channelStateStream?.map((cs) => cs.channel?.createdBy); + Stream? get createdByStream => + state?.channelStateStream?.map((cs) => cs!.channel?.createdBy); /// Channel frozen status - bool get frozen => state?._channelState?.channel?.frozen; + bool? get frozen => state?._channelState?.channel?.frozen; /// Channel frozen status as a stream - Stream get frozenStream => - state?.channelStateStream?.map((cs) => cs.channel?.frozen); + Stream? get frozenStream => + state?.channelStateStream?.map((cs) => cs!.channel?.frozen); /// Channel creation date - DateTime get createdAt => state?._channelState?.channel?.createdAt; + DateTime? get createdAt => state?._channelState?.channel?.createdAt; /// Channel creation date as a stream - Stream get createdAtStream => - state?.channelStateStream?.map((cs) => cs.channel?.createdAt); + Stream? get createdAtStream => + state?.channelStateStream?.map((cs) => cs!.channel?.createdAt); /// Channel last message date - DateTime get lastMessageAt => state?._channelState?.channel?.lastMessageAt; + DateTime? get lastMessageAt => state?._channelState?.channel?.lastMessageAt; /// Channel last message date as a stream - Stream get lastMessageAtStream => - state?.channelStateStream?.map((cs) => cs.channel?.lastMessageAt); + Stream? get lastMessageAtStream => + state?.channelStateStream?.map((cs) => cs!.channel?.lastMessageAt); /// Channel updated date - DateTime get updatedAt => state?._channelState?.channel?.updatedAt; + DateTime? get updatedAt => state?._channelState?.channel?.updatedAt; /// Channel updated date as a stream - Stream get updatedAtStream => - state?.channelStateStream?.map((cs) => cs.channel?.updatedAt); + Stream? get updatedAtStream => + state?.channelStateStream?.map((cs) => cs!.channel?.updatedAt); /// Channel deletion date - DateTime get deletedAt => state?._channelState?.channel?.deletedAt; + DateTime? get deletedAt => state?._channelState?.channel?.deletedAt; /// Channel deletion date as a stream - Stream get deletedAtStream => - state?.channelStateStream?.map((cs) => cs.channel?.deletedAt); + Stream? get deletedAtStream => + state?.channelStateStream?.map((cs) => cs!.channel?.deletedAt); /// Channel member count - int get memberCount => state?._channelState?.channel?.memberCount; + int? get memberCount => state?._channelState?.channel?.memberCount; /// Channel member count as a stream - Stream get memberCountStream => - state?.channelStateStream?.map((cs) => cs.channel?.memberCount); + Stream? get memberCountStream => + state?.channelStateStream?.map((cs) => cs!.channel?.memberCount); /// Channel id - String get id => state?._channelState?.channel?.id ?? _id; + String? get id => state?._channelState?.channel?.id ?? _id; /// Channel id as a stream - Stream get idStream => - state?.channelStateStream?.map((cs) => cs.channel?.id ?? _id); + Stream? get idStream => + state?.channelStateStream?.map((cs) => cs!.channel?.id ?? _id); /// Channel cid - String get cid => state?._channelState?.channel?.cid ?? _cid; + String? get cid => state?._channelState?.channel?.cid ?? _cid; /// Channel team - String get team => state?._channelState?.channel?.team; + String? get team => state?._channelState?.channel?.team; /// Channel cid as a stream - Stream get cidStream => - state?.channelStateStream?.map((cs) => cs.channel?.cid ?? _cid); + Stream? get cidStream => + state?.channelStateStream?.map((cs) => cs!.channel?.cid ?? _cid); /// Channel extra data - Map get extraData => + Map? get extraData => state?._channelState?.channel?.extraData ?? _extraData; /// Channel extra data as a stream - Stream> get extraDataStream => - state?.channelStateStream?.map((cs) => cs.channel?.extraData); + Stream?>? get extraDataStream => + state?.channelStateStream?.map((cs) => cs!.channel?.extraData); /// The main Stream chat client StreamChatClient get client => _client; @@ -174,7 +176,7 @@ class Channel { /// Optionally, provide a [reason] for the cancellation. void cancelAttachmentUpload( String attachmentId, { - String reason, + String? reason, }) { final cancelToken = _cancelableAttachmentUploadRequest[attachmentId]; if (cancelToken == null) { @@ -195,23 +197,22 @@ class Channel { String messageId, Iterable attachmentIds, ) { - final message = state.messages.firstWhere( + final message = state!.messages!.firstWhereOrNull( (it) => it.id == messageId, - orElse: () => null, ); if (message == null) { throw Exception('Error, Message not found'); } - final attachments = message.attachments.where((it) { + final attachments = message.attachments!.where((it) { if (it.uploadState.isSuccess) return false; return attachmentIds.contains(it.id); }); if (attachments.isEmpty) { client.logger.info('No attachments available to upload'); - if (message.attachments.every((it) => it.uploadState.isSuccess)) { + if (message.attachments!.every((it) => it.uploadState.isSuccess)) { _messageAttachmentsUploadCompleter.remove(messageId)?.complete(message); } return Future.value(); @@ -221,9 +222,9 @@ class Channel { void updateAttachment(Attachment attachment) { final index = - message.attachments.indexWhere((it) => it.id == attachment.id); + message.attachments!.indexWhere((it) => it.id == attachment.id); if (index != -1) { - message.attachments[index] = attachment; + message.attachments![index] = attachment; state?.addMessage(message); } } @@ -251,13 +252,13 @@ class Channel { it.file, onSendProgress: onSendProgress, cancelToken: cancelToken, - ).then((it) => it.file); + ).then((it) => it!.file!); } else { future = sendFile( it.file, onSendProgress: onSendProgress, cancelToken: cancelToken, - ).then((it) => it.file); + ).then((it) => it!.file!); } _cancelableAttachmentUploadRequest[it.id] = cancelToken; return future.then((url) { @@ -287,7 +288,7 @@ class Channel { _cancelableAttachmentUploadRequest.remove(it.id); }); })).whenComplete(() { - if (message.attachments.every((it) => it.uploadState.isSuccess)) { + if (message.attachments!.every((it) => it.uploadState.isSuccess)) { _messageAttachmentsUploadCompleter.remove(messageId)?.complete(message); } }); @@ -303,14 +304,13 @@ class Channel { .remove(message.id) ?.completeError('Message Cancelled'); - final quotedMessage = state?.messages?.firstWhere( + final quotedMessage = state?.messages?.firstWhereOrNull( (m) => m.id == message?.quotedMessageId, - orElse: () => null, ); // ignore: parameter_assignments message = message.copyWith( createdAt: message.createdAt ?? DateTime.now(), - user: _client.state.user, + user: _client.state!.user, quotedMessage: quotedMessage, status: MessageSendingStatus.sending, attachments: message.attachments?.map( @@ -323,10 +323,10 @@ class Channel { if (message.parentId != null && message.id == null) { final parentMessage = - state.messages.firstWhere((m) => m.id == message.parentId); + state!.messages!.firstWhere((m) => m.id == message.parentId); state?.addMessage(parentMessage.copyWith( - replyCount: parentMessage.replyCount + 1, + replyCount: parentMessage.replyCount! + 1, )); } @@ -341,15 +341,16 @@ class Channel { // ignore: unawaited_futures _uploadAttachments( message.id, - message.attachments.map((it) => it.id), + message.attachments!.map((it) => it.id), ); // ignore: parameter_assignments message = await attachmentsUploadCompleter.future; } - final response = await _client.sendMessage(message, id, type); - state?.addMessage(response.message); + final response = await (_client.sendMessage(message, id, type) + as FutureOr); + state?.addMessage(response.message!); return response; } catch (error) { if (error is DioError && error.type != DioErrorType.response) { @@ -362,7 +363,7 @@ class Channel { /// Updates the [message] in this channel. /// Waits for a [_messageAttachmentsUploadCompleter] to complete /// before actually updating the message. - Future updateMessage(Message message) async { + Future updateMessage(Message message) async { // Cancelling previous completer in case it's called again in the process // Eg. Updating the message while the previous call is in progress. _messageAttachmentsUploadCompleter @@ -392,7 +393,7 @@ class Channel { // ignore: unawaited_futures _uploadAttachments( message.id, - message.attachments.map((it) => it.id), + message.attachments!.map((it) => it.id), ); // ignore: parameter_assignments @@ -400,9 +401,15 @@ class Channel { } final response = await _client.updateMessage(message); - state?.addMessage(response?.message?.copyWith( + + final m = response?.message?.copyWith( ownReactions: message.ownReactions, - )); + ); + + if (m != null) { + state?.addMessage(m); + } + return response; } catch (error) { if (error is DioError && error.type != DioErrorType.response) { @@ -413,11 +420,11 @@ class Channel { } /// Deletes the [message] from the channel. - Future deleteMessage(Message message) async { + Future deleteMessage(Message message) async { // Directly deleting the local messages which are not yet sent to server if (message.status == MessageSendingStatus.sending || message.status == MessageSendingStatus.failed) { - state.addMessage(message.copyWith( + state!.addMessage(message.copyWith( type: 'deleted', status: MessageSendingStatus.sent, )); @@ -454,7 +461,7 @@ class Channel { } /// Pins provided message - Future pinMessage( + Future pinMessage( Message message, Object timeoutOrExpirationDate, ) { @@ -467,7 +474,7 @@ class Channel { return true; }(), 'Check for invalid token or expiration date'); - DateTime pinExpires; + DateTime? pinExpires; if (timeoutOrExpirationDate is DateTime) { pinExpires = timeoutOrExpirationDate; } else if (timeoutOrExpirationDate is num) { @@ -484,14 +491,14 @@ class Channel { } /// Unpins provided message - Future unpinMessage(Message message) => + Future unpinMessage(Message message) => updateMessage(message.copyWith(pinned: false)); /// Send a file to this channel - Future sendFile( - AttachmentFile file, { - ProgressCallback onSendProgress, - CancelToken cancelToken, + Future sendFile( + AttachmentFile? file, { + ProgressCallback? onSendProgress, + CancelToken? cancelToken, }) => _client.sendFile( file, @@ -502,10 +509,10 @@ class Channel { ); /// Send an image to this channel - Future sendImage( - AttachmentFile file, { - ProgressCallback onSendProgress, - CancelToken cancelToken, + Future sendImage( + AttachmentFile? file, { + ProgressCallback? onSendProgress, + CancelToken? cancelToken, }) => _client.sendImage( file, @@ -516,11 +523,11 @@ class Channel { ); /// A message search. - Future search({ - String query, - Map messageFilters, - List sort, - PaginationParams paginationParams, + Future search({ + String? query, + Map? messageFilters, + List? sort, + PaginationParams? paginationParams, }) => _client.search( { @@ -535,16 +542,16 @@ class Channel { ); /// Delete a file from this channel - Future deleteFile( + Future deleteFile( String url, { - CancelToken cancelToken, + CancelToken? cancelToken, }) => _client.deleteFile(url, id, type, cancelToken: cancelToken); /// Delete an image from this channel - Future deleteImage( + Future deleteImage( String url, { - CancelToken cancelToken, + CancelToken? cancelToken, }) => _client.deleteImage(url, id, type, cancelToken: cancelToken); @@ -554,12 +561,12 @@ class Channel { return _client.post( '$_channelURL/event', data: {'event': event.toJson()}, - ).then((res) => _client.decode(res.data, EmptyResponse.fromJson)); + ).then((res) => _client.decode(res.data, EmptyResponse.fromJson)!); } /// Send a reaction to this channel /// Set [enforceUnique] to true to remove the existing user reaction - Future sendReaction( + Future sendReaction( Message message, String type, { Map extraData = const {}, @@ -567,11 +574,11 @@ class Channel { }) async { final messageId = message.id; final now = DateTime.now(); - final user = _client.state.user; + final user = _client.state!.user; final latestReactions = [...message.latestReactions ?? []]; if (enforceUnique) { - latestReactions.removeWhere((it) => it.userId == user.id); + latestReactions.removeWhere((it) => it.userId == user!.id); } final newReaction = Reaction( @@ -586,7 +593,7 @@ class Channel { // Inserting at the 0th index as it's the latest reaction latestReactions.insert(0, newReaction); final ownReactions = [...latestReactions] - ..removeWhere((it) => it.userId != user.id); + ..removeWhere((it) => it.userId != user!.id); final newMessage = message.copyWith( reactionCounts: {...message?.reactionCounts ?? {}} @@ -629,10 +636,10 @@ class Channel { } /// Delete a reaction from this channel - Future deleteReaction( + Future deleteReaction( Message message, Reaction reaction) async { final type = reaction.type; - final user = _client.state.user; + final user = _client.state!.user; final reactionCounts = {...message.reactionCounts ?? {}}; if (reactionCounts.containsKey(type)) { @@ -650,7 +657,7 @@ class Channel { r.messageId == reaction.messageId); final ownReactions = [...latestReactions ?? []] - ..removeWhere((it) => it.userId != user.id); + ..removeWhere((it) => it.userId != user!.id); final newMessage = message.copyWith( reactionCounts: reactionCounts..removeWhere((_, value) => value == 0), @@ -673,9 +680,9 @@ class Channel { } /// Edit the channel custom data - Future update( + Future update( Map channelData, [ - Message updateMessage, + Message? updateMessage, ]) async { final response = await _client.post(_channelURL, data: { if (updateMessage != null) @@ -686,42 +693,42 @@ class Channel { } /// Edit the channel custom data - Future updatePartial( + Future updatePartial( Map channelData) async { final response = await _client.patch(_channelURL, data: channelData); return _client.decode(response.data, PartialUpdateChannelResponse.fromJson); } /// Delete this channel. Messages are permanently removed. - Future delete() async { + Future delete() async { final response = await _client.delete(_channelURL); return _client.decode(response.data, EmptyResponse.fromJson); } /// Removes all messages from the channel - Future truncate() async { + Future truncate() async { final response = await _client.post('$_channelURL/truncate'); return _client.decode(response.data, EmptyResponse.fromJson); } /// Accept invitation to the channel - Future acceptInvite([Message message]) async { + Future acceptInvite([Message? message]) async { final res = await _client.post(_channelURL, data: {'accept_invite': true, 'message': message?.toJson()}); return _client.decode(res.data, AcceptInviteResponse.fromJson); } /// Reject invitation to the channel - Future rejectInvite([Message message]) async { + Future rejectInvite([Message? message]) async { final res = await _client.post(_channelURL, data: {'reject_invite': true, 'message': message?.toJson()}); return _client.decode(res.data, RejectInviteResponse.fromJson); } /// Add members to the channel - Future addMembers( + Future addMembers( List memberIds, [ - Message message, + Message? message, ]) async { final res = await _client.post(_channelURL, data: { 'add_members': memberIds, @@ -731,9 +738,9 @@ class Channel { } /// Invite members to the channel - Future inviteMembers( + Future inviteMembers( List memberIds, [ - Message message, + Message? message, ]) async { final res = await _client.post(_channelURL, data: { 'invites': memberIds, @@ -743,9 +750,9 @@ class Channel { } /// Remove members from the channel - Future removeMembers( + Future removeMembers( List memberIds, [ - Message message, + Message? message, ]) async { final res = await _client.post(_channelURL, data: { 'remove_members': memberIds, @@ -769,34 +776,33 @@ class Channel { 'message_id': messageId, }); - final res = _client.decode(response.data, SendActionResponse.fromJson); + final res = _client.decode(response.data, SendActionResponse.fromJson)!; if (res.message != null) { - state.addMessage(res.message); + state!.addMessage(res.message!); } else { - final oldIndex = state.messages?.indexWhere((m) => m.id == messageId); + final oldIndex = state!.messages?.indexWhere((m) => m.id == messageId); - Message oldMessage; + Message? oldMessage; if (oldIndex != null && oldIndex != -1) { - oldMessage = state.messages[oldIndex]; - state.updateChannelState(state._channelState.copyWith( - messages: state.messages..remove(oldMessage), + oldMessage = state!.messages![oldIndex]; + state!.updateChannelState(state!._channelState!.copyWith( + messages: state?.messages?..remove(oldMessage), )); } else { - oldMessage = state.threads.values + oldMessage = state!.threads!.values .expand((messages) => messages) - .firstWhere((m) => m.id == messageId, orElse: () => null); + .firstWhereOrNull((m) => m.id == messageId); if (oldMessage?.parentId != null) { - final parentMessage = state.messages.firstWhere( - (element) => element.id == oldMessage.parentId, - orElse: () => null, + final parentMessage = state!.messages!.firstWhereOrNull( + (element) => element.id == oldMessage!.parentId, ); if (parentMessage != null) { - state.addMessage(parentMessage.copyWith( - replyCount: parentMessage.replyCount - 1)); + state!.addMessage(parentMessage.copyWith( + replyCount: parentMessage.replyCount! - 1)); } - state.updateThreadInfo(oldMessage.parentId, - state.threads[oldMessage.parentId]..remove(oldMessage)); + state!.updateThreadInfo(oldMessage!.parentId, + state!.threads![oldMessage.parentId!]!..remove(oldMessage)); } } @@ -807,11 +813,11 @@ class Channel { } /// Mark all channel messages as read - Future markRead() async { + Future markRead() async { _checkInitialized(); - client.state.totalUnreadCount = - max(0, (client.state.totalUnreadCount ?? 0) - (state.unreadCount ?? 0)); - state._unreadCountController.add(0); + client.state!.totalUnreadCount = max( + 0, (client.state!.totalUnreadCount ?? 0) - (state!.unreadCount ?? 0)); + state!._unreadCountController.add(0); final response = await _client.post('$_channelURL/read', data: {}); return _client.decode(response.data, EmptyResponse.fromJson); } @@ -828,7 +834,7 @@ class Channel { ChannelState response; try { - response = await query(options: watchOptions); + response = await query(options: watchOptions)!; } catch (error, stackTrace) { if (!_initializedCompleter.isCompleted) { _initializedCompleter.completeError(error, stackTrace); @@ -845,14 +851,14 @@ class Channel { void _initState(ChannelState channelState) { state = ChannelClientState(this, channelState); - client.state.channels[cid] = this; + client.state!.channels![cid] = this; if (!_initializedCompleter.isCompleted) { _initializedCompleter.complete(true); } } /// Stop watching the channel - Future stopWatching() async { + Future stopWatching() async { final response = await _client.post( '$_channelURL/stop-watching', data: {}, @@ -868,10 +874,10 @@ class Channel { PaginationParams options, { bool preferOffline = false, }) async { - final cachedReplies = await _client.chatPersistenceClient?.getReplies( + final cachedReplies = (await _client.chatPersistenceClient?.getReplies( parentId, options: options, - ); + ))!; if (cachedReplies != null && cachedReplies.isNotEmpty) { state?.updateThreadInfo(parentId, cachedReplies); if (preferOffline) { @@ -885,7 +891,7 @@ class Channel { final repliesResponse = _client.decode( response.data, QueryRepliesResponse.fromJson, - ); + )!; state?.updateThreadInfo(parentId, repliesResponse.messages); @@ -893,7 +899,7 @@ class Channel { } /// List the reactions for a message in the channel - Future getReactions( + Future getReactions( String messageID, PaginationParams options, ) async { @@ -916,7 +922,7 @@ class Channel { final res = _client.decode( response.data, GetMessagesByIdResponse.fromJson, - ); + )!; state?.updateChannelState(ChannelState(messages: res.messages)); @@ -924,7 +930,7 @@ class Channel { } /// Retrieves a list of messages by ID - Future translateMessage( + Future translateMessage( String messageId, String language, ) async { @@ -941,20 +947,20 @@ class Channel { } /// Creates a new channel - Future create() async => query(options: { + 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 - Future query({ + Future? query({ Map options = const {}, - PaginationParams messagesPagination, - PaginationParams membersPagination, - PaginationParams watchersPagination, + PaginationParams? messagesPagination, + PaginationParams? membersPagination, + PaginationParams? watchersPagination, bool preferOffline = false, }) async { var path = '/channels/$type'; @@ -984,11 +990,11 @@ class Channel { if (preferOffline && cid != null) { final updatedState = - await _client.chatPersistenceClient?.getChannelStateByCid( + (await _client.chatPersistenceClient?.getChannelStateByCid( cid, messagePagination: messagesPagination, - ); - if (updatedState != null && updatedState.messages.isNotEmpty) { + ))!; + if (updatedState != null && updatedState.messages!.isNotEmpty) { if (state == null) { _initState(updatedState); } else { @@ -1000,11 +1006,12 @@ class Channel { try { final response = await _client.post(path, data: payload); - final updatedState = _client.decode(response.data, ChannelState.fromJson); + final updatedState = + _client.decode(response.data, ChannelState.fromJson)!; if (_id == null) { - _id = updatedState.channel.id; - _cid = updatedState.channel.cid; + _id = updatedState.channel!.id; + _cid = updatedState.channel!.cid; } state?.updateChannelState(updatedState); @@ -1013,7 +1020,7 @@ class Channel { if (!_client.persistenceEnabled) { rethrow; } - return _client.chatPersistenceClient?.getChannelStateByCid( + return _client.chatPersistenceClient!.getChannelStateByCid( cid, messagePagination: messagesPagination, ); @@ -1021,10 +1028,10 @@ class Channel { } /// Query channel members - Future queryMembers({ - Map filter, - List sort, - PaginationParams pagination, + Future queryMembers({ + Map? filter, + List? sort, + PaginationParams? pagination, }) async { final payload = { 'sort': sort, @@ -1039,7 +1046,7 @@ class Channel { if (id != null) { payload['id'] = id; } else if (state?.members?.isNotEmpty == true) { - payload['members'] = state.members; + payload['members'] = state!.members; } final rawRes = await _client.get('/members', queryParameters: { @@ -1050,7 +1057,7 @@ class Channel { } /// Mutes the channel - Future mute({Duration expiration}) async { + Future mute({Duration? expiration}) async { final response = await _client.post('/moderation/mute/channel', data: { 'channel_cid': cid, if (expiration != null) 'expiration': expiration.inMilliseconds, @@ -1059,7 +1066,7 @@ class Channel { } /// Unmutes the channel - Future unmute() async { + Future unmute() async { final response = await _client.post('/moderation/unmute/channel', data: { 'channel_cid': cid, }); @@ -1067,7 +1074,7 @@ class Channel { } /// Bans a user from the channel - Future banUser( + Future banUser( String userID, Map options, ) async { @@ -1081,7 +1088,7 @@ class Channel { } /// Remove the ban for a user in the channel - Future unbanUser(String userID) async { + Future unbanUser(String userID) async { _checkInitialized(); return _client.unbanUser(userID, { 'type': type, @@ -1090,7 +1097,7 @@ class Channel { } /// Shadow bans a user from the channel - Future shadowBan( + Future shadowBan( String userID, Map options, ) async { @@ -1104,7 +1111,7 @@ class Channel { } /// Remove the shadow ban for a user in the channel - Future removeShadowBan(String userID) async { + Future removeShadowBan(String userID) async { _checkInitialized(); return _client.removeShadowBan(userID, { 'type': type, @@ -1115,13 +1122,13 @@ 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 - Future hide({bool clearHistory = false}) async { + Future hide({bool clearHistory = false}) async { _checkInitialized(); final response = await _client .post('$_channelURL/hide', data: {'clear_history': clearHistory}); if (clearHistory == true) { - state.truncate(); + state!.truncate(); await _client.chatPersistenceClient?.deleteMessageByCid(_cid); } @@ -1129,7 +1136,7 @@ class Channel { } /// Removes the hidden status for the channel - Future show() async { + Future show() async { _checkInitialized(); final response = await _client.post('$_channelURL/show'); return _client.decode(response.data, EmptyResponse.fromJson); @@ -1139,10 +1146,10 @@ class Channel { /// 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, + String? eventType, + String? eventType2, + String? eventType3, + String? eventType4, ]) => _client .on( @@ -1153,11 +1160,11 @@ class Channel { ) .where((e) => e.cid == cid); - DateTime _lastTypingEvent; + DateTime? _lastTypingEvent; /// First of the [EventType.typingStart] and [EventType.typingStop] events /// based on the users keystrokes. Call this on every keystroke. - Future keyStroke([String parentId]) async { + Future keyStroke([String? parentId]) async { if (config?.typingEvents == false) { return; } @@ -1166,7 +1173,7 @@ class Channel { final now = DateTime.now(); if (_lastTypingEvent == null || - now.difference(_lastTypingEvent).inSeconds >= 2) { + now.difference(_lastTypingEvent!).inSeconds >= 2) { _lastTypingEvent = now; await sendEvent(Event( type: EventType.typingStart, @@ -1176,7 +1183,7 @@ class Channel { } /// Sets last typing to null and sends the typing.stop event - Future stopTyping([String parentId]) async { + Future stopTyping([String? parentId]) async { if (config?.typingEvents == false) { return; } @@ -1191,7 +1198,7 @@ class Channel { /// Call this method to dispose the channel client void dispose() { - state.dispose(); + state!.dispose(); } void _checkInitialized() { @@ -1270,9 +1277,8 @@ class ChannelClientState { final _subscriptions = []; void _computeInitialUnread() { - final userRead = channelState?.read?.firstWhere( - (r) => r.user.id == _channel._client.state?.user?.id, - orElse: () => null, + final userRead = channelState?.read?.firstWhereOrNull( + (r) => r.user!.id == _channel._client.state?.user?.id, ); if (userRead != null) { _unreadCountController.add(userRead.unreadMessages ?? 0); @@ -1295,14 +1301,14 @@ class ChannelClientState { return false; } final expiration = - DateTime.parse(uri.queryParameters['Expires']); + DateTime.parse(uri.queryParameters['Expires']!); return expiration.isBefore(DateTime.now()); }) == true) ?.map((e) => e.id) ?.toList(); if (expiredAttachmentMessagesId?.isNotEmpty == true) { - _channel.getMessagesById(expiredAttachmentMessagesId); + _channel.getMessagesById(expiredAttachmentMessagesId!); _updatedMessagesIds.addAll(expiredAttachmentMessagesId); } } @@ -1310,9 +1316,9 @@ class ChannelClientState { void _listenMemberAdded() { _subscriptions.add(_channel.on(EventType.memberAdded).listen((Event e) { final member = e.member; - updateChannelState(channelState.copyWith( + updateChannelState(channelState!.copyWith( members: [ - ...channelState.members, + ...channelState!.members!, member, ], )); @@ -1322,17 +1328,17 @@ class ChannelClientState { void _listenMemberRemoved() { _subscriptions.add(_channel.on(EventType.memberRemoved).listen((Event e) { final user = e.user; - updateChannelState(channelState.copyWith( + updateChannelState(channelState!.copyWith( members: List.from( - channelState.members..removeWhere((m) => m.userId == user.id)), + channelState!.members!..removeWhere((m) => m!.userId == user!.id)), )); })); } void _listenChannelUpdated() { _subscriptions.add(_channel.on(EventType.channelUpdated).listen((Event e) { - final channel = e.channel; - updateChannelState(channelState.copyWith( + final channel = e.channel!; + updateChannelState(channelState!.copyWith( channel: channel, members: channel.members, )); @@ -1343,7 +1349,7 @@ class ChannelClientState { _subscriptions.add(_channel .on(EventType.channelTruncated, EventType.notificationChannelTruncated) .listen((event) async { - final channel = event.channel; + final channel = event.channel!; await _channel._client.chatPersistenceClient ?.deleteMessageByCid(channel.cid); truncate(); @@ -1354,28 +1360,28 @@ class ChannelClientState { /// 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. - bool get isUpToDate => _isUpToDateController.value; + bool? get isUpToDate => _isUpToDateController.value; - set isUpToDate(bool isUpToDate) => _isUpToDateController.add(isUpToDate); + set isUpToDate(bool? isUpToDate) => _isUpToDateController.add(isUpToDate); /// [isUpToDate] flag count as a stream - Stream get isUpToDateStream => _isUpToDateController.stream; + Stream get isUpToDateStream => _isUpToDateController.stream; - final BehaviorSubject _isUpToDateController = + final BehaviorSubject _isUpToDateController = BehaviorSubject.seeded(true); /// The retry queue associated to this channel - RetryQueue retryQueue; + RetryQueue? retryQueue; /// Retry failed message Future retryFailedMessages() async { final failedMessages = - [...messages, ...threads.values.expand((v) => v)] + [...messages!, ...threads!.values.expand((v) => v)] .where( (message) => message.status != null && message.status != MessageSendingStatus.sent && - message.createdAt.isBefore( + message.createdAt!.isBefore( DateTime.now().subtract( const Duration( seconds: 1, @@ -1385,14 +1391,14 @@ class ChannelClientState { ) .toList(); - retryQueue.add(failedMessages); + retryQueue!.add(failedMessages); } void _listenReactionDeleted() { _subscriptions.add(_channel.on(EventType.reactionDeleted).listen((event) { - final userId = _channel.client.state.user.id; - final message = event.message.copyWith( - ownReactions: [...event.message.latestReactions] + final userId = _channel.client.state!.user!.id; + final message = event.message!.copyWith( + ownReactions: [...event.message!.latestReactions!] ..removeWhere((it) => it.userId != userId), ); addMessage(message); @@ -1401,9 +1407,9 @@ class ChannelClientState { void _listenReactions() { _subscriptions.add(_channel.on(EventType.reactionNew).listen((event) { - final userId = _channel.client.state.user.id; - final message = event.message.copyWith( - ownReactions: [...event.message.latestReactions] + final userId = _channel.client.state!.user!.id; + final message = event.message!.copyWith( + ownReactions: [...event.message!.latestReactions!] ..removeWhere((it) => it.userId != userId), ); addMessage(message); @@ -1417,17 +1423,17 @@ class ChannelClientState { EventType.reactionUpdated, ) .listen((event) { - final userId = _channel.client.state.user.id; - final message = event.message.copyWith( - ownReactions: [...event.message.latestReactions] + final userId = _channel.client.state!.user!.id; + final message = event.message!.copyWith( + ownReactions: [...event.message!.latestReactions!] ..removeWhere((it) => it.userId != userId), ); addMessage(message); if (message.pinned == true) { - _channelState = _channelState.copyWith( + _channelState = _channelState!.copyWith( pinnedMessages: [ - ..._channelState.pinnedMessages ?? [], + ..._channelState!.pinnedMessages ?? [], message, ], ); @@ -1437,7 +1443,7 @@ class ChannelClientState { void _listenMessageDeleted() { _subscriptions.add(_channel.on(EventType.messageDeleted).listen((event) { - final message = event.message; + final message = event.message!; addMessage(message); })); } @@ -1449,14 +1455,14 @@ class ChannelClientState { EventType.notificationMessageNew, ) .listen((event) { - final message = event.message; - if (isUpToDate || + final message = event.message!; + if (isUpToDate! || (message.parentId != null && message.showInChannel != true)) { addMessage(message); } if (_countMessageAsUnread(message)) { - _unreadCountController.add(_unreadCountController.value + 1); + _unreadCountController.add(_unreadCountController.value! + 1); } })); } @@ -1464,10 +1470,10 @@ class ChannelClientState { /// Add a message to this channel void addMessage(Message message) { if (message.parentId == null || message.showInChannel == true) { - final newMessages = List.from(_channelState.messages); + final newMessages = List.from(_channelState!.messages!); final oldIndex = newMessages.indexWhere((m) => m.id == message.id); if (oldIndex != -1) { - Message m; + Message? m; if (message.quotedMessageId != null && message.quotedMessage == null) { final oldMessage = newMessages[oldIndex]; m = message.copyWith( @@ -1479,9 +1485,9 @@ class ChannelClientState { newMessages.add(message); } - _channelState = _channelState.copyWith( + _channelState = _channelState!.copyWith( messages: newMessages, - channel: _channelState.channel.copyWith( + channel: _channelState!.channel!.copyWith( lastMessageAt: message.createdAt, ), ); @@ -1507,18 +1513,18 @@ class ChannelClientState { (event) { final readList = List.from(_channelState?.read ?? []); final userReadIndex = - read?.indexWhere((r) => r.user.id == event.user.id); + 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) { + 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); + _channelState = _channelState!.copyWith(read: readList); } }, ), @@ -1526,67 +1532,68 @@ class ChannelClientState { } /// Channel message list - List get messages => _channelState.messages; + List? get messages => _channelState!.messages; /// Channel message list as a stream - Stream> get messagesStream => - channelStateStream.map((cs) => cs.messages); + Stream?> get messagesStream => + channelStateStream.map((cs) => cs!.messages); /// Channel pinned message list - List get pinnedMessages => _channelState.pinnedMessages?.toList(); + List? get pinnedMessages => _channelState!.pinnedMessages?.toList(); /// Channel pinned message list as a stream - Stream> get pinnedMessagesStream => - channelStateStream.map((cs) => cs.pinnedMessages?.toList()); + Stream?> get pinnedMessagesStream => + channelStateStream.map((cs) => cs!.pinnedMessages?.toList()); /// Get channel last message - Message get lastMessage => _channelState.messages?.isNotEmpty == true - ? _channelState.messages.last + Message? get lastMessage => _channelState!.messages?.isNotEmpty == true + ? _channelState!.messages!.last : null; /// Get channel last message - Stream get lastMessageStream => messagesStream - .map((event) => event?.isNotEmpty == true ? event.last : null); + Stream get lastMessageStream => messagesStream + .map((event) => event?.isNotEmpty == true ? event!.last : null); /// Channel members list - List get members => _channelState.members - .map((e) => e.copyWith(user: _channel.client.state.users[e.user.id])) + List get members => _channelState!.members! + .map((e) => e!.copyWith(user: _channel.client.state!.users![e.user!.id!])) .toList(); /// Channel members list as a stream Stream> get membersStream => CombineLatestStream.combine2< - List, Map, List>( - channelStateStream.map((cs) => cs.members), - _channel.client.state.usersStream, + List?, Map, List>( + channelStateStream.map((cs) => cs!.members), + _channel.client.state!.usersStream, (members, users) => - members.map((e) => e.copyWith(user: users[e.user.id])).toList(), + members!.map((e) => e!.copyWith(user: users[e.user!.id])).toList(), ); /// Channel watcher count - int get watcherCount => _channelState.watcherCount; + int? get watcherCount => _channelState!.watcherCount; /// Channel watcher count as a stream - Stream get watcherCountStream => - channelStateStream.map((cs) => cs.watcherCount); + Stream get watcherCountStream => + channelStateStream.map((cs) => cs!.watcherCount); /// Channel watchers list - List get watchers => _channelState.watchers - .map((e) => _channel.client.state.users[e.id] ?? e) + List get watchers => _channelState!.watchers! + .map((e) => _channel.client.state!.users![e.id!] ?? e) .toList(); /// Channel watchers list as a stream - Stream> get watchersStream => - CombineLatestStream.combine2, Map, List>( - channelStateStream.map((cs) => cs.watchers), - _channel.client.state.usersStream, - (watchers, users) => watchers.map((e) => users[e.id] ?? e).toList(), + Stream> get watchersStream => CombineLatestStream.combine2< + List?, Map, List>( + channelStateStream.map((cs) => cs!.watchers), + _channel.client.state!.usersStream, + (watchers, users) => watchers!.map((e) => users[e.id] ?? e).toList(), ); /// Channel read list - List get read => _channelState.read; + List? get read => _channelState!.read; /// Channel read list as a stream - Stream> get readStream => channelStateStream.map((cs) => cs.read); + Stream?> get readStream => + channelStateStream.map((cs) => cs!.read); final BehaviorSubject _unreadCountController = BehaviorSubject.seeded(0); @@ -1594,36 +1601,36 @@ class ChannelClientState { Stream get unreadCountStream => _unreadCountController.stream; /// Unread count getter - int get unreadCount => _unreadCountController.value; + int? get unreadCount => _unreadCountController.value; bool _countMessageAsUnread(Message message) { final userId = _channel.client.state?.user?.id; - final userIsMuted = _channel.client.state?.user?.mutes?.firstWhere( - (m) => m.user?.id == message.user.id, - orElse: () => null, + final userIsMuted = _channel.client.state?.user?.mutes?.firstWhereOrNull( + (m) => m.user?.id == message.user!.id, ) != null; return message.silent != true && message.shadowed != true && - message.user.id != userId && + message.user!.id != userId && !userIsMuted; } /// Update threads with updated information about messages - void updateThreadInfo(String parentId, List messages) { - final newThreads = Map>.from(threads); + void updateThreadInfo(String? parentId, List? messages) { + final newThreads = Map?>.from(threads!); if (newThreads.containsKey(parentId)) { newThreads[parentId] = [ ...newThreads[parentId] - ?.where( - (newMessage) => !messages.any((m) => m.id == newMessage.id)) + ?.where((newMessage) => + !messages!.any((m) => m.id == newMessage.id)) ?.toList() ?? [], - ...messages, + ...messages!, ]; - newThreads[parentId].sort(_sortByCreatedAt); + newThreads[parentId]! + .sort(_sortByCreatedAt as int Function(Message, Message)?); } else { newThreads[parentId] = messages; } @@ -1633,7 +1640,7 @@ class ChannelClientState { /// Delete all channel messages void truncate() { - _channelState = _channelState.copyWith( + _channelState = _channelState!.copyWith( messages: [], ); } @@ -1651,7 +1658,7 @@ class ChannelClientState { true) ?.toList() ?? [], - ]..sort(_sortByCreatedAt); + ]..sort(_sortByCreatedAt as int Function(Message, Message)?); final newWatchers = [ ...updatedState?.watchers ?? [], @@ -1664,7 +1671,7 @@ class ChannelClientState { [], ]; - final newMembers = [ + final newMembers = [ ...updatedState?.members ?? [], ]; @@ -1673,7 +1680,7 @@ class ChannelClientState { ..._channelState?.read ?.where((r) => updatedState.read - ?.any((newRead) => newRead.user.id == r.user.id) != + ?.any((newRead) => newRead.user!.id == r.user!.id) != true) ?.toList() ?? [], @@ -1681,9 +1688,9 @@ class ChannelClientState { _checkExpiredAttachmentMessages(updatedState); - _channelState = _channelState.copyWith( + _channelState = _channelState!.copyWith( messages: newMessages, - channel: _channelState.channel?.merge(updatedState.channel), + channel: _channelState!.channel?.merge(updatedState.channel), watchers: newWatchers, watcherCount: updatedState.watcherCount, members: newMembers, @@ -1692,7 +1699,7 @@ class ChannelClientState { ); } - int _sortByCreatedAt(a, b) { + int? _sortByCreatedAt(a, b) { if (a.createdAt == null) { return 1; } @@ -1705,49 +1712,51 @@ class ChannelClientState { } /// The channel state related to this client - ChannelState get _channelState => _channelStateController.value; + ChannelState? get _channelState => _channelStateController.value; /// The channel state related to this client as a stream - Stream get channelStateStream => _channelStateController.stream; + Stream get channelStateStream => + _channelStateController.stream; /// The channel state related to this client - ChannelState get channelState => _channelStateController.value; - BehaviorSubject _channelStateController; + ChannelState? get channelState => _channelStateController.value; + late BehaviorSubject _channelStateController; final Debounce _debouncedUpdatePersistenceChannelState; - set _channelState(ChannelState v) { + set _channelState(ChannelState? v) { _channelStateController.add(v); _debouncedUpdatePersistenceChannelState?.call([v]); } /// The channel threads related to this channel - Map> get threads => _threadsController.value; + Map>? get threads => + _threadsController.value as Map>?; /// The channel threads related to this channel as a stream - Stream>> get threadsStream => + Stream?>> get threadsStream => _threadsController.stream; - final BehaviorSubject>> _threadsController = + final BehaviorSubject?>> _threadsController = BehaviorSubject.seeded({}); - set _threads(Map> v) { + set _threads(Map?> v) { _channel._client.chatPersistenceClient?.updateMessages( _channel.cid, - v.values.expand((v) => v).toList(), + v.values.expand((v) => v!).toList(), ); _threadsController.add(v); } /// Channel related typing users last value - List get typingEvents => _typingEventsController.value; + List? get typingEvents => _typingEventsController.value as List?; /// Channel related typing users stream - Stream> get typingEventsStream => _typingEventsController.stream; - final BehaviorSubject> _typingEventsController = + Stream> get typingEventsStream => _typingEventsController.stream; + final BehaviorSubject> _typingEventsController = BehaviorSubject.seeded([]); final Channel _channel; - final Map _typings = {}; + final Map _typings = {}; void _listenTypingEvents() { if (_channel.config?.typingEvents == false) { @@ -1758,7 +1767,7 @@ class ChannelClientState { ..add( _channel.on(EventType.typingStart).listen( (event) { - if (event.user.id != _channel.client.state.user.id) { + if (event.user!.id != _channel.client.state!.user!.id) { _typings[event.user] = DateTime.now(); _typingEventsController.add(_typings.keys.toList()); } @@ -1768,7 +1777,7 @@ class ChannelClientState { ..add( _channel.on(EventType.typingStop).listen( (event) { - if (event.user.id != _channel.client.state.user.id) { + if (event.user!.id != _channel.client.state!.user!.id) { _typings.remove(event.user); _typingEventsController.add(_typings.keys.toList()); } @@ -1780,12 +1789,12 @@ class ChannelClientState { .on() .where((event) => event.user != null && - members?.any((m) => m.userId == event.user.id) == true) + members?.any((m) => m.userId == event.user!.id) == true) .listen( (event) { final newMembers = List.from(members); final oldMemberIndex = - newMembers.indexWhere((m) => m.userId == event.user.id); + newMembers.indexWhere((m) => m.userId == event.user!.id); if (oldMemberIndex > -1) { final oldMember = newMembers.removeAt(oldMemberIndex); updateChannelState(ChannelState( @@ -1802,7 +1811,7 @@ class ChannelClientState { ); } - Timer _cleaningTimer; + late Timer _cleaningTimer; void _startCleaning() { if (_channel.config?.typingEvents == false) { @@ -1813,7 +1822,7 @@ class ChannelClientState { final now = DateTime.now(); if (_channel._lastTypingEvent != null && - now.difference(_channel._lastTypingEvent).inSeconds > 1) { + now.difference(_channel._lastTypingEvent!).inSeconds > 1) { _channel.stopTyping(); } @@ -1821,12 +1830,12 @@ class ChannelClientState { }); } - Timer _pinnedMessagesTimer; + late Timer _pinnedMessagesTimer; void _startCleaningPinnedMessages() { _pinnedMessagesTimer = Timer.periodic(const Duration(seconds: 30), (_) { final now = DateTime.now(); - var expiredMessages = channelState.pinnedMessages + var expiredMessages = channelState!.pinnedMessages ?.where((m) => m.pinExpires?.isBefore(now) == true) ?.toList() ?? []; @@ -1838,8 +1847,8 @@ class ChannelClientState { )) .toList(); - updateChannelState(_channelState.copyWith( - pinnedMessages: pinnedMessages.where(_pinIsValid()).toList(), + updateChannelState(_channelState!.copyWith( + pinnedMessages: pinnedMessages!.where(_pinIsValid()).toList(), messages: expiredMessages, )); } @@ -1865,7 +1874,7 @@ class ChannelClientState { void dispose() { _debouncedUpdatePersistenceChannelState?.cancel(); _unreadCountController.close(); - retryQueue.dispose(); + retryQueue!.dispose(); _subscriptions.forEach((s) => s.cancel()); _channelStateController.close(); _isUpToDateController.close(); @@ -1878,5 +1887,5 @@ class ChannelClientState { bool Function(Message) _pinIsValid() { final now = DateTime.now(); - return (Message m) => m.pinExpires.isAfter(now); + return (Message m) => m.pinExpires!.isAfter(now); } diff --git a/packages/stream_chat/lib/src/api/requests.dart b/packages/stream_chat/lib/src/api/requests.dart index 51151fca..5e3c0741 100644 --- a/packages/stream_chat/lib/src/api/requests.dart +++ b/packages/stream_chat/lib/src/api/requests.dart @@ -34,7 +34,7 @@ class SortOption { /// Sorting field Comparator required for offline sorting @JsonKey(ignore: true) - final Comparator comparator; + final Comparator? comparator; /// Serialize model to json Map toJson() => _$SortOptionToJson(this); @@ -70,31 +70,31 @@ class PaginationParams { /// Filter on ids greater than the given value. @JsonKey(name: 'id_gt') - final String greaterThan; + final String? greaterThan; /// Filter on ids greater than or equal to the given value. @JsonKey(name: 'id_gte') - final String greaterThanOrEqual; + final String? greaterThanOrEqual; /// Filter on ids smaller than the given value. @JsonKey(name: 'id_lt') - final String lessThan; + final String? lessThan; /// Filter on ids smaller than or equal to the given value. @JsonKey(name: 'id_lte') - final String lessThanOrEqual; + final String? lessThanOrEqual; /// Serialize model to json Map toJson() => _$PaginationParamsToJson(this); /// Creates a copy of [PaginationParams] with specified attributes overridden. PaginationParams copyWith({ - int limit, - int offset, - String greaterThan, - String greaterThanOrEqual, - String lessThan, - String lessThanOrEqual, + int? limit, + int? offset, + String? greaterThan, + String? greaterThanOrEqual, + String? lessThan, + String? lessThanOrEqual, }) => PaginationParams( limit: limit ?? this.limit, diff --git a/packages/stream_chat/lib/src/api/responses.dart b/packages/stream_chat/lib/src/api/responses.dart index 431a57e7..a26fc626 100644 --- a/packages/stream_chat/lib/src/api/responses.dart +++ b/packages/stream_chat/lib/src/api/responses.dart @@ -13,192 +13,192 @@ import 'package:stream_chat/src/models/user.dart'; part 'responses.g.dart'; class _BaseResponse { - String duration; + String? duration; } /// Model response for [StreamChatClient.resync] api call @JsonSerializable(createToJson: false) class SyncResponse extends _BaseResponse { /// The list of events - List events; + List? events; /// Create a new instance from a json - static SyncResponse fromJson(Map json) => - _$SyncResponseFromJson(json); + static SyncResponse fromJson(Map? json) => + _$SyncResponseFromJson(json!); } /// Model response for [StreamChatClient.queryChannels] api call @JsonSerializable(createToJson: false) class QueryChannelsResponse extends _BaseResponse { /// List of channels state returned by the query - List channels; + List? channels; /// Create a new instance from a json - static QueryChannelsResponse fromJson(Map json) => - _$QueryChannelsResponseFromJson(json); + static QueryChannelsResponse fromJson(Map? json) => + _$QueryChannelsResponseFromJson(json!); } /// Model response for [StreamChatClient.queryChannels] api call @JsonSerializable(createToJson: false) class TranslateMessageResponse extends _BaseResponse { /// List of channels state returned by the query - TranslatedMessage message; + TranslatedMessage? message; /// Create a new instance from a json - static TranslateMessageResponse fromJson(Map json) => - _$TranslateMessageResponseFromJson(json); + static TranslateMessageResponse fromJson(Map? json) => + _$TranslateMessageResponseFromJson(json!); } /// Model response for [StreamChatClient.queryChannels] api call @JsonSerializable(createToJson: false) class QueryMembersResponse extends _BaseResponse { /// List of channels state returned by the query - List members; + List? members; /// Create a new instance from a json - static QueryMembersResponse fromJson(Map json) => - _$QueryMembersResponseFromJson(json); + static QueryMembersResponse fromJson(Map? json) => + _$QueryMembersResponseFromJson(json!); } /// Model response for [StreamChatClient.queryUsers] api call @JsonSerializable(createToJson: false) class QueryUsersResponse extends _BaseResponse { /// List of users returned by the query - List users; + List? users; /// Create a new instance from a json - static QueryUsersResponse fromJson(Map json) => - _$QueryUsersResponseFromJson(json); + static QueryUsersResponse fromJson(Map? json) => + _$QueryUsersResponseFromJson(json!); } /// Model response for [channel.getReactions] api call @JsonSerializable(createToJson: false) class QueryReactionsResponse extends _BaseResponse { /// List of reactions returned by the query - List reactions; + List? reactions; /// Create a new instance from a json - static QueryReactionsResponse fromJson(Map json) => - _$QueryReactionsResponseFromJson(json); + static QueryReactionsResponse fromJson(Map? json) => + _$QueryReactionsResponseFromJson(json!); } /// Model response for [Channel.getReplies] api call @JsonSerializable(createToJson: false) class QueryRepliesResponse extends _BaseResponse { /// List of messages returned by the api call - List messages; + List? messages; /// Create a new instance from a json - static QueryRepliesResponse fromJson(Map json) => - _$QueryRepliesResponseFromJson(json); + static QueryRepliesResponse fromJson(Map? json) => + _$QueryRepliesResponseFromJson(json!); } /// Model response for [StreamChatClient.getDevices] api call @JsonSerializable(createToJson: false) class ListDevicesResponse extends _BaseResponse { /// List of user devices - List devices; + List? devices; /// Create a new instance from a json - static ListDevicesResponse fromJson(Map json) => - _$ListDevicesResponseFromJson(json); + static ListDevicesResponse fromJson(Map? json) => + _$ListDevicesResponseFromJson(json!); } /// Model response for [Channel.sendFile] api call @JsonSerializable(createToJson: false) class SendFileResponse extends _BaseResponse { /// The url of the uploaded file - String file; + String? file; /// Create a new instance from a json - static SendFileResponse fromJson(Map json) => - _$SendFileResponseFromJson(json); + static SendFileResponse fromJson(Map? json) => + _$SendFileResponseFromJson(json!); } /// Model response for [Channel.sendImage] api call @JsonSerializable(createToJson: false) class SendImageResponse extends _BaseResponse { /// The url of the uploaded file - String file; + String? file; /// Create a new instance from a json - static SendImageResponse fromJson(Map json) => - _$SendImageResponseFromJson(json); + static SendImageResponse fromJson(Map? json) => + _$SendImageResponseFromJson(json!); } /// Model response for [Channel.sendReaction] api call @JsonSerializable(createToJson: false) class SendReactionResponse extends _BaseResponse { /// Message returned by the api call - Message message; + Message? message; /// The reaction created by the api call - Reaction reaction; + Reaction? reaction; /// Create a new instance from a json - static SendReactionResponse fromJson(Map json) => - _$SendReactionResponseFromJson(json); + static SendReactionResponse fromJson(Map? json) => + _$SendReactionResponseFromJson(json!); } /// Model response for [StreamChatClient.connectGuestUser] api call @JsonSerializable(createToJson: false) class ConnectGuestUserResponse extends _BaseResponse { /// Guest user access token - String accessToken; + String? accessToken; /// Guest user - User user; + User? user; /// Create a new instance from a json - static ConnectGuestUserResponse fromJson(Map json) => - _$ConnectGuestUserResponseFromJson(json); + static ConnectGuestUserResponse fromJson(Map? json) => + _$ConnectGuestUserResponseFromJson(json!); } /// Model response for [StreamChatClient.updateUser] api call @JsonSerializable(createToJson: false) class UpdateUsersResponse extends _BaseResponse { /// Updated users - Map users; + Map? users; /// Create a new instance from a json - static UpdateUsersResponse fromJson(Map json) => - _$UpdateUsersResponseFromJson(json); + static UpdateUsersResponse fromJson(Map? json) => + _$UpdateUsersResponseFromJson(json!); } /// Model response for [StreamChatClient.updateMessage] api call @JsonSerializable(createToJson: false) class UpdateMessageResponse extends _BaseResponse { /// Message returned by the api call - Message message; + Message? message; /// Create a new instance from a json - static UpdateMessageResponse fromJson(Map json) => - _$UpdateMessageResponseFromJson(json); + static UpdateMessageResponse fromJson(Map? json) => + _$UpdateMessageResponseFromJson(json!); } /// Model response for [Channel.sendMessage] api call @JsonSerializable(createToJson: false) class SendMessageResponse extends _BaseResponse { /// Message returned by the api call - Message message; + Message? message; /// Create a new instance from a json - static SendMessageResponse fromJson(Map json) => - _$SendMessageResponseFromJson(json); + static SendMessageResponse fromJson(Map? json) => + _$SendMessageResponseFromJson(json!); } /// Model response for [StreamChatClient.getMessage] api call @JsonSerializable(createToJson: false) class GetMessageResponse extends _BaseResponse { /// Message returned by the api call - Message message; + Message? message; /// Channel of the message - ChannelModel channel; + ChannelModel? channel; /// Create a new instance from a json - static GetMessageResponse fromJson(Map json) { - final res = _$GetMessageResponseFromJson(json); + static GetMessageResponse fromJson(Map? json) { + final res = _$GetMessageResponseFromJson(json!); final jsonChannel = res.message?.extraData?.remove('channel'); if (jsonChannel != null) { res.channel = ChannelModel.fromJson(jsonChannel); @@ -211,176 +211,176 @@ class GetMessageResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class SearchMessagesResponse extends _BaseResponse { /// List of messages returned by the api call - List results; + List? results; /// Create a new instance from a json - static SearchMessagesResponse fromJson(Map json) => - _$SearchMessagesResponseFromJson(json); + static SearchMessagesResponse fromJson(Map? json) => + _$SearchMessagesResponseFromJson(json!); } /// Model response for [Channel.getMessagesById] api call @JsonSerializable(createToJson: false) class GetMessagesByIdResponse extends _BaseResponse { /// Message returned by the api call - List messages; + List? messages; /// Create a new instance from a json - static GetMessagesByIdResponse fromJson(Map json) => - _$GetMessagesByIdResponseFromJson(json); + static GetMessagesByIdResponse fromJson(Map? json) => + _$GetMessagesByIdResponseFromJson(json!); } /// Model response for [Channel.update] api call @JsonSerializable(createToJson: false) class UpdateChannelResponse extends _BaseResponse { /// Updated channel - ChannelModel channel; + ChannelModel? channel; /// Channel members - List members; + List? members; /// Message returned by the api call - Message message; + Message? message; /// Create a new instance from a json - static UpdateChannelResponse fromJson(Map json) => - _$UpdateChannelResponseFromJson(json); + static UpdateChannelResponse fromJson(Map? json) => + _$UpdateChannelResponseFromJson(json!); } /// Model response for [Channel.updatePartial] api call @JsonSerializable(createToJson: false) class PartialUpdateChannelResponse extends _BaseResponse { /// Updated channel - ChannelModel channel; + ChannelModel? channel; /// Channel members - List members; + List? members; /// Create a new instance from a json - static PartialUpdateChannelResponse fromJson(Map json) => - _$PartialUpdateChannelResponseFromJson(json); + static PartialUpdateChannelResponse fromJson(Map? json) => + _$PartialUpdateChannelResponseFromJson(json!); } /// Model response for [Channel.inviteMembers] api call @JsonSerializable(createToJson: false) class InviteMembersResponse extends _BaseResponse { /// Updated channel - ChannelModel channel; + ChannelModel? channel; /// Channel members - List members; + List? members; /// Message returned by the api call - Message message; + Message? message; /// Create a new instance from a json - static InviteMembersResponse fromJson(Map json) => - _$InviteMembersResponseFromJson(json); + static InviteMembersResponse fromJson(Map? json) => + _$InviteMembersResponseFromJson(json!); } /// Model response for [Channel.removeMembers] api call @JsonSerializable(createToJson: false) class RemoveMembersResponse extends _BaseResponse { /// Updated channel - ChannelModel channel; + ChannelModel? channel; /// Channel members - List members; + List? members; /// Message returned by the api call - Message message; + Message? message; /// Create a new instance from a json - static RemoveMembersResponse fromJson(Map json) => - _$RemoveMembersResponseFromJson(json); + static RemoveMembersResponse fromJson(Map? json) => + _$RemoveMembersResponseFromJson(json!); } /// Model response for [Channel.sendAction] api call @JsonSerializable(createToJson: false) class SendActionResponse extends _BaseResponse { /// Message returned by the api call - Message message; + Message? message; /// Create a new instance from a json - static SendActionResponse fromJson(Map json) => - _$SendActionResponseFromJson(json); + static SendActionResponse fromJson(Map? json) => + _$SendActionResponseFromJson(json!); } /// Model response for [Channel.addMembers] api call @JsonSerializable(createToJson: false) class AddMembersResponse extends _BaseResponse { /// Updated channel - ChannelModel channel; + ChannelModel? channel; /// Channel members - List members; + List? members; /// Message returned by the api call - Message message; + Message? message; /// Create a new instance from a json - static AddMembersResponse fromJson(Map json) => - _$AddMembersResponseFromJson(json); + static AddMembersResponse fromJson(Map? json) => + _$AddMembersResponseFromJson(json!); } /// Model response for [Channel.acceptInvite] api call @JsonSerializable(createToJson: false) class AcceptInviteResponse extends _BaseResponse { /// Updated channel - ChannelModel channel; + ChannelModel? channel; /// Channel members - List members; + List? members; /// Message returned by the api call - Message message; + Message? message; /// Create a new instance from a json - static AcceptInviteResponse fromJson(Map json) => - _$AcceptInviteResponseFromJson(json); + static AcceptInviteResponse fromJson(Map? json) => + _$AcceptInviteResponseFromJson(json!); } /// Model response for [Channel.rejectInvite] api call @JsonSerializable(createToJson: false) class RejectInviteResponse extends _BaseResponse { /// Updated channel - ChannelModel channel; + ChannelModel? channel; /// Channel members - List members; + List? members; /// Message returned by the api call - Message message; + Message? message; /// Create a new instance from a json - static RejectInviteResponse fromJson(Map json) => - _$RejectInviteResponseFromJson(json); + static RejectInviteResponse fromJson(Map? json) => + _$RejectInviteResponseFromJson(json!); } /// Model response for empty responses @JsonSerializable(createToJson: false) class EmptyResponse extends _BaseResponse { /// Create a new instance from a json - static EmptyResponse fromJson(Map json) => - _$EmptyResponseFromJson(json); + static EmptyResponse fromJson(Map? json) => + _$EmptyResponseFromJson(json!); } /// Model response for [Channel.query] api call @JsonSerializable(createToJson: false) class ChannelStateResponse extends _BaseResponse { /// Updated channel - ChannelModel channel; + ChannelModel? channel; /// List of messages returned by the api call - List messages; + List? messages; /// Channel members - List members; + List? members; /// Number of users watching the channel - int watcherCount; + int? watcherCount; /// List of read states - List read; + List? read; /// Create a new instance from a json static ChannelStateResponse fromJson(Map json) => diff --git a/packages/stream_chat/lib/src/api/retry_policy.dart b/packages/stream_chat/lib/src/api/retry_policy.dart index a4d1e7bd..2eaf6ab5 100644 --- a/packages/stream_chat/lib/src/api/retry_policy.dart +++ b/packages/stream_chat/lib/src/api/retry_policy.dart @@ -6,30 +6,30 @@ import 'package:stream_chat/src/exceptions.dart'; class RetryPolicy { /// Instantiate a new RetryPolicy RetryPolicy({ - @required this.shouldRetry, - @required this.retryTimeout, - this.attempt, + required this.shouldRetry, + required this.retryTimeout, + this.attempt = 0, }); /// The number of attempts tried so far int attempt = 0; /// This function evaluates if we should retry the failure - final bool Function(StreamChatClient client, int attempt, ApiError apiError) + 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 final Duration Function( - StreamChatClient client, int attempt, ApiError apiError) retryTimeout; + StreamChatClient client, int attempt, ApiError? apiError) retryTimeout; /// Creates a copy of [RetryPolicy] with specified attributes overridden. RetryPolicy copyWith({ - bool Function(StreamChatClient client, int attempt, ApiError apiError) + bool Function(StreamChatClient client, int attempt, ApiError? apiError)? shouldRetry, - Duration Function(StreamChatClient client, int attempt, ApiError apiError) + Duration Function(StreamChatClient client, int attempt, ApiError? apiError)? retryTimeout, - int attempt, + int? attempt, }) => RetryPolicy( retryTimeout: retryTimeout ?? this.retryTimeout, diff --git a/packages/stream_chat/lib/src/api/retry_queue.dart b/packages/stream_chat/lib/src/api/retry_queue.dart index 1049e0e9..a63a7d3a 100644 --- a/packages/stream_chat/lib/src/api/retry_queue.dart +++ b/packages/stream_chat/lib/src/api/retry_queue.dart @@ -14,7 +14,7 @@ import 'package:stream_chat/stream_chat.dart'; class RetryQueue { /// Instantiate a new RetryQueue object RetryQueue({ - @required this.channel, + required this.channel, this.logger, }) { _retryPolicy = channel.client.retryPolicy; @@ -28,29 +28,29 @@ class RetryQueue { final Channel channel; /// The logger associated to this queue - final Logger logger; + final Logger? logger; final _subscriptions = []; void _listenConnectionRecovered() { _subscriptions .add(channel.client.on(EventType.connectionRecovered).listen((event) { - if (!_isRetrying && event.online) { + if (!_isRetrying && event.online!) { _startRetrying(); } })); } - final HeapPriorityQueue _messageQueue = HeapPriorityQueue(_byDate); + final HeapPriorityQueue _messageQueue = HeapPriorityQueue(_byDate); bool _isRetrying = false; - RetryPolicy _retryPolicy; + RetryPolicy? _retryPolicy; /// Add a list of messages - void add(List messages) { + void add(List messages) { logger?.info('added ${messages.length} messages'); final messageList = _messageQueue.toList(); _messageQueue.addAll(messages - .where((element) => !messageList.any((m) => m.id == element.id))); + .where((element) => !messageList.any((m) => m!.id == element!.id))); if (_messageQueue.isNotEmpty && !_isRetrying) { _startRetrying(); @@ -60,10 +60,10 @@ class RetryQueue { Future _startRetrying() async { logger?.info('start retrying'); _isRetrying = true; - final retryPolicy = _retryPolicy.copyWith(attempt: 0); + final retryPolicy = _retryPolicy!.copyWith(attempt: 0); while (_messageQueue.isNotEmpty) { - final message = _messageQueue.first; + final message = _messageQueue.first!; try { logger?.info('retry attempt ${retryPolicy.attempt}'); await _sendMessage(message); @@ -72,7 +72,7 @@ class RetryQueue { logger?.info('now ${_messageQueue.length} messages in the queue'); retryPolicy.attempt = 0; } catch (error) { - ApiError apiError; + ApiError? apiError; if (error is DioError) { if (error.type == DioErrorType.response) { _messageQueue.remove(message); @@ -84,7 +84,7 @@ class RetryQueue { ); } else if (error is ApiError) { apiError = error; - if (apiError.status?.toString()?.startsWith('4') == true) { + if (apiError.status?.toString().startsWith('4') == true) { _messageQueue.remove(message); return; } @@ -101,6 +101,7 @@ class RetryQueue { } retryPolicy.attempt++; + final timeout = retryPolicy.retryTimeout( channel.client, retryPolicy.attempt, @@ -112,13 +113,13 @@ class RetryQueue { _isRetrying = false; } - void _sendFailedEvent(Message message) { - final newStatus = message.status == MessageSendingStatus.sending + void _sendFailedEvent(Message? message) { + final newStatus = message!.status == MessageSendingStatus.sending ? MessageSendingStatus.failed : (message.status == MessageSendingStatus.updating ? MessageSendingStatus.failed_update : MessageSendingStatus.failed_delete); - channel.state.addMessage(message.copyWith( + channel.state!.addMessage(message.copyWith( status: newStatus, )); } @@ -141,20 +142,20 @@ class RetryQueue { final messageList = _messageQueue.toList(); if (event.message != null) { final messageIndex = - messageList.indexWhere((m) => m.id == event.message.id); + messageList.indexWhere((m) => m!.id == event.message!.id); if (messageIndex == -1 && [ MessageSendingStatus.failed_update, MessageSendingStatus.failed, MessageSendingStatus.failed_delete, - ].contains(event.message.status)) { + ].contains(event.message!.status)) { logger?.info('add message from events'); add([event.message]); } else if (messageIndex != -1 && [ MessageSendingStatus.sent, null, - ].contains(event.message.status)) { + ].contains(event.message!.status)) { _messageQueue.remove(messageList[messageIndex]); } } @@ -167,14 +168,14 @@ class RetryQueue { _subscriptions.forEach((s) => s.cancel()); } - static int _byDate(Message m1, Message m2) { - final date1 = _getMessageDate(m1); - final date2 = _getMessageDate(m2); + static int _byDate(Message? m1, Message? m2) { + final date1 = _getMessageDate(m1!)!; + final date2 = _getMessageDate(m2!)!; return date1.compareTo(date2); } - static DateTime _getMessageDate(Message m1) { + static DateTime? _getMessageDate(Message m1) { switch (m1.status) { case MessageSendingStatus.failed_delete: case MessageSendingStatus.deleting: diff --git a/packages/stream_chat/lib/src/api/web_socket_channel_html.dart b/packages/stream_chat/lib/src/api/web_socket_channel_html.dart index 9ddd83e5..ab821ca9 100644 --- a/packages/stream_chat/lib/src/api/web_socket_channel_html.dart +++ b/packages/stream_chat/lib/src/api/web_socket_channel_html.dart @@ -3,5 +3,5 @@ import 'package:web_socket_channel/web_socket_channel.dart'; /// Html version of websocket implementation /// Used in Flutter web version -WebSocketChannel connectWebSocket(String url, {Iterable protocols}) => +WebSocketChannel connectWebSocket(String url, {Iterable? protocols}) => HtmlWebSocketChannel.connect(url, protocols: protocols); diff --git a/packages/stream_chat/lib/src/api/web_socket_channel_io.dart b/packages/stream_chat/lib/src/api/web_socket_channel_io.dart index ed37ba7f..8402ca0c 100644 --- a/packages/stream_chat/lib/src/api/web_socket_channel_io.dart +++ b/packages/stream_chat/lib/src/api/web_socket_channel_io.dart @@ -3,5 +3,5 @@ import 'package:web_socket_channel/web_socket_channel.dart'; /// IO version of websocket implementation /// Used in Flutter mobile version -WebSocketChannel connectWebSocket(String url, {Iterable protocols}) => +WebSocketChannel connectWebSocket(String url, {Iterable? protocols}) => IOWebSocketChannel.connect(url, protocols: protocols); diff --git a/packages/stream_chat/lib/src/api/web_socket_channel_stub.dart b/packages/stream_chat/lib/src/api/web_socket_channel_stub.dart index 7e2e47bd..e2efaee6 100644 --- a/packages/stream_chat/lib/src/api/web_socket_channel_stub.dart +++ b/packages/stream_chat/lib/src/api/web_socket_channel_stub.dart @@ -3,7 +3,7 @@ import 'package:web_socket_channel/web_socket_channel.dart'; /// Stub version of websocket implementation /// Used just for conditional library import WebSocketChannel connectWebSocket(String url, - {Iterable protocols, - Map headers, - Duration pingInterval}) => + {Iterable? protocols, + Map? headers, + Duration? pingInterval}) => throw UnimplementedError(); diff --git a/packages/stream_chat/lib/src/api/websocket.dart b/packages/stream_chat/lib/src/api/websocket.dart index 0f78b1bf..ba4d4271 100644 --- a/packages/stream_chat/lib/src/api/websocket.dart +++ b/packages/stream_chat/lib/src/api/websocket.dart @@ -16,8 +16,8 @@ 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 ConnectWebSocket = WebSocketChannel Function(String url, - {Iterable 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 @@ -27,7 +27,7 @@ class WebSocket { /// Creates a new websocket /// To connect the WS call [connect] WebSocket({ - @required this.baseUrl, + required this.baseUrl, this.user, this.connectParams, this.connectPayload, @@ -38,22 +38,22 @@ class WebSocket { this.healthCheckInterval = 20, this.reconnectionMonitorTimeout = 40, }) { - final qs = Map.from(connectParams); + final qs = Map.from(connectParams!); - final data = Map.from(connectPayload); + final data = Map.from(connectPayload!); - data['user_details'] = user.toJson(); + data['user_details'] = user!.toJson(); qs['json'] = json.encode(data); if (baseUrl.startsWith('https')) { _path = baseUrl.replaceFirst('https://', ''); - _path = Uri.https(_path, 'connect', qs) + _path = Uri.https(_path!, 'connect', qs) .toString() .replaceFirst('https', 'wss'); } else if (baseUrl.startsWith('http')) { _path = baseUrl.replaceFirst('http://', ''); _path = - Uri.http(_path, 'connect', qs).toString().replaceFirst('http', 'ws'); + Uri.http(_path!, 'connect', qs).toString().replaceFirst('http', 'ws'); } else { _path = Uri.https(baseUrl, 'connect', qs) .toString() @@ -65,25 +65,25 @@ class WebSocket { final String baseUrl; /// User performing the WS connection - final User user; + final User? user; /// Querystring connection parameters - final Map connectParams; + final Map? connectParams; /// WS connection payload - final Map connectPayload; + final Map? connectPayload; /// Functions that will be called every time a new event is received from the /// connection - final EventHandler handler; + final EventHandler? handler; /// A WS specific logger instance - final Logger logger; + final Logger? logger; /// Connection function /// Used only for testing purpose @visibleForTesting - final ConnectWebSocket connectFunc; + final ConnectWebSocket? connectFunc; /// Interval of the reconnection monitor timer /// This checks that it received a new event in the last @@ -107,17 +107,17 @@ class WebSocket { _connectionStatusController.add(status); /// The current connection status value - ConnectionStatus get connectionStatus => _connectionStatusController.value; + ConnectionStatus? get connectionStatus => _connectionStatusController.value; /// This notifies of connection status changes Stream get connectionStatusStream => _connectionStatusController.stream; - String _path; + String? _path; int _retryAttempt = 1; - WebSocketChannel _channel; - Timer _healthCheck, _reconnectionMonitor; - DateTime _lastEventAt; + late WebSocketChannel _channel; + Timer? _healthCheck, _reconnectionMonitor; + DateTime? _lastEventAt; bool _manuallyDisconnected = false; bool _connecting = false; bool _reconnecting = false; @@ -127,23 +127,23 @@ class WebSocket { Completer _connectionCompleter = Completer(); /// Connect the WS using the parameters passed in the constructor - Future connect() { + Future? connect() { _manuallyDisconnected = false; if (_connecting) { - logger.severe('already connecting'); + logger!.severe('already connecting'); return null; } _connecting = true; _connectionStatus = ConnectionStatus.connecting; - logger.info('connecting to $_path'); + logger!.info('connecting to $_path'); _channel = - connectFunc?.call(_path) ?? WebSocketChannel.connect(Uri.parse(_path)); + connectFunc?.call(_path) ?? WebSocketChannel.connect(Uri.parse(_path!)); _channel.stream.listen( - (data) { + (data) async { final jsonData = json.decode(data); if (jsonData['error'] != null) { return _onConnectionError(jsonData['error']); @@ -166,7 +166,7 @@ class WebSocket { return; } - logger.info('connection closed | closeCode: ${_channel.closeCode} | ' + logger!.info('connection closed | closeCode: ${_channel.closeCode} | ' 'closedReason: ${_channel.closeReason}'); if (!_reconnecting) { @@ -180,10 +180,10 @@ class WebSocket { } final event = _decodeEvent(data); - logger.info('received new event: $data'); + logger!.info('received new event: $data'); if (_lastEventAt == null) { - logger.info('connection estabilished'); + logger!.info('connection estabilished'); _connecting = false; _reconnecting = false; _lastEventAt = DateTime.now(); @@ -199,14 +199,14 @@ class WebSocket { _startHealthCheck(); } - handler(event); + handler!(event); _lastEventAt = DateTime.now(); } Future _onConnectionError(error, [stacktrace]) async { - logger..severe('error connecting')..severe(error); + logger!..severe('error connecting')..severe(error); if (stacktrace != null) { - logger.severe(stacktrace); + logger!.severe(stacktrace); } _connecting = false; @@ -225,7 +225,7 @@ class WebSocket { void _reconnectionTimer(_) { final now = DateTime.now(); if (_lastEventAt != null && - now.difference(_lastEventAt).inSeconds > reconnectionMonitorTimeout) { + now.difference(_lastEventAt!).inSeconds > reconnectionMonitorTimeout) { _channel.sink.close(); } } @@ -244,18 +244,18 @@ class WebSocket { return; } if (_connecting) { - logger.info('already connecting'); + logger!.info('already connecting'); return; } - logger.info('reconnecting..'); + logger!.info('reconnecting..'); _cancelTimers(); try { await connect(); } catch (e) { - logger.log(Level.SEVERE, e.toString()); + logger!.log(Level.SEVERE, e.toString()); } await Future.delayed( Duration(seconds: min(_retryAttempt * 5, 25)), @@ -267,7 +267,7 @@ class WebSocket { } Future _reconnect() async { - logger.info('reconnect'); + logger!.info('reconnect'); if (!_reconnecting) { _reconnecting = true; _connectionStatus = ConnectionStatus.connecting; @@ -279,20 +279,20 @@ class WebSocket { void _cancelTimers() { _lastEventAt = null; if (_healthCheck != null) { - _healthCheck.cancel(); + _healthCheck!.cancel(); } if (_reconnectionMonitor != null) { - _reconnectionMonitor.cancel(); + _reconnectionMonitor!.cancel(); } } void _healthCheckTimer(_) { - logger.info('sending health.check'); + logger!.info('sending health.check'); _channel.sink.add("{'type': 'health.check'}"); } void _startHealthCheck() { - logger.info('start health check monitor'); + logger!.info('start health check monitor'); _healthCheck = Timer.periodic( Duration(seconds: healthCheckInterval), @@ -311,7 +311,7 @@ class WebSocket { if (_manuallyDisconnected) { return; } - logger.info('disconnecting'); + logger!.info('disconnecting'); _connectionCompleter = Completer(); _cancelTimers(); _reconnecting = false; diff --git a/packages/stream_chat/lib/src/attachment_file_uploader.dart b/packages/stream_chat/lib/src/attachment_file_uploader.dart index 7d9e8c40..42fab498 100644 --- a/packages/stream_chat/lib/src/attachment_file_uploader.dart +++ b/packages/stream_chat/lib/src/attachment_file_uploader.dart @@ -11,12 +11,12 @@ abstract class AttachmentFileUploader { /// /// Optionally, access upload progress using [onSendProgress] /// and cancel the request using [cancelToken] - Future sendImage( - AttachmentFile image, - String channelId, - String channelType, { - ProgressCallback onSendProgress, - CancelToken cancelToken, + Future sendImage( + AttachmentFile? image, + String? channelId, + String? channelType, { + ProgressCallback? onSendProgress, + CancelToken? cancelToken, }); /// Uploads a [file] to the given channel. @@ -24,34 +24,34 @@ abstract class AttachmentFileUploader { /// /// Optionally, access upload progress using [onSendProgress] /// and cancel the request using [cancelToken] - Future sendFile( - AttachmentFile file, - String channelId, - String channelType, { - ProgressCallback onSendProgress, - CancelToken cancelToken, + Future sendFile( + AttachmentFile? file, + String? channelId, + String? channelType, { + ProgressCallback? onSendProgress, + CancelToken? cancelToken, }); /// Deletes a image using its [url] from the given channel. /// Returns [EmptyResponse] once deleted successfully. /// /// Optionally, cancel the request using [cancelToken] - Future deleteImage( + Future deleteImage( String url, - String channelId, - String channelType, { - CancelToken cancelToken, + String? channelId, + String? channelType, { + CancelToken? cancelToken, }); /// Deletes a file using its [url] from the given channel. /// Returns [EmptyResponse] once deleted successfully. /// /// Optionally, cancel the request using [cancelToken] - Future deleteFile( + Future deleteFile( String url, - String channelId, - String channelType, { - CancelToken cancelToken, + String? channelId, + String? channelType, { + CancelToken? cancelToken, }); } @@ -63,26 +63,26 @@ class StreamAttachmentFileUploader implements AttachmentFileUploader { final StreamChatClient _client; @override - Future sendImage( - AttachmentFile file, - String channelId, - String channelType, { - ProgressCallback onSendProgress, - CancelToken cancelToken, + Future sendImage( + AttachmentFile? file, + String? channelId, + String? channelType, { + ProgressCallback? onSendProgress, + CancelToken? cancelToken, }) async { - final filename = file.path?.split('/')?.last ?? file.name; + final filename = file!.path?.split('/')?.last ?? file.name; final mimeType = filename.mimeType; - MultipartFile multiPartFile; + MultipartFile? multiPartFile; if (file.path != null) { multiPartFile = await MultipartFile.fromFile( - file.path, + file.path!, filename: filename, contentType: mimeType, ); } else if (file.bytes != null) { multiPartFile = MultipartFile.fromBytes( - file.bytes, + file.bytes!, filename: filename, contentType: mimeType, ); @@ -100,26 +100,26 @@ class StreamAttachmentFileUploader implements AttachmentFileUploader { } @override - Future sendFile( - AttachmentFile file, - String channelId, - String channelType, { - ProgressCallback onSendProgress, - CancelToken cancelToken, + Future sendFile( + AttachmentFile? file, + String? channelId, + String? channelType, { + ProgressCallback? onSendProgress, + CancelToken? cancelToken, }) async { - final filename = file.path?.split('/')?.last ?? file.name; + final filename = file!.path?.split('/')?.last ?? file.name; final mimeType = filename.mimeType; - MultipartFile multiPartFile; + MultipartFile? multiPartFile; if (file.path != null) { multiPartFile = await MultipartFile.fromFile( - file.path, + file.path!, filename: filename, contentType: mimeType, ); } else if (file.bytes != null) { multiPartFile = MultipartFile.fromBytes( - file.bytes, + file.bytes!, filename: filename, contentType: mimeType, ); @@ -137,11 +137,11 @@ class StreamAttachmentFileUploader implements AttachmentFileUploader { } @override - Future deleteImage( + Future deleteImage( String url, - String channelId, - String channelType, { - CancelToken cancelToken, + String? channelId, + String? channelType, { + CancelToken? cancelToken, }) async { final response = await _client.delete( '/channels/$channelType/$channelId/image', @@ -152,11 +152,11 @@ class StreamAttachmentFileUploader implements AttachmentFileUploader { } @override - Future deleteFile( + Future deleteFile( String url, - String channelId, - String channelType, { - CancelToken cancelToken, + String? channelId, + String? channelType, { + CancelToken? cancelToken, }) async { final response = await _client.delete( '/channels/$channelType/$channelId/file', diff --git a/packages/stream_chat/lib/src/client.dart b/packages/stream_chat/lib/src/client.dart index f46ba178..5ea3030a 100644 --- a/packages/stream_chat/lib/src/client.dart +++ b/packages/stream_chat/lib/src/client.dart @@ -34,11 +34,11 @@ import 'package:uuid/uuid.dart'; typedef LogHandlerFunction = void Function(LogRecord record); /// Used for decoding [Map] data to a generic type `T`. -typedef DecoderFunction = T Function(Map); +typedef DecoderFunction = T Function(Map?); /// A function which can be used to request a Stream Chat API token from your /// own backend server. Function requires a single [userId]. -typedef TokenProvider = Future Function(String userId); +typedef TokenProvider = Future Function(String? userId); /// Provider used to send push notifications. enum PushProvider { @@ -85,17 +85,18 @@ class StreamChatClient { this.logHandlerFunction, Duration connectTimeout = const Duration(seconds: 6), Duration receiveTimeout = const Duration(seconds: 6), - Dio httpClient, - RetryPolicy retryPolicy, + Dio? httpClient, + RetryPolicy? retryPolicy, this.attachmentFileUploader, }) { _retryPolicy = retryPolicy ?? RetryPolicy( retryTimeout: - (StreamChatClient client, int attempt, ApiError error) => + (StreamChatClient client, int attempt, ApiError? error) => Duration(seconds: 1 * attempt), - shouldRetry: (StreamChatClient client, int attempt, ApiError error) => - attempt < 5, + shouldRetry: + (StreamChatClient client, int attempt, ApiError? error) => + attempt < 5, ); attachmentFileUploader ??= StreamAttachmentFileUploader(this); @@ -108,32 +109,32 @@ class StreamChatClient { logger.info('instantiating new client'); } - set chatPersistenceClient(ChatPersistenceClient value) { + set chatPersistenceClient(ChatPersistenceClient? value) { _originalChatPersistenceClient = value; } - ChatPersistenceClient _originalChatPersistenceClient; + ChatPersistenceClient? _originalChatPersistenceClient; /// Chat persistence client - ChatPersistenceClient get chatPersistenceClient => _chatPersistenceClient; + ChatPersistenceClient? get chatPersistenceClient => _chatPersistenceClient; - ChatPersistenceClient _chatPersistenceClient; + ChatPersistenceClient? _chatPersistenceClient; /// Attachment uploader - AttachmentFileUploader attachmentFileUploader; + AttachmentFileUploader? attachmentFileUploader; /// Whether the chat persistence is available or not bool get persistenceEnabled => _chatPersistenceClient != null; - RetryPolicy _retryPolicy; + RetryPolicy? _retryPolicy; bool _synced = false; /// The retry policy options getter - RetryPolicy get retryPolicy => _retryPolicy; + RetryPolicy? get retryPolicy => _retryPolicy; /// This client state - ClientState state; + ClientState? state; /// By default the Chat client will write all messages with level Warn or /// Error to stdout. @@ -169,7 +170,7 @@ class StreamChatClient { /// final client = StreamChatClient("stream-chat-api-key", /// logHandlerFunction: myLogHandlerFunction); ///``` - LogHandlerFunction logHandlerFunction; + LogHandlerFunction? logHandlerFunction; /// Your project Stream Chat api key. /// Find your API keys here https://getstream.io/dashboard/ @@ -184,7 +185,7 @@ class StreamChatClient { /// 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]. - final TokenProvider tokenProvider; + final TokenProvider? tokenProvider; /// [Dio] httpClient /// It's be chosen because it's easy to use and supports interesting features @@ -195,8 +196,8 @@ class StreamChatClient { static const _defaultBaseURL = 'chat-us-east-1.stream-io-api.com'; static const _tokenExpiredErrorCode = 40; - StreamSubscription _connectionStatusSubscription; - Future Function(ConnectionStatus) _connectionStatusHandler; + StreamSubscription? _connectionStatusSubscription; + Future Function(ConnectionStatus)? _connectionStatusHandler; final BehaviorSubject _controller = BehaviorSubject(); @@ -211,7 +212,7 @@ class StreamChatClient { _wsConnectionStatusController.add(status); /// The current status value of the websocket connection - ConnectionStatus get wsConnectionStatus => + ConnectionStatus? get wsConnectionStatus => _wsConnectionStatusController.value; /// This notifies the connection status of the websocket connection. @@ -220,19 +221,19 @@ class StreamChatClient { _wsConnectionStatusController.stream; /// The current user token - String token; + String? token; /// The id of the current websocket connection - String get connectionId => _connectionId; + String? get connectionId => _connectionId; bool _anonymous = false; - String _connectionId; - WebSocket _ws; + String? _connectionId; + late WebSocket _ws; bool get _hasConnectionId => _connectionId != null; void _setupDio( - Dio httpClient, + Dio? httpClient, Duration receiveTimeout, Duration connectTimeout, ) { @@ -301,11 +302,11 @@ class StreamChatClient { if (tokenProvider != null) { httpClient.lock(); - final userId = state.user.id; + final userId = state!.user!.id; await _disconnect(); - final newToken = await tokenProvider(userId); + final newToken = await tokenProvider!(userId); await Future.delayed(const Duration(seconds: 4)); token = newToken; @@ -341,13 +342,11 @@ class StreamChatClient { ), ), ); - } catch (err) { + } on DioError { handler.reject(err); } } } - - return err; } LogHandlerFunction _getDefaultLogHandler() { @@ -391,11 +390,11 @@ class StreamChatClient { await _disconnect(); httpClient.close(); await _controller.close(); - state.dispose(); + state!.dispose(); await _wsConnectionStatusController.close(); } - Map get _httpHeaders => { + Map get _httpHeaders => { 'Authorization': token, 'stream-auth-type': _authType, 'X-Stream-Client': _userAgent, @@ -409,8 +408,8 @@ class StreamChatClient { /// Connects the current user, this triggers a connection to the API. /// It returns a [Future] that resolves when the connection is setup. - Future connectUser(User user, String token) async { - if (_connectCompleter != null && !_connectCompleter.isCompleted) { + Future connectUser(User user, String? token) async { + if (_connectCompleter != null && !_connectCompleter!.isCompleted) { logger.warning('Already connecting'); throw Exception('Already connecting'); } @@ -418,15 +417,15 @@ class StreamChatClient { _connectCompleter = Completer(); logger.info('connect user'); - state.user = OwnUser.fromJson(user.toJson()); + state!.user = OwnUser.fromJson(user.toJson()); this.token = token; _anonymous = false; return connect().then((event) { - _connectCompleter.complete(event); + _connectCompleter!.complete(event); return event; }).catchError((e, s) { - _connectCompleter.completeError(e, s); + _connectCompleter!.completeError(e, s); throw e; }); } @@ -447,17 +446,17 @@ class StreamChatClient { Use `connectUser` providing a token. '''); } - final token = await tokenProvider(user.id); + final token = await tokenProvider!(user.id); return connectUser(user, token); } /// Stream of [Event] coming from websocket connection /// Pass an eventType as parameter in order to filter just a type of event Stream on([ - String eventType, - String eventType2, - String eventType3, - String eventType4, + String? eventType, + String? eventType2, + String? eventType3, + String? eventType4, ]) => stream.where((event) => eventType == null || @@ -474,7 +473,7 @@ class StreamChatClient { _connectionId = event.connectionId; } - if (!event.isLocal) { + if (!event.isLocal!) { if (_synced && event.createdAt != null) { await _chatPersistenceClient?.updateConnectionInfo(event); await _chatPersistenceClient?.updateLastSyncAt(event.createdAt); @@ -482,16 +481,16 @@ class StreamChatClient { } if (event.user != null) { - state._updateUser(event.user); + state!._updateUser(event.user); } if (event.me != null) { - state.user = event.me; + state!.user = event.me; } _controller.add(event); } - Completer _connectCompleter; + Completer? _connectCompleter; /// Connect the client websocket Future connect() async { @@ -510,12 +509,12 @@ class StreamChatClient { if (_originalChatPersistenceClient != null) { _chatPersistenceClient = _originalChatPersistenceClient; - await _chatPersistenceClient.connect(state.user.id); + await _chatPersistenceClient!.connect(state!.user!.id); } _ws = WebSocket( baseUrl: baseURL, - user: state.user, + user: state!.user, connectParams: { 'api_key': apiKey, 'authorization': token, @@ -523,7 +522,7 @@ class StreamChatClient { 'X-Stream-Client': _userAgent, }, connectPayload: { - 'user_id': state.user.id, + 'user_id': state!.user!.id, 'server_determines_connection_id': true, }, handler: handleEvent, @@ -544,11 +543,11 @@ class StreamChatClient { type: EventType.connectionRecovered, online: true, )); - if (state.channels?.isNotEmpty == true) { + if (state!.channels?.isNotEmpty == true) { // ignore: unawaited_futures queryChannelsOnline(filter: { 'cid': { - '\$in': state.channels.keys.toList(), + '\$in': state!.channels!.keys.toList(), }, }).then( (_) async { @@ -564,9 +563,9 @@ class StreamChatClient { _connectionStatusSubscription = _ws.connectionStatusStream.listen(_connectionStatusHandler); - var event = await _chatPersistenceClient?.getConnectionInfo(); + var event = (await _chatPersistenceClient?.getConnectionInfo())!; - await _ws.connect().then((e) async { + await _ws.connect()!.then((e) async { await _chatPersistenceClient?.updateConnectionInfo(e); event = e; await resync(); @@ -582,8 +581,8 @@ class StreamChatClient { } /// Get the events missed while offline to sync the offline storage - Future resync([List cids]) async { - final lastSyncAt = await _chatPersistenceClient?.getLastSyncAt(); + Future resync([List? cids]) async { + final lastSyncAt = (await _chatPersistenceClient?.getLastSyncAt())!; if (lastSyncAt == null) { _synced = true; @@ -606,17 +605,17 @@ class StreamChatClient { final res = decode( rawRes.data, SyncResponse.fromJson, - ); + )!; - res.events.sort((a, b) => a.createdAt.compareTo(b.createdAt)); + res.events!.sort((a, b) => a.createdAt!.compareTo(b.createdAt!)); - res.events.forEach((element) { + res.events!.forEach((element) { logger ..fine('element.type: ${element.type}') ..fine('element.message.text: ${element.message?.text}'); }); - res.events.forEach(handleEvent); + res.events!.forEach(handleEvent); await _chatPersistenceClient?.updateLastSyncAt(DateTime.now()); _synced = true; @@ -625,17 +624,17 @@ class StreamChatClient { } } - String _asMap(sort) => sort?.map((s) => s.toJson().toString())?.join(''); + String? _asMap(sort) => sort?.map((s) => s.toJson().toString())?.join(''); final _queryChannelsStreams = >>{}; /// Requests channels with a given query. Stream> queryChannels({ - Map filter, - List> sort, - Map options, + Map? filter, + List>? sort, + Map? options, PaginationParams paginationParams = const PaginationParams(), - int messageLimit, + int? messageLimit, bool waitForConnect = true, }) async* { final hash = base64.encode(utf8.encode( @@ -644,7 +643,7 @@ class StreamChatClient { )); if (_queryChannelsStreams.containsKey(hash)) { - yield await _queryChannelsStreams[hash]; + yield await _queryChannelsStreams[hash]!; } else { final channels = await queryChannelsOffline( filter: filter, @@ -674,17 +673,17 @@ class StreamChatClient { /// Requests channels with a given query from the API. Future> queryChannelsOnline({ - @required Map filter, - List> sort, - Map options, - int messageLimit, + required Map? filter, + List>? sort, + Map? options, + int? messageLimit, PaginationParams paginationParams = const PaginationParams(), bool waitForConnect = true, }) async { if (waitForConnect) { - if (_connectCompleter != null && !_connectCompleter.isCompleted) { + if (_connectCompleter != null && !_connectCompleter!.isCompleted) { logger.info('awaiting connection completer'); - await _connectCompleter.future; + await _connectCompleter!.future; } if (wsConnectionStatus != ConnectionStatus.connected) { throw Exception( @@ -730,7 +729,7 @@ class StreamChatClient { final res = decode( response.data, QueryChannelsResponse.fromJson, - ); + )!; if ((res.channels ?? []).isEmpty && (paginationParams?.offset ?? 0) == 0) { logger.warning( @@ -742,14 +741,14 @@ class StreamChatClient { return []; } - final channels = res.channels; + final channels = res.channels!; final users = channels - .expand((it) => it.members) - .map((it) => it.user) + .expand((it) => it.members!) + .map((it) => it!.user) .toList(growable: false); - state._updateUsers(users); + state!._updateUsers(users); logger.info('Got ${res.channels?.length} channels from api'); @@ -757,39 +756,39 @@ class StreamChatClient { await _chatPersistenceClient?.updateChannelQueries( filter, - channels.map((c) => c.channel.cid).toList(), + channels.map((c) => c.channel!.cid).toList(), clearQueryCache: paginationParams?.offset == null || paginationParams.offset == 0, ); - state.channels = updateData.key; + state!.channels = updateData.key; return updateData.value; } /// Requests channels with a given query from the Persistence client. Future> queryChannelsOffline({ - @required Map filter, - @required List> sort, + required Map? filter, + required List>? sort, PaginationParams paginationParams = const PaginationParams(), }) async { - final offlineChannels = await _chatPersistenceClient?.getChannelStates( + final offlineChannels = (await _chatPersistenceClient?.getChannelStates( filter: filter, sort: sort, paginationParams: paginationParams, - ); + ))!; final updatedData = _mapChannelStateToChannel(offlineChannels); - state.channels = updatedData.key; + state!.channels = updatedData.key; return updatedData.value; } - MapEntry, List> _mapChannelStateToChannel( + MapEntry, List> _mapChannelStateToChannel( List channelStates, ) { - final channels = {...state.channels ?? {}}; + final channels = {...state!.channels ?? {}}; final newChannels = []; if (channelStates != null) { for (final channelState in channelStates) { - final channel = channels[channelState.channel.cid]; + final channel = channels[channelState.channel!.cid]; if (channel != null) { channel.state?.updateChannelState(channelState); newChannels.add(channel); @@ -817,7 +816,7 @@ class StreamChatClient { /// Handy method to make http GET request with error parsing. Future> get( String path, { - Map queryParameters, + Map? queryParameters, }) async { try { final response = await httpClient.get( @@ -835,8 +834,8 @@ class StreamChatClient { Future> post( String path, { dynamic data, - ProgressCallback onSendProgress, - CancelToken cancelToken, + ProgressCallback? onSendProgress, + CancelToken? cancelToken, }) async { try { final response = await httpClient.post( @@ -855,8 +854,8 @@ class StreamChatClient { /// Handy method to make http DELETE request with error parsing. Future> delete( String path, { - Map queryParameters, - CancelToken cancelToken, + Map? queryParameters, + CancelToken? cancelToken, }) async { try { final response = await httpClient.delete( @@ -874,7 +873,7 @@ class StreamChatClient { /// Handy method to make http PATCH request with error parsing. Future> patch( String path, { - Map queryParameters, + Map? queryParameters, dynamic data, }) async { try { @@ -893,7 +892,7 @@ class StreamChatClient { /// Handy method to make http PUT request with error parsing. Future> put( String path, { - Map queryParameters, + Map? queryParameters, dynamic data, }) async { try { @@ -910,7 +909,7 @@ class StreamChatClient { } /// Used to log errors and stacktrace in case of bad json deserialization - T decode(String j, DecoderFunction decoderFunction) { + T? decode(String? j, DecoderFunction decoderFunction) { try { if (j == null) { return null; @@ -927,8 +926,8 @@ class StreamChatClient { String get _userAgent => 'stream-chat-dart-client-${CurrentPlatform.name}-' '${PACKAGE_VERSION.split('+')[0]}'; - Map get _commonQueryParams => { - 'user_id': state.user?.id, + Map get _commonQueryParams => { + 'user_id': state!.user?.id, 'api_key': apiKey, 'connection_id': _connectionId, }; @@ -943,7 +942,7 @@ class StreamChatClient { /// to the API. It returns a [Future] that resolves when the connection is /// setup. Future connectAnonymousUser() async { - if (_connectCompleter != null && !_connectCompleter.isCompleted) { + if (_connectCompleter != null && !_connectCompleter!.isCompleted) { logger.warning('Already connecting'); throw Exception('Already connecting'); } @@ -952,13 +951,13 @@ class StreamChatClient { _anonymous = true; const uuid = Uuid(); - state.user = OwnUser(id: uuid.v4()); + state!.user = OwnUser(id: uuid.v4()); return connect().then((event) { - _connectCompleter.complete(event); + _connectCompleter!.complete(event); return event; }).catchError((e, s) { - _connectCompleter.completeError(e, s); + _connectCompleter!.completeError(e, s); throw e; }); } @@ -978,8 +977,8 @@ class StreamChatClient { res.data, ConnectGuestUserResponse.fromJson)) .whenComplete(() => _anonymous = false); return connectUser( - response.user, - response.accessToken, + (response?.user)!, + response?.accessToken, ); } @@ -999,7 +998,7 @@ class StreamChatClient { _connectCompleter = null; if (clearUser == true) { - state.dispose(); + state!.dispose(); state = ClientState(this); } @@ -1015,10 +1014,10 @@ class StreamChatClient { /// Requests users with a given query. Future queryUsers({ - Map filter, - List sort, - Map options, - PaginationParams pagination, + Map? filter, + List? sort, + Map? options, + PaginationParams? pagination, }) async { final defaultOptions = { 'presence': _hasConnectionId, @@ -1047,20 +1046,20 @@ class StreamChatClient { final response = decode( rawRes.data, QueryUsersResponse.fromJson, - ); + )!; - state?._updateUsers(response.users); + state?._updateUsers(response.users!); return response; } /// A message search. - Future search( + Future search( Map filters, { - String query, - List sort, - PaginationParams paginationParams, - Map messageFilters, + String? query, + List? sort, + PaginationParams? paginationParams, + Map? messageFilters, }) async { assert(() { if (filters == null || filters.isEmpty) { @@ -1094,14 +1093,14 @@ class StreamChatClient { } /// Send a [file] to the [channelId] of type [channelType] - Future sendFile( - AttachmentFile file, - String channelId, - String channelType, { - ProgressCallback onSendProgress, - CancelToken cancelToken, + Future sendFile( + AttachmentFile? file, + String? channelId, + String? channelType, { + ProgressCallback? onSendProgress, + CancelToken? cancelToken, }) => - attachmentFileUploader.sendFile( + attachmentFileUploader!.sendFile( file, channelId, channelType, @@ -1110,14 +1109,14 @@ class StreamChatClient { ); /// Send a [image] to the [channelId] of type [channelType] - Future sendImage( - AttachmentFile image, - String channelId, - String channelType, { - ProgressCallback onSendProgress, - CancelToken cancelToken, + Future sendImage( + AttachmentFile? image, + String? channelId, + String? channelType, { + ProgressCallback? onSendProgress, + CancelToken? cancelToken, }) => - attachmentFileUploader.sendImage( + attachmentFileUploader!.sendImage( image, channelId, channelType, @@ -1126,13 +1125,13 @@ class StreamChatClient { ); /// Delete a file from this channel - Future deleteFile( + Future deleteFile( String url, - String channelId, - String channelType, { - CancelToken cancelToken, + String? channelId, + String? channelType, { + CancelToken? cancelToken, }) => - attachmentFileUploader.deleteFile( + attachmentFileUploader!.deleteFile( url, channelId, channelType, @@ -1140,13 +1139,13 @@ class StreamChatClient { ); /// Delete an image from this channel - Future deleteImage( + Future deleteImage( String url, - String channelId, - String channelType, { - CancelToken cancelToken, + String? channelId, + String? channelType, { + CancelToken? cancelToken, }) => - attachmentFileUploader.deleteImage( + attachmentFileUploader!.deleteImage( url, channelId, channelType, @@ -1154,7 +1153,7 @@ class StreamChatClient { ); /// Add a device for Push Notifications. - Future addDevice(String id, PushProvider pushProvider) async { + Future addDevice(String id, PushProvider pushProvider) async { final response = await post('/devices', data: { 'id': id, 'push_provider': pushProvider.name, @@ -1163,14 +1162,14 @@ class StreamChatClient { } /// Gets a list of user devices. - Future getDevices() async { + Future getDevices() async { final response = await get('/devices'); return decode( response.data, ListDevicesResponse.fromJson); } /// Remove a user's device. - Future removeDevice(String id) async { + Future removeDevice(String id) async { final response = await delete('/devices', queryParameters: { 'id': id, }); @@ -1188,24 +1187,26 @@ class StreamChatClient { /// Returns a channel client with the given type, id and custom data. Channel channel( String type, { - String id, - Map extraData, + String? id, + Map? extraData, }) { if (type != null && id != null && - state.channels?.containsKey('$type:$id') == true) { - return state.channels['$type:$id']; + state!.channels?.containsKey('$type:$id') == true) { + if (state!.channels!['$type:$id'] != null) { + return state!.channels!['$type:$id'] as Channel; + } } return Channel(this, type, id, extraData); } /// Update or Create the given user object. - Future updateUser(User user) async => + Future updateUser(User user) async => updateUsers([user]); /// Batch update a list of users - Future updateUsers(List users) async { + Future updateUsers(List users) async { final response = await post('/users', data: { 'users': users.asMap().map((_, u) => MapEntry(u.id, u.toJson())), }); @@ -1216,7 +1217,7 @@ class StreamChatClient { } /// Bans a user from all channels - Future banUser( + Future banUser( String targetUserID, [ Map options = const {}, ]) async { @@ -1232,7 +1233,7 @@ class StreamChatClient { } /// Remove global ban for a user - Future unbanUser( + Future unbanUser( String targetUserID, [ Map options = const {}, ]) async { @@ -1248,7 +1249,7 @@ class StreamChatClient { } /// Shadow bans a user - Future shadowBan( + Future shadowBan( String targetID, [ Map options = const {}, ]) async => @@ -1258,7 +1259,7 @@ class StreamChatClient { }); /// Removes shadow ban from a user - Future removeShadowBan( + Future removeShadowBan( String targetID, [ Map options = const {}, ]) async => @@ -1268,7 +1269,7 @@ class StreamChatClient { }); /// Mutes a user - Future muteUser(String targetID) async { + Future muteUser(String targetID) async { final response = await post('/moderation/mute', data: { 'target_id': targetID, }); @@ -1276,7 +1277,7 @@ class StreamChatClient { } /// Unmutes a user - Future unmuteUser(String targetID) async { + Future unmuteUser(String targetID) async { final response = await post('/moderation/unmute', data: { 'target_id': targetID, }); @@ -1284,7 +1285,7 @@ class StreamChatClient { } /// Flag a message - Future flagMessage(String messageID) async { + Future flagMessage(String messageID) async { final response = await post('/moderation/flag', data: { 'target_message_id': messageID, }); @@ -1292,7 +1293,7 @@ class StreamChatClient { } /// Unflag a message - Future unflagMessage(String messageId) async { + Future unflagMessage(String messageId) async { final response = await post('/moderation/unflag', data: { 'target_message_id': messageId, }); @@ -1300,7 +1301,7 @@ class StreamChatClient { } /// Flag a user - Future flagUser(String userId) async { + Future flagUser(String userId) async { final response = await post('/moderation/flag', data: { 'target_user_id': userId, }); @@ -1308,7 +1309,7 @@ class StreamChatClient { } /// Unflag a message - Future unflagUser(String userId) async { + Future unflagUser(String userId) async { final response = await post('/moderation/unflag', data: { 'target_user_id': userId, }); @@ -1316,14 +1317,14 @@ class StreamChatClient { } /// Mark all channels for this user as read - Future markAllRead() async { + Future markAllRead() async { final response = await post('/channels/read'); return decode(response.data, EmptyResponse.fromJson); } /// Sends the message to the given channel - Future sendMessage( - Message message, String channelId, String channelType) async { + Future sendMessage( + Message message, String? channelId, String? channelType) async { final response = await post( '/channels/$channelType/$channelId/message', data: {'message': message.toJson()}, @@ -1332,7 +1333,7 @@ class StreamChatClient { } /// Update the given message - Future updateMessage(Message message) async { + Future updateMessage(Message message) async { final response = await post( '/messages/${message.id}', data: {'message': message.toJson()}, @@ -1341,19 +1342,19 @@ class StreamChatClient { } /// Deletes the given message - Future deleteMessage(Message message) async { + Future deleteMessage(Message message) async { final response = await delete('/messages/${message.id}'); return decode(response.data, EmptyResponse.fromJson); } /// Get a message by id - Future getMessage(String messageId) async { + Future getMessage(String messageId) async { final response = await get('/messages/$messageId'); return decode(response.data, GetMessageResponse.fromJson); } /// Pins provided message - Future pinMessage( + Future pinMessage( Message message, Object timeoutOrExpirationDate, ) { @@ -1366,7 +1367,7 @@ class StreamChatClient { return true; }(), 'Check whether time out is valid'); - DateTime pinExpires; + DateTime? pinExpires; if (timeoutOrExpirationDate is DateTime) { pinExpires = timeoutOrExpirationDate.toUtc(); } else if (timeoutOrExpirationDate is num) { @@ -1380,7 +1381,7 @@ class StreamChatClient { } /// Unpins provided message - Future unpinMessage(Message message) => + Future unpinMessage(Message message) => updateMessage(message.copyWith(pinned: false)); } @@ -1395,7 +1396,7 @@ class ClientState { .map((e) => e.me) .listen((user) { _userController.add(user); - if (user.totalUnreadCount != null) { + if (user!.totalUnreadCount != null) { _totalUnreadCountController.add(user.totalUnreadCount); } @@ -1425,7 +1426,7 @@ class ClientState { final _subscriptions = []; /// Used internally for optimistic update of unread count - set totalUnreadCount(int unreadCount) { + set totalUnreadCount(int? unreadCount) { _totalUnreadCountController?.add(unreadCount ?? 0); } @@ -1433,15 +1434,15 @@ class ClientState { _subscriptions.add(_client.on(EventType.channelHidden).listen((event) { _client.chatPersistenceClient?.deleteChannels([event.cid]); if (channels != null) { - channels = channels..removeWhere((cid, ch) => cid == event.cid); + channels = channels?..removeWhere((cid, ch) => cid == event.cid); } })); } void _listenUserUpdated() { _subscriptions.add(_client.on(EventType.userUpdated).listen((event) { - if (event.user.id == user.id) { - user = OwnUser.fromJson(event.user.toJson()); + if (event.user!.id == user!.id) { + user = OwnUser.fromJson(event.user!.toJson()); } _updateUser(event.user); })); @@ -1455,10 +1456,10 @@ class ClientState { EventType.notificationChannelDeleted, ) .listen((Event event) async { - final eventChannel = event.channel; + final eventChannel = event.channel!; await _client.chatPersistenceClient?.deleteChannels([eventChannel.cid]); if (channels != null) { - channels = channels..remove(eventChannel.cid); + channels = channels?..remove(eventChannel.cid); } })); } @@ -1466,61 +1467,62 @@ class ClientState { final StreamChatClient _client; /// Update user information - set user(OwnUser user) { + set user(OwnUser? user) { _userController.add(user); } - void _updateUsers(List userList) { + void _updateUsers(List userList) { final newUsers = { ...users ?? {}, - for (var user in userList) user.id: user, + for (var user in userList) user!.id: user, }; _usersController.add(newUsers); } - void _updateUser(User user) => _updateUsers([user]); + void _updateUser(User? user) => _updateUsers([user]); /// The current user - OwnUser get user => _userController.value; + OwnUser? get user => _userController.value; /// The current user as a stream - Stream get userStream => _userController.stream; + Stream get userStream => _userController.stream; /// The current user - Map get users => _usersController.value; + Map? get users => _usersController.value as Map?; /// The current user as a stream - Stream> get usersStream => _usersController.stream; + Stream> get usersStream => _usersController.stream; /// The current unread channels count - int get unreadChannels => _unreadChannelsController.value; + int? get unreadChannels => _unreadChannelsController.value; /// The current unread channels count as a stream - Stream get unreadChannelsStream => _unreadChannelsController.stream; + Stream get unreadChannelsStream => _unreadChannelsController.stream; /// The current total unread messages count - int get totalUnreadCount => _totalUnreadCountController.value; + int? get totalUnreadCount => _totalUnreadCountController.value; /// The current total unread messages count as a stream - Stream get totalUnreadCountStream => _totalUnreadCountController.stream; + Stream get totalUnreadCountStream => _totalUnreadCountController.stream; /// The current list of channels in memory as a stream - Stream> get channelsStream => _channelsController.stream; + Stream?> get channelsStream => + _channelsController.stream; /// The current list of channels in memory - Map get channels => _channelsController.value; + Map? get channels => _channelsController.value; - set channels(Map v) { + set channels(Map? v) { _channelsController.add(v); } - final BehaviorSubject> _channelsController = + final BehaviorSubject?> _channelsController = BehaviorSubject.seeded({}); - final BehaviorSubject _userController = BehaviorSubject(); - final BehaviorSubject> _usersController = + final BehaviorSubject _userController = BehaviorSubject(); + final BehaviorSubject> _usersController = BehaviorSubject.seeded({}); - final BehaviorSubject _unreadChannelsController = BehaviorSubject(); - final BehaviorSubject _totalUnreadCountController = BehaviorSubject(); + final BehaviorSubject _unreadChannelsController = BehaviorSubject(); + final BehaviorSubject _totalUnreadCountController = BehaviorSubject(); /// Call this method to dispose this object void dispose() { @@ -1528,7 +1530,7 @@ class ClientState { _userController.close(); _unreadChannelsController.close(); _totalUnreadCountController.close(); - channels.values.forEach((c) => c.dispose()); + channels!.values.forEach((c) => c.dispose()); _channelsController.close(); } } 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 d15772df..6036c649 100644 --- a/packages/stream_chat/lib/src/db/chat_persistence_client.dart +++ b/packages/stream_chat/lib/src/db/chat_persistence_client.dart @@ -11,7 +11,7 @@ import 'package:stream_chat/src/models/user.dart'; /// A simple client used for persisting chat data locally. abstract class ChatPersistenceClient { /// Creates a new connection to the client - Future connect(String userId); + Future connect(String? userId); /// Closes the client connection /// If [flush] is true, the data will also be deleted @@ -20,7 +20,7 @@ abstract class ChatPersistenceClient { /// Get stored replies by messageId Future> getReplies( String parentId, { - PaginationParams options, + PaginationParams? options, }); /// Get stored connection event @@ -33,40 +33,40 @@ abstract class ChatPersistenceClient { Future updateConnectionInfo(Event event); /// Update stored lastSyncAt - Future updateLastSyncAt(DateTime lastSyncAt); + Future updateLastSyncAt(DateTime? lastSyncAt); /// Get the channel cids saved in the offline storage Future> getChannelCids(); /// Get stored [ChannelModel]s by providing channel [cid] - Future getChannelByCid(String cid); + Future getChannelByCid(String? cid); /// Get stored channel [Member]s by providing channel [cid] - Future> getMembersByCid(String cid); + Future> getMembersByCid(String? cid); /// Get stored channel [Read]s by providing channel [cid] - Future> getReadsByCid(String cid); + Future> getReadsByCid(String? cid); /// Get stored [Message]s by providing channel [cid] /// /// Optionally, you can [messagePagination] /// for filtering out messages Future> getMessagesByCid( - String cid, { - PaginationParams messagePagination, + String? cid, { + PaginationParams? messagePagination, }); /// Get stored pinned [Message]s by providing channel [cid] Future> getPinnedMessagesByCid( - String cid, { - PaginationParams messagePagination, + String? cid, { + PaginationParams? messagePagination, }); /// Get [ChannelState] data by providing channel [cid] Future getChannelStateByCid( - String cid, { - PaginationParams messagePagination, - PaginationParams pinnedMessagePagination, + String? cid, { + PaginationParams? messagePagination, + PaginationParams? pinnedMessagePagination, }) async { final data = await Future.wait([ getMembersByCid(cid), @@ -76,11 +76,11 @@ abstract class ChatPersistenceClient { getPinnedMessagesByCid(cid, messagePagination: pinnedMessagePagination), ]); return ChannelState( - members: data[0], - read: data[1], - channel: data[2], - messages: data[3], - pinnedMessages: data[4], + members: data[0] as List?, + read: data[1] as List?, + channel: data[2] as ChannelModel?, + messages: data[3] as List?, + pinnedMessages: data[4] as List?, ); } @@ -89,9 +89,9 @@ abstract class ChatPersistenceClient { /// Optionally, pass [filter], [sort], [paginationParams] /// for filtering out states. Future> getChannelStates({ - Map filter, - List> sort = const [], - PaginationParams paginationParams, + Map? filter, + List>? sort = const [], + PaginationParams? paginationParams, }); /// Update list of channel queries. @@ -99,8 +99,8 @@ abstract class ChatPersistenceClient { /// If [clearQueryCache] is true before the insert /// the list of matching rows will be deleted Future updateChannelQueries( - Map filter, - List cids, { + Map? filter, + List cids, { bool clearQueryCache = false, }); @@ -119,46 +119,46 @@ abstract class ChatPersistenceClient { Future deletePinnedMessageByIds(List messageIds); /// Remove a message by channel [cid] - Future deleteMessageByCid(String cid) => deleteMessageByCids([cid]); + Future deleteMessageByCid(String? cid) => deleteMessageByCids([cid]); /// Remove a pinned message by channel [cid] Future deletePinnedMessageByCid(String cid) async => deletePinnedMessageByCids([cid]); /// Remove a message by message [cids] - Future deleteMessageByCids(List cids); + Future deleteMessageByCids(List cids); /// Remove a pinned message by message [cids] Future deletePinnedMessageByCids(List cids); /// Remove a channel by [cid] - Future deleteChannels(List cids); + Future deleteChannels(List cids); /// Updates the message data of a particular channel [cid] with /// the new [messages] data - Future updateMessages(String cid, List messages); + Future updateMessages(String? cid, List messages); /// Updates the pinned message data of a particular channel [cid] with /// the new [messages] data - Future updatePinnedMessages(String cid, List messages); + Future updatePinnedMessages(String? cid, List messages); /// Returns all the threads by parent message of a particular channel by /// providing channel [cid] - Future>> getChannelThreads(String cid); + Future>> getChannelThreads(String? cid); /// Updates all the channels using the new [channels] data. - Future updateChannels(List channels); + Future updateChannels(List channels); /// Updates all the members of a particular channle [cid] /// with the new [members] data - Future updateMembers(String cid, List members); + Future updateMembers(String? cid, List members); /// Updates the read data of a particular channel [cid] with /// the new [reads] data - Future updateReads(String cid, List reads); + Future updateReads(String? cid, List reads); /// Updates the users data with the new [users] data - Future updateUsers(List users); + Future updateUsers(List users); /// Updates the reactions data with the new [reactions] data Future updateReactions(List reactions); @@ -167,7 +167,7 @@ abstract class ChatPersistenceClient { Future deleteReactionsByMessageId(List messageIds); /// Deletes all the members by channel [cids] - Future deleteMembersByCids(List cids); + Future deleteMembersByCids(List cids); /// Update the channel state data using [channelState] Future updateChannelState(ChannelState channelState) => @@ -176,12 +176,12 @@ abstract class ChatPersistenceClient { /// Update list of channel states Future updateChannelStates(List channelStates) async { final deleteReactions = deleteReactionsByMessageId(channelStates - .expand((it) => it.messages) + .expand((it) => it.messages!) .map((m) => m.id) .toList(growable: false)); final deleteMembers = deleteMembersByCids( - channelStates.map((it) => it.channel.cid).toList(growable: false), + channelStates.map((it) => it.channel!.cid).toList(growable: false), ); await Future.wait([ @@ -193,54 +193,54 @@ abstract class ChatPersistenceClient { channelStates.map((it) => it.channel).where((it) => it != null); final reactions = channelStates - .expand((it) => it.messages) + .expand((it) => it.messages!) .expand((it) => [ if (it.ownReactions != null) - ...it.ownReactions.where((r) => r.userId != null), + ...it.ownReactions!.where((r) => r.userId != null), if (it.latestReactions != null) - ...it.latestReactions.where((r) => r.userId != null) + ...it.latestReactions!.where((r) => r.userId != null) ]) .where((it) => it != null); final users = channelStates .map((cs) => [ cs.channel?.createdBy, - ...cs.messages + ...?cs.messages ?.map((m) => [ m.user, if (m.latestReactions != null) - ...m.latestReactions.map((r) => r.user), + ...m.latestReactions!.map((r) => r.user), if (m.ownReactions != null) - ...m.ownReactions.map((r) => r.user), + ...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), + if (cs.read != null) ...cs.read!.map((r) => r.user), + if (cs.members != null) ...cs.members!.map((m) => m!.user), ]) .expand((it) => it) .where((it) => it != null); final updateMessagesFuture = channelStates.map((it) { - final cid = it.channel.cid; - final messages = it.messages.where((it) => it != null); + final cid = it.channel!.cid; + final messages = it.messages!.where((it) => it != null); return updateMessages(cid, messages.toList(growable: false)); }).toList(growable: false); final updatePinnedMessagesFuture = channelStates.map((it) { - final cid = it.channel.cid; - final messages = it.pinnedMessages.where((it) => it != null); + final cid = it.channel!.cid; + final messages = it.pinnedMessages!.where((it) => it != null); return updatePinnedMessages(cid, messages.toList(growable: false)); }).toList(growable: false); final updateReadsFuture = channelStates.map((it) { - final cid = it.channel.cid; + final cid = it.channel!.cid; final reads = it.read?.where((it) => it != null) ?? []; return updateReads(cid, reads.toList(growable: false)); }).toList(growable: false); final updateMembersFuture = channelStates.map((it) { - final cid = it.channel.cid; - final members = it.members.where((it) => it != null); + final cid = it.channel!.cid; + final members = it.members!.where((it) => it != null); return updateMembers(cid, members.toList(growable: false)); }).toList(growable: false); diff --git a/packages/stream_chat/lib/src/exceptions.dart b/packages/stream_chat/lib/src/exceptions.dart index 60dd6bdf..714bb386 100644 --- a/packages/stream_chat/lib/src/exceptions.dart +++ b/packages/stream_chat/lib/src/exceptions.dart @@ -4,25 +4,25 @@ import 'dart:convert'; 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']; + if (jsonData != null && jsonData!.containsKey('code')) { + _code = jsonData!['code']; } } /// Raw body of the response - final String body; + final String? body; /// Json parsed body - final Map jsonData; + final Map? jsonData; /// Http status code of the response - final int status; + final int? status; /// Stream specific error code - int get code => _code; - int _code; + int? get code => _code; + int? _code; - static Map _decode(String body) { + static Map? _decode(String? body) { try { if (body == null) { return null; diff --git a/packages/stream_chat/lib/src/extensions/map_extension.dart b/packages/stream_chat/lib/src/extensions/map_extension.dart index 1a3376ad..af57a35a 100644 --- a/packages/stream_chat/lib/src/extensions/map_extension.dart +++ b/packages/stream_chat/lib/src/extensions/map_extension.dart @@ -1,6 +1,6 @@ /// Useful extension functions for [Map] extension MapX on Map { /// Returns a new map with null keys or values removed - Map get nullProtected => - {...this}..removeWhere((key, value) => key == null || value == null); + Map get nullProtected => {...this as Map} + ..removeWhere((key, value) => key == null || value == null); } diff --git a/packages/stream_chat/lib/src/extensions/rate_limit.dart b/packages/stream_chat/lib/src/extensions/rate_limit.dart index c9f5934f..8ed91481 100644 --- a/packages/stream_chat/lib/src/extensions/rate_limit.dart +++ b/packages/stream_chat/lib/src/extensions/rate_limit.dart @@ -10,7 +10,7 @@ extension RateLimit on Function { Duration wait, { bool leading = false, bool trailing = true, - Duration maxWait, + Duration? maxWait, }) => Debounce( this, @@ -40,7 +40,7 @@ Debounce debounce( Duration wait, { bool leading = false, bool trailing = true, - Duration maxWait, + Duration? maxWait, }) => Debounce( func, @@ -121,13 +121,13 @@ class Debounce { Duration wait, { bool leading = false, bool trailing = true, - Duration maxWait, + Duration? maxWait, }) : _leading = leading, _trailing = trailing, _wait = wait?.inMilliseconds ?? 0, _maxing = maxWait != null { if (_maxing) { - _maxWait = math.max(maxWait.inMilliseconds, _wait); + _maxWait = math.max(maxWait!.inMilliseconds, _wait); } } @@ -137,15 +137,15 @@ class Debounce { final int _wait; final bool _maxing; - int _maxWait; - List _lastArgs; - Map _lastNamedArgs; - Timer _timer; - int _lastCallTime; - Object _result; - int _lastInvokeTime = 0; + late int _maxWait; + List? _lastArgs; + Map? _lastNamedArgs; + Timer? _timer; + int? _lastCallTime; + Object? _result; + int? _lastInvokeTime = 0; - Object _invokeFunc(int time) { + Object? _invokeFunc(int? time) { final args = _lastArgs; final namedArgs = _lastNamedArgs; _lastArgs = _lastNamedArgs = null; @@ -154,11 +154,11 @@ class Debounce { } Timer _startTimer(Function pendingFunc, int wait) => - Timer(Duration(milliseconds: wait), pendingFunc); + Timer(Duration(milliseconds: wait), pendingFunc as void Function()); bool _shouldInvoke(int time) { final timeSinceLastCall = time - (_lastCallTime ?? double.nan); - final timeSinceLastInvoke = time - _lastInvokeTime; + final timeSinceLastInvoke = time - _lastInvokeTime!; // Either this is the first call, activity has stopped and we're at the // trailing edge, the system time has gone backwards and we're treating @@ -169,7 +169,7 @@ class Debounce { (_maxing && timeSinceLastInvoke >= _maxWait); } - Object _trailingEdge(int time) { + Object? _trailingEdge(int time) { _timer = null; // Only invoke if we have `lastArgs` which means `func` has been @@ -182,8 +182,8 @@ class Debounce { } int _remainingWait(int time) { - final timeSinceLastCall = time - _lastCallTime; - final timeSinceLastInvoke = time - _lastInvokeTime; + final timeSinceLastCall = time - _lastCallTime!; + final timeSinceLastInvoke = time - _lastInvokeTime!; final timeWaiting = _wait - timeSinceLastCall; return _maxing @@ -201,7 +201,7 @@ class Debounce { } } - Object _leadingEdge(int time) { + Object? _leadingEdge(int? time) { // Reset any `maxWait` timer. _lastInvokeTime = time; // Start the timer for the trailing edge. @@ -218,7 +218,7 @@ class Debounce { } /// Immediately invokes all the remaining delayed functions. - Object flush() { + Object? flush() { final now = DateTime.now().millisecondsSinceEpoch; return _timer == null ? _result : _trailingEdge(now); } @@ -228,15 +228,15 @@ class Debounce { /// Calls/invokes this class like a function. /// Pass [args] and [namedArgs] to be used while invoking [_func]. - Object call( + Object? call( List args, { - Map namedArgs, + Map? namedArgs, }) { final time = DateTime.now().millisecondsSinceEpoch; final isInvoking = _shouldInvoke(time); _lastArgs = args; - _lastNamedArgs = namedArgs; + _lastNamedArgs = namedArgs as Map?; _lastCallTime = time; if (isInvoking) { @@ -323,13 +323,13 @@ class Throttle { void cancel() => _debounce.cancel(); /// Immediately invokes all the remaining delayed functions. - Object flush() => _debounce.flush(); + Object? flush() => _debounce.flush(); /// True if there are functions remaining to get invoked. bool get isPending => _debounce.isPending; /// Calls/invokes this class like a function. /// Pass [args] and [namedArgs] to be used while invoking `func`. - Object call(List args, {Map namedArgs}) => + Object? call(List args, {Map? namedArgs}) => _debounce.call(args, namedArgs: namedArgs); } diff --git a/packages/stream_chat/lib/src/extensions/string_extension.dart b/packages/stream_chat/lib/src/extensions/string_extension.dart index 49949412..e87eea7e 100644 --- a/packages/stream_chat/lib/src/extensions/string_extension.dart +++ b/packages/stream_chat/lib/src/extensions/string_extension.dart @@ -2,14 +2,14 @@ import 'package:http_parser/http_parser.dart' as http_parser; import 'package:mime/mime.dart'; /// Useful extension functions for [String] -extension StringX on String { +extension StringX on String? { /// Returns the mime type from the passed file name. - http_parser.MediaType get mimeType { + http_parser.MediaType? get mimeType { if (this == null) return null; - if (toLowerCase().endsWith('heic')) { + if (this!.toLowerCase().endsWith('heic')) { return http_parser.MediaType.parse('image/heic'); } else { - return http_parser.MediaType.parse(lookupMimeType(this)); + return http_parser.MediaType.parse(lookupMimeType(this!)!); } } } diff --git a/packages/stream_chat/lib/src/models/action.dart b/packages/stream_chat/lib/src/models/action.dart index 62d0f104..b91f1e13 100644 --- a/packages/stream_chat/lib/src/models/action.dart +++ b/packages/stream_chat/lib/src/models/action.dart @@ -12,19 +12,19 @@ class Action { factory Action.fromJson(Map json) => _$ActionFromJson(json); /// The name of the action - final String name; + final String? name; /// The style of the action - final String style; + final String? style; /// The test of the action - final String text; + final String? text; /// The type of the action - final String type; + final String? type; /// The value of the action - final String value; + final String? value; /// 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 551d70e4..ae169766 100644 --- a/packages/stream_chat/lib/src/models/attachment.dart +++ b/packages/stream_chat/lib/src/models/attachment.dart @@ -13,10 +13,10 @@ part 'attachment.g.dart'; class Attachment { /// Constructor used for json serialization Attachment({ - String id, + String? id, this.type, this.titleLink, - String title, + String? title, this.thumbUrl, this.text, this.pretext, @@ -34,10 +34,10 @@ class Attachment { this.actions, this.extraData, this.file, - UploadState uploadState, + UploadState? uploadState, }) : id = id ?? const Uuid().v4(), title = title ?? file?.name, - localUri = file?.path != null ? Uri.parse(file.path) : null { + localUri = file?.path != null ? Uri.parse(file!.path!) : null { this.uploadState = uploadState ?? ((assetUrl != null || imageUrl != null) ? const UploadState.success() @@ -47,68 +47,68 @@ class Attachment { /// Create a new instance from a json factory Attachment.fromJson(Map json) => _$AttachmentFromJson( - Serialization.moveToExtraDataFromRoot(json, topLevelFields)); + Serialization.moveToExtraDataFromRoot(json, topLevelFields)!); /// Create a new instance from a db data factory Attachment.fromData(Map json) => _$AttachmentFromJson(Serialization.moveToExtraDataFromRoot( - json, topLevelFields + dbSpecificTopLevelFields)); + json, topLevelFields + dbSpecificTopLevelFields)!); ///The attachment type based on the URL resource. This can be: audio, ///image or video - final String type; + final String? type; ///The link to which the attachment message points to. - final String titleLink; + final String? titleLink; /// The attachment title - final String title; + final String? title; /// The URL to the attached file thumbnail. You can use this to represent the /// attached link. - final String thumbUrl; + final String? thumbUrl; /// The attachment text. It will be displayed in the channel next to the /// original message. - final String text; + final String? text; /// Optional text that appears above the attachment block - final String pretext; + final String? pretext; /// The original URL that was used to scrape this attachment. - final String ogScrapeUrl; + final String? ogScrapeUrl; /// The URL to the attached image. This is present for URL pointing to an /// image article (eg. Unsplash) - final String imageUrl; - final String footerIcon; - final String footer; + final String? imageUrl; + final String? footerIcon; + final String? footer; final dynamic fields; - final String fallback; - final String color; + final String? fallback; + final String? color; /// The name of the author. - final String authorName; - final String authorLink; - final String authorIcon; + final String? authorName; + final String? authorLink; + final String? authorIcon; /// The URL to the audio, video or image related to the URL. - final String assetUrl; + final String? assetUrl; /// Actions from a command - final List actions; + final List? actions; - final Uri localUri; + final Uri? localUri; /// The file present inside this attachment. - final AttachmentFile file; + final AttachmentFile? file; /// The current upload state of the attachment - UploadState uploadState; + UploadState? uploadState; /// Map of custom channel extraData @JsonKey(includeIfNull: false) - final Map extraData; + final Map? extraData; /// The attachment ID. /// @@ -156,28 +156,28 @@ class Attachment { _$AttachmentToJson(this), topLevelFields + dbSpecificTopLevelFields); Attachment copyWith({ - String id, - String type, - String titleLink, - String title, - String thumbUrl, - String text, - String pretext, - String ogScrapeUrl, - String imageUrl, - String footerIcon, - String footer, + String? id, + String? type, + String? titleLink, + String? title, + String? thumbUrl, + String? text, + String? pretext, + String? ogScrapeUrl, + String? imageUrl, + String? footerIcon, + String? footer, dynamic fields, - String fallback, - String color, - String authorName, - String authorLink, - String authorIcon, - String assetUrl, - List actions, - AttachmentFile file, - UploadState uploadState, - Map extraData, + String? fallback, + String? color, + String? authorName, + String? authorLink, + String? authorIcon, + String? assetUrl, + List? actions, + AttachmentFile? file, + UploadState? uploadState, + Map? extraData, }) => Attachment( id: id ?? this.id, diff --git a/packages/stream_chat/lib/src/models/attachment_file.dart b/packages/stream_chat/lib/src/models/attachment_file.dart index bae7810f..46208e28 100644 --- a/packages/stream_chat/lib/src/models/attachment_file.dart +++ b/packages/stream_chat/lib/src/models/attachment_file.dart @@ -19,7 +19,7 @@ abstract class UploadState with _$UploadState { const factory UploadState.success() = Success; /// Failed state of the union - const factory UploadState.failed({@required String error}) = Failed; + const factory UploadState.failed({required String error}) = Failed; /// Creates a new instance from a json factory UploadState.fromJson(Map json) => @@ -27,7 +27,7 @@ abstract class UploadState with _$UploadState { } /// Helper extension for UploadState -extension UploadStateX on UploadState { +extension UploadStateX on UploadState? { /// Returns true if state is [Preparing] bool get isPreparing => this is Preparing; @@ -65,21 +65,21 @@ class AttachmentFile { /// ``` /// final File myFile = File(platformFile.path); /// ``` - final String path; + final String? path; /// File name including its extension. - final String name; + final String? name; /// Byte data for this file. Particularly useful if you want to manipulate /// its data or easily upload to somewhere else. @JsonKey(toJson: _toString, fromJson: _fromString) - final Uint8List bytes; + final Uint8List? bytes; /// The file size in bytes. - final int size; + final int? size; /// File extension for this file. - String get extension => name?.split('.')?.last; + String? get extension => name?.split('.')?.last; /// 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 3541573e..1500aaf2 100644 --- a/packages/stream_chat/lib/src/models/channel_config.dart +++ b/packages/stream_chat/lib/src/models/channel_config.dart @@ -30,52 +30,52 @@ class ChannelConfig { _$ChannelConfigFromJson(json); /// Moderation configuration - final String automod; + final String? automod; /// List of available commands - final List commands; + final List? commands; /// True if the channel should send connect events - final bool connectEvents; + final bool? connectEvents; /// Date of channel creation - final DateTime createdAt; + final DateTime? createdAt; /// Date of last channel update - final DateTime updatedAt; + final DateTime? updatedAt; /// Max channel message length - final int maxMessageLength; + final int? maxMessageLength; /// Duration of message retention - final String messageRetention; + final String? messageRetention; /// True if users can be muted - final bool mutes; + final bool? mutes; /// Name of the channel - final String name; + final String? name; /// True if reaction are active for this channel - final bool reactions; + final bool? reactions; /// True if readEvents are active for this channel - final bool readEvents; + final bool? readEvents; /// True if reply message are active for this channel - final bool replies; + final bool? replies; /// True if it's possible to perform a search in this channel - final bool search; + final bool? search; /// True if typing events should be sent for this channel - final bool typingEvents; + final bool? typingEvents; /// True if it's possible to upload files to this channel - final bool uploads; + final bool? uploads; /// True if urls appears as attachments - final bool urlEnrichment; + final bool? urlEnrichment; /// 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 62ea1853..827f1246 100644 --- a/packages/stream_chat/lib/src/models/channel_model.dart +++ b/packages/stream_chat/lib/src/models/channel_model.dart @@ -26,59 +26,59 @@ class ChannelModel { }); /// Create a new instance from a json - factory ChannelModel.fromJson(Map json) => + factory ChannelModel.fromJson(Map? json) => _$ChannelModelFromJson( - Serialization.moveToExtraDataFromRoot(json, topLevelFields)); + Serialization.moveToExtraDataFromRoot(json, topLevelFields)!); /// The id of this channel - final String id; + final String? id; /// The type of this channel - final String type; + final String? type; /// The cid of this channel @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final String cid; + final String? cid; /// The channel configuration data @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final ChannelConfig config; + final ChannelConfig? config; /// The user that created this channel @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final User createdBy; + final User? createdBy; /// True if this channel is frozen @JsonKey(includeIfNull: false) - final bool frozen; + final bool? frozen; /// The date of the last message @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final DateTime lastMessageAt; + final DateTime? lastMessageAt; /// The date of channel creation @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final DateTime createdAt; + final DateTime? createdAt; /// The date of the last channel update @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final DateTime updatedAt; + final DateTime? updatedAt; /// The date of channel deletion @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final DateTime deletedAt; + final DateTime? deletedAt; /// The count of this channel members @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final int memberCount; + final int? memberCount; /// Map of custom channel extraData @JsonKey(includeIfNull: false) - final Map extraData; + final Map? extraData; /// The team the channel belongs to @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final String team; + final String? team; /// Known top level fields. /// Useful for [Serialization] methods. @@ -98,8 +98,8 @@ class ChannelModel { ]; /// Shortcut for channel name - String get name => - extraData?.containsKey('name') == true ? extraData['name'] : cid; + String? get name => + extraData?.containsKey('name') == true ? extraData!['name'] : cid; /// Serialize to json Map toJson() => Serialization.moveFromExtraDataToRoot( @@ -109,19 +109,19 @@ class ChannelModel { /// Creates a copy of [ChannelModel] with specified attributes overridden. ChannelModel copyWith({ - String id, - String type, - String cid, - ChannelConfig config, - User createdBy, - bool frozen, - DateTime lastMessageAt, - DateTime createdAt, - DateTime updatedAt, - DateTime deletedAt, - int memberCount, - Map extraData, - String team, + String? id, + String? type, + String? cid, + ChannelConfig? config, + User? createdBy, + bool? frozen, + DateTime? lastMessageAt, + DateTime? createdAt, + DateTime? updatedAt, + DateTime? deletedAt, + int? memberCount, + Map? extraData, + String? team, }) => ChannelModel( id: id ?? this.id, @@ -141,7 +141,7 @@ class ChannelModel { /// Returns a new [ChannelModel] that is a combination of this channelModel /// and the given [other] channelModel. - ChannelModel merge(ChannelModel other) { + ChannelModel merge(ChannelModel? other) { if (other == null) return this; return copyWith( id: other.id, diff --git a/packages/stream_chat/lib/src/models/channel_state.dart b/packages/stream_chat/lib/src/models/channel_state.dart index 3a1f3178..64c88af7 100644 --- a/packages/stream_chat/lib/src/models/channel_state.dart +++ b/packages/stream_chat/lib/src/models/channel_state.dart @@ -22,42 +22,42 @@ class ChannelState { }); /// The channel to which this state belongs - final ChannelModel channel; + final ChannelModel? channel; /// A paginated list of channel messages - final List messages; + final List? messages; /// A paginated list of channel members - final List members; + final List? members; /// A paginated list of pinned messages - final List pinnedMessages; + final List? pinnedMessages; /// The count of users watching the channel - final int watcherCount; + final int? watcherCount; /// A paginated list of users watching the channel - final List watchers; + final List? watchers; /// The list of channel reads - final List read; + final List? read; /// Create a new instance from a json - static ChannelState fromJson(Map json) => - _$ChannelStateFromJson(json); + static ChannelState fromJson(Map? json) => + _$ChannelStateFromJson(json!); /// Serialize to json Map toJson() => _$ChannelStateToJson(this); /// Creates a copy of [ChannelState] with specified attributes overridden. ChannelState copyWith({ - ChannelModel channel, - List messages, - List members, - List pinnedMessages, - int watcherCount, - List watchers, - List read, + ChannelModel? channel, + List? messages, + List? members, + List? pinnedMessages, + int? watcherCount, + List? watchers, + List? read, }) => ChannelState( channel: channel ?? this.channel, diff --git a/packages/stream_chat/lib/src/models/command.dart b/packages/stream_chat/lib/src/models/command.dart index a5ababd2..c8df72fd 100644 --- a/packages/stream_chat/lib/src/models/command.dart +++ b/packages/stream_chat/lib/src/models/command.dart @@ -17,13 +17,13 @@ class Command { _$CommandFromJson(json); /// The name of the command - final String name; + final String? name; /// The description explaining the command - final String description; + final String? description; /// The arguments of the command - final String args; + 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 150e6759..28481282 100644 --- a/packages/stream_chat/lib/src/models/device.dart +++ b/packages/stream_chat/lib/src/models/device.dart @@ -15,10 +15,10 @@ class Device { factory Device.fromJson(Map json) => _$DeviceFromJson(json); /// The id of the device - final String id; + final String? id; /// The notification push provider - final String pushProvider; + 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 28cf1b38..ab97d86f 100644 --- a/packages/stream_chat/lib/src/models/event.dart +++ b/packages/stream_chat/lib/src/models/event.dart @@ -31,68 +31,68 @@ class Event { }) : isLocal = true; /// Create a new instance from a json - factory Event.fromJson(Map 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; + final String? type; /// The channel cid to which the event belongs - final String cid; + final String? cid; /// The channel id to which the event belongs - final String channelId; + final String? channelId; /// The channel type to which the event belongs - final String channelType; + final String? channelType; /// The connection id in which the event has been sent - final String connectionId; + final String? connectionId; /// The date of creation of the event - final DateTime createdAt; + final DateTime? createdAt; /// User object of the health check user - final OwnUser me; + final OwnUser? me; /// User object of the current user - final User user; + final User? user; /// The message sent with the event - final Message message; + final Message? message; /// The channel sent with the event - final EventChannel channel; + final EventChannel? channel; /// The member sent with the event - final Member member; + final Member? member; /// The reaction sent with the event - final Reaction reaction; + final Reaction? reaction; /// The number of unread messages for current user - final int totalUnreadCount; + final int? totalUnreadCount; /// User total unread channels - final int unreadChannels; + final int? unreadChannels; /// Online status - final bool online; + final bool? online; /// The id of the parent message of a thread - final String parentId; + final String? parentId; /// True if the event is generated by this client - bool isLocal; + bool? isLocal; /// Map of custom channel extraData @JsonKey(includeIfNull: false) - final Map extraData; + final Map? extraData; /// Known top level fields. /// Useful for [Serialization] methods. @@ -124,23 +124,23 @@ class Event { /// Creates a copy of [Event] with specified attributes overridden. Event copyWith({ - String type, - String cid, - String channelId, - String channelType, - String connectionId, - DateTime createdAt, - OwnUser me, - User user, - Message message, - EventChannel channel, - Member member, - Reaction reaction, - int totalUnreadCount, - int unreadChannels, - bool online, - String parentId, - Map extraData, + String? type, + String? cid, + String? channelId, + String? channelType, + String? connectionId, + DateTime? createdAt, + OwnUser? me, + User? user, + Message? message, + EventChannel? channel, + Member? member, + Reaction? reaction, + int? totalUnreadCount, + int? unreadChannels, + bool? online, + String? parentId, + Map? extraData, }) => Event( type: type ?? this.type, @@ -169,18 +169,18 @@ class EventChannel extends ChannelModel { /// Constructor used for json serialization EventChannel({ this.members, - String id, - String type, - String cid, - ChannelConfig config, - User createdBy, - bool frozen, - DateTime lastMessageAt, - DateTime createdAt, - DateTime updatedAt, - DateTime deletedAt, - int memberCount, - Map extraData, + String? id, + String? type, + String? cid, + ChannelConfig? config, + User? createdBy, + bool? frozen, + DateTime? lastMessageAt, + DateTime? createdAt, + DateTime? updatedAt, + DateTime? deletedAt, + int? memberCount, + Map? extraData, }) : super( id: id, type: type, @@ -197,14 +197,14 @@ class EventChannel extends ChannelModel { ); /// Create a new instance from a json - factory EventChannel.fromJson(Map json) => + factory EventChannel.fromJson(Map? json) => _$EventChannelFromJson(Serialization.moveToExtraDataFromRoot( json, topLevelFields, - )); + )!); /// A paginated list of channel members - final List members; + final List? members; /// Known top level fields. /// Useful for [Serialization] methods. diff --git a/packages/stream_chat/lib/src/models/member.dart b/packages/stream_chat/lib/src/models/member.dart index e99d8d34..bd2bed8a 100644 --- a/packages/stream_chat/lib/src/models/member.dart +++ b/packages/stream_chat/lib/src/models/member.dart @@ -31,51 +31,51 @@ class Member { } /// The interested user - final User user; + final User? user; /// The date in which the user accepted the invite to the channel - final DateTime inviteAcceptedAt; + final DateTime? inviteAcceptedAt; /// The date in which the user rejected the invite to the channel - final DateTime inviteRejectedAt; + final DateTime? inviteRejectedAt; /// True if the user has been invited to the channel - final bool invited; + final bool? invited; /// The role of the user in the channel - final String role; + final String? role; /// The id of the interested user - final String userId; + final String? userId; /// True if the user is a moderator of the channel - final bool isModerator; + final bool? isModerator; /// True if the member is banned from the channel - final bool banned; + final bool? banned; /// True if the member is shadow banned from the channel - final bool shadowBanned; + final bool? shadowBanned; /// The date of creation - final DateTime createdAt; + final DateTime? createdAt; /// The last date of update - final DateTime updatedAt; + final DateTime? updatedAt; /// Creates a copy of [Member] with specified attributes overridden. Member copyWith({ - User user, - DateTime inviteAcceptedAt, - DateTime inviteRejectedAt, - bool invited, - String role, - String userId, - bool isModerator, - DateTime createdAt, - DateTime updatedAt, - bool banned, - bool shadowBanned, + User? user, + DateTime? inviteAcceptedAt, + DateTime? inviteRejectedAt, + bool? invited, + String? role, + String? userId, + bool? isModerator, + DateTime? createdAt, + DateTime? updatedAt, + bool? banned, + bool? shadowBanned, }) => Member( user: user ?? this.user, diff --git a/packages/stream_chat/lib/src/models/message.dart b/packages/stream_chat/lib/src/models/message.dart index de441dff..0df5a7ca 100644 --- a/packages/stream_chat/lib/src/models/message.dart +++ b/packages/stream_chat/lib/src/models/message.dart @@ -44,7 +44,7 @@ enum MessageSendingStatus { class Message { /// Constructor used for json serialization Message({ - String id, + String? id, this.text, this.type, this.attachments, @@ -67,7 +67,7 @@ class Message { this.user, this.pinned = false, this.pinnedAt, - DateTime pinExpires, + DateTime? pinExpires, this.pinnedBy, this.extraData, this.deletedAt, @@ -77,15 +77,15 @@ class Message { pinExpires = pinExpires?.toUtc(); /// Create a new instance from a json - factory Message.fromJson(Map json) => _$MessageFromJson( - Serialization.moveToExtraDataFromRoot(json, topLevelFields)); + 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 - final String text; + final String? text; /// The status of a sending message @JsonKey(ignore: true) @@ -93,99 +93,99 @@ class Message { /// The message type @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final String type; + final String? type; /// The list of attachments, either provided by the user or generated from a /// command or as a result of URL scraping. @JsonKey(includeIfNull: false) - final List attachments; + final List? attachments; /// The list of user mentioned in the message @JsonKey(toJson: Serialization.userIds) - final List mentionedUsers; + final List? mentionedUsers; /// A map describing the count of number of every reaction @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final Map reactionCounts; + final Map? reactionCounts; /// A map describing the count of score of every reaction @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final Map reactionScores; + final Map? reactionScores; /// The latest reactions to the message created by any user. @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final List latestReactions; + final List? latestReactions; /// The reactions added to the message by the current user. @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final List ownReactions; + final List? ownReactions; /// The ID of the parent message, if the message is a thread reply. - final String parentId; + final String? parentId; /// A quoted reply message @JsonKey(toJson: Serialization.readOnly) - final Message quotedMessage; + final Message? quotedMessage; /// The ID of the quoted message, if the message is a quoted reply. - final String quotedMessageId; + final String? quotedMessageId; /// Reserved field indicating the number of replies for this message. @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final int replyCount; + final int? replyCount; /// Reserved field indicating the thread participants for this message. @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final List threadParticipants; + final List? threadParticipants; /// Check if this message needs to show in the channel. - final bool showInChannel; + final bool? showInChannel; /// If true the message is silent - final bool silent; + final bool? silent; /// If true the message will not send a push notification - final bool skipPush; + final bool? skipPush; /// If true the message is shadowed @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final bool shadowed; + final bool? shadowed; /// A used command name. @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final String command; + final String? command; /// Reserved field indicating when the message was created. @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final DateTime createdAt; + final DateTime? createdAt; /// Reserved field indicating when the message was updated last time. @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final DateTime updatedAt; + final DateTime? updatedAt; /// User who sent the message @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final User user; + final User? user; /// If true the message is pinned - final bool pinned; + final bool? pinned; /// Reserved field indicating when the message was pinned @JsonKey(toJson: Serialization.readOnly) - final DateTime pinnedAt; + final DateTime? pinnedAt; /// Reserved field indicating when the message will expire /// /// if `null` message has no expiry - final DateTime pinExpires; + final DateTime? pinExpires; /// Reserved field indicating who pinned the message @JsonKey(toJson: Serialization.readOnly) - final User pinnedBy; + final User? pinnedBy; /// Message custom extraData @JsonKey(includeIfNull: false) - final Map extraData; + final Map? extraData; /// True if the message is a system info bool get isSystem => type == 'system'; @@ -198,7 +198,7 @@ class Message { /// Reserved field indicating when the message was deleted. @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final DateTime deletedAt; + final DateTime? deletedAt; /// Known top level fields. /// Useful for [Serialization] methods. @@ -239,35 +239,35 @@ class Message { /// Creates a copy of [Message] with specified attributes overridden. Message copyWith({ - String id, - String text, - String type, - List attachments, - List mentionedUsers, - Map reactionCounts, - Map reactionScores, - List latestReactions, - List ownReactions, - String parentId, - Message quotedMessage, - String quotedMessageId, - int replyCount, - List threadParticipants, - bool showInChannel, - bool shadowed, - bool silent, - String command, - DateTime createdAt, - DateTime updatedAt, - DateTime deletedAt, - User user, - bool pinned, - DateTime pinnedAt, - Object pinExpires = _pinExpires, - User pinnedBy, - Map extraData, - MessageSendingStatus status, - bool skipPush, + String? id, + String? text, + String? type, + List? attachments, + List? mentionedUsers, + Map? reactionCounts, + Map? reactionScores, + List? latestReactions, + List? ownReactions, + String? parentId, + Message? quotedMessage, + String? quotedMessageId, + int? replyCount, + List? threadParticipants, + bool? showInChannel, + bool? shadowed, + bool? silent, + String? command, + DateTime? createdAt, + DateTime? updatedAt, + DateTime? deletedAt, + User? user, + bool? pinned, + DateTime? pinnedAt, + Object? pinExpires = _pinExpires, + User? pinnedBy, + Map? extraData, + MessageSendingStatus? status, + bool? skipPush, }) { assert(() { if (pinExpires is! DateTime && @@ -305,7 +305,8 @@ class Message { pinned: pinned ?? this.pinned, pinnedAt: pinnedAt ?? this.pinnedAt, pinnedBy: pinnedBy ?? this.pinnedBy, - pinExpires: pinExpires == _pinExpires ? this.pinExpires : pinExpires, + pinExpires: + pinExpires == _pinExpires ? this.pinExpires : pinExpires as DateTime?, skipPush: skipPush ?? this.skipPush, ); } @@ -355,13 +356,13 @@ class TranslatedMessage extends Message { TranslatedMessage(this.i18n); /// Create a new instance from a json - factory TranslatedMessage.fromJson(Map json) => + factory TranslatedMessage.fromJson(Map? json) => _$TranslatedMessageFromJson( - Serialization.moveToExtraDataFromRoot(json, topLevelFields), + Serialization.moveToExtraDataFromRoot(json, topLevelFields)!, ); /// A Map of - final Map i18n; + final Map? i18n; /// Known top level fields. /// Useful for [Serialization] methods. diff --git a/packages/stream_chat/lib/src/models/mute.dart b/packages/stream_chat/lib/src/models/mute.dart index e3d5e1a0..65300d4a 100644 --- a/packages/stream_chat/lib/src/models/mute.dart +++ b/packages/stream_chat/lib/src/models/mute.dart @@ -16,19 +16,19 @@ class Mute { /// The user that performed the muting action @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final User user; + final User? user; /// The target user @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final ChannelModel channel; + final ChannelModel? channel; /// The date in which the use was muted @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final DateTime createdAt; + final DateTime? createdAt; /// The date of the last update @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final DateTime updatedAt; + final DateTime? updatedAt; /// 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 18ee2abf..041c4e95 100644 --- a/packages/stream_chat/lib/src/models/own_user.dart +++ b/packages/stream_chat/lib/src/models/own_user.dart @@ -17,14 +17,14 @@ class OwnUser extends User { this.totalUnreadCount, this.unreadChannels, this.channelMutes, - String id, - String role, - DateTime createdAt, - DateTime updatedAt, - DateTime lastActive, - bool online, - Map extraData, - bool banned, + String? id, + String? role, + DateTime? createdAt, + DateTime? updatedAt, + DateTime? lastActive, + bool? online, + Map? extraData, + bool? banned, }) : super( id: id, role: role, @@ -37,28 +37,28 @@ class OwnUser extends User { ); /// Create a new instance from a json - factory OwnUser.fromJson(Map json) => _$OwnUserFromJson( - Serialization.moveToExtraDataFromRoot(json, topLevelFields)); + factory OwnUser.fromJson(Map? json) => _$OwnUserFromJson( + Serialization.moveToExtraDataFromRoot(json, topLevelFields)!); /// List of user devices @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final List devices; + final List? devices; /// List of users muted by the user @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final List mutes; + final List? mutes; /// List of users muted by the user @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final List channelMutes; + final List? channelMutes; /// Total unread messages by the user @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final int totalUnreadCount; + final int? totalUnreadCount; /// Total unread channels by the user @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final int unreadChannels; + final int? unreadChannels; /// Known top level fields. /// Useful for [Serialization] methods. diff --git a/packages/stream_chat/lib/src/models/reaction.dart b/packages/stream_chat/lib/src/models/reaction.dart index 6792bf3f..42c23841 100644 --- a/packages/stream_chat/lib/src/models/reaction.dart +++ b/packages/stream_chat/lib/src/models/reaction.dart @@ -13,39 +13,39 @@ class Reaction { this.createdAt, this.type, this.user, - String userId, + 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)); + factory Reaction.fromJson(Map? json) => _$ReactionFromJson( + Serialization.moveToExtraDataFromRoot(json, topLevelFields)!); /// The messageId to which the reaction belongs - final String messageId; + final String? messageId; /// The type of the reaction - final String type; + final String? type; /// The date of the reaction @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final DateTime createdAt; + final DateTime? createdAt; /// The user that sent the reaction @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final User user; + final User? user; /// The score of the reaction (ie. number of reactions sent) - final int score; + final int? score; /// The userId that sent the reaction @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final String userId; + final String? userId; /// Reaction custom extraData @JsonKey(includeIfNull: false) - final Map extraData; + final Map? extraData; /// Map of custom user extraData static const topLevelFields = [ @@ -63,13 +63,13 @@ class Reaction { /// Creates a copy of [Reaction] with specified attributes overridden. Reaction copyWith({ - String messageId, - DateTime createdAt, - String type, - User user, - String userId, - int score, - Map extraData, + String? messageId, + DateTime? createdAt, + String? type, + User? user, + String? userId, + int? score, + Map? extraData, }) => Reaction( messageId: messageId ?? this.messageId, diff --git a/packages/stream_chat/lib/src/models/read.dart b/packages/stream_chat/lib/src/models/read.dart index fc8ef1bc..87b3dc89 100644 --- a/packages/stream_chat/lib/src/models/read.dart +++ b/packages/stream_chat/lib/src/models/read.dart @@ -17,22 +17,22 @@ class Read { factory Read.fromJson(Map json) => _$ReadFromJson(json); /// Date of the read event - final DateTime lastRead; + final DateTime? lastRead; /// User who sent the event - final User user; + final User? user; /// Number of unread messages - final int unreadMessages; + final int? unreadMessages; /// Serialize to json Map toJson() => _$ReadToJson(this); /// Creates a copy of [Read] with specified attributes overridden. Read copyWith({ - DateTime lastRead, - User user, - int unreadMessages, + DateTime? lastRead, + User? user, + int? unreadMessages, }) => Read( lastRead: lastRead ?? this.lastRead, diff --git a/packages/stream_chat/lib/src/models/serialization.dart b/packages/stream_chat/lib/src/models/serialization.dart index 18cdd545..48726d5e 100644 --- a/packages/stream_chat/lib/src/models/serialization.dart +++ b/packages/stream_chat/lib/src/models/serialization.dart @@ -10,12 +10,12 @@ class Serialization { static const Function readOnly = readonly; /// List of users to list of userIds - static List userIds(List users) => + 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( - Map json, + static Map? moveToExtraDataFromRoot( + Map? json, List topLevelFields, ) { if (json == null) return null; diff --git a/packages/stream_chat/lib/src/models/user.dart b/packages/stream_chat/lib/src/models/user.dart index 3b9f9add..97411a3e 100644 --- a/packages/stream_chat/lib/src/models/user.dart +++ b/packages/stream_chat/lib/src/models/user.dart @@ -20,8 +20,8 @@ class User { }); /// Create a new instance from a json - factory User.fromJson(Map json) => _$UserFromJson( - Serialization.moveToExtraDataFromRoot(json, topLevelFields)); + factory User.fromJson(Map? json) => _$UserFromJson( + Serialization.moveToExtraDataFromRoot(json, topLevelFields)!); /// Use this named constructor to create a new user instance User.init( @@ -49,47 +49,47 @@ class User { ]; /// User id - final String id; + final String? id; /// User role @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final String role; + final String? role; /// User role @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final List teams; + final List? teams; /// Date of user creation @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final DateTime createdAt; + final DateTime? createdAt; /// Date of last user update @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final DateTime updatedAt; + final DateTime? updatedAt; /// Date of last user connection @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final DateTime lastActive; + final DateTime? lastActive; /// True if user is online @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final bool online; + final bool? online; /// True if user is banned from the chat @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final bool banned; + final bool? banned; /// Map of custom user extraData @JsonKey(includeIfNull: false) - final Map extraData; + final Map? extraData; @override int get hashCode => id.hashCode; /// Shortcut for user name - String get name => - (extraData?.containsKey('name') == true && extraData['name'] != '') - ? extraData['name'] + String? get name => + (extraData?.containsKey('name') == true && extraData!['name'] != '') + ? extraData!['name'] : id; @override @@ -103,15 +103,15 @@ class User { /// Creates a copy of [User] with specified attributes overridden. User copyWith({ - String id, - String role, - DateTime createdAt, - DateTime updatedAt, - DateTime lastActive, - bool online, - Map extraData, - bool banned, - List teams, + String? id, + String? role, + DateTime? createdAt, + DateTime? updatedAt, + DateTime? lastActive, + bool? online, + Map? extraData, + bool? banned, + List? teams, }) => User( id: id ?? this.id, diff --git a/packages/stream_chat/pubspec.yaml b/packages/stream_chat/pubspec.yaml index 74310a86..defc8db5 100644 --- a/packages/stream_chat/pubspec.yaml +++ b/packages/stream_chat/pubspec.yaml @@ -6,25 +6,25 @@ repository: https://github.com/GetStream/stream-chat-flutter issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues environment: - sdk: ">=2.7.0 <3.0.0" + sdk: '>=2.12.0 <3.0.0' dependencies: async: ^2.5.0 collection: ^1.15.0 - dio: ">=4.0.0-prev3 <4.0.0" - freezed_annotation: ^0.14.0 + dio: ^4.0.0 + freezed_annotation: ^0.14.1 http_parser: ^4.0.0 - json_annotation: ^4.0.0 - logging: ^1.0.0 + json_annotation: ^4.0.1 + logging: ^1.0.1 meta: ^1.3.0 mime: ^1.0.0 rxdart: ^0.26.0 - uuid: ^3.0.0 + uuid: ^3.0.4 web_socket_channel: ^2.0.0 dev_dependencies: build_runner: ^1.10.0 - freezed: ^0.14.0 - json_serializable: ^4.0.0 - mocktail: ^0.1.0 - test: ^1.16.0 + freezed: ^0.14.1+2 + json_serializable: ^4.1.0 + mocktail: ^0.1.1 + test: ^1.16.8 diff --git a/packages/stream_chat/test/src/api/channel_test.dart b/packages/stream_chat/test/src/api/channel_test.dart index ae4d5296..058a8044 100644 --- a/packages/stream_chat/test/src/api/channel_test.dart +++ b/packages/stream_chat/test/src/api/channel_test.dart @@ -50,7 +50,7 @@ void main() { ), ); - await channelClient.sendMessage(message); + await channelClient?.sendMessage(message); verify(() => mockDio.post('/channels/messaging/testid/message', data: { @@ -80,7 +80,7 @@ void main() { statusCode: 200, requestOptions: FakeRequestOptions(), )); - await channelClient.watch(); + await channelClient?.watch(); when( () => mockDio.post( @@ -95,7 +95,7 @@ void main() { ), ); - await channelClient.markRead(); + await channelClient?.markRead(); verify(() => mockDio.post('/channels/messaging/testid/read', data: {})).called(1); @@ -124,7 +124,7 @@ void main() { ), ); - await channelClient.getReplies('messageid', pagination); + await channelClient?.getReplies('messageid', pagination); verify(() => mockDio.get('/messages/messageid/replies', queryParameters: pagination.toJson())).called(1); @@ -141,7 +141,7 @@ void main() { httpClient: mockDio, tokenProvider: (_) async => '', ); - final channelClient = client.channel('messaging', id: 'testid'); + Channel channelClient = client.channel('messaging', id: 'testid'); when(() => mockDio.post( any(), @@ -564,7 +564,11 @@ void main() { 'api-key', httpClient: mockDio, tokenProvider: (_) async => '', - )..state.user = OwnUser(id: 'test-id'); + ); + + if (client != null) { + client.state?.user = OwnUser(id: 'test-id'); + } final channelClient = client.channel('messaging', id: 'testid'); const reactionType = 'test'; @@ -617,7 +621,11 @@ void main() { 'api-key', httpClient: mockDio, tokenProvider: (_) async => '', - )..state.user = OwnUser(id: 'test-id'); + ); + + if (client != null) { + client.state?.user = OwnUser(id: 'test-id'); + } final channelClient = client.channel('messaging', id: 'testid'); @@ -1069,8 +1077,8 @@ void main() { verify(() => mockDio.post('/channels/messaging/query', data: options)).called(1); - expect(channelClient.id, response.channel.id); - expect(channelClient.cid, response.channel.cid); + expect(channelClient.id, response?.channel?.id); + expect(channelClient.cid, response?.channel?.cid); }); test('with id', () async { @@ -1706,8 +1714,8 @@ void main() { verify(() => mockDio.post('/channels/messaging/query', data: options)).called(1); - expect(channelClient.id, response.channel.id); - expect(channelClient.cid, response.channel.cid); + expect(channelClient.id, response?.channel?.id); + expect(channelClient.cid, response?.channel?.cid); }); test('watch', () async { @@ -2027,8 +2035,8 @@ void main() { verify(() => mockDio.post('/channels/messaging/query', data: options)).called(1); - expect(channelClient.id, response.channel.id); - expect(channelClient.cid, response.channel.cid); + expect(channelClient.id, response.channel?.id); + expect(channelClient.cid, response.channel?.cid); }); test('stopWatching', () async { diff --git a/packages/stream_chat/test/src/api/websocket_test.dart b/packages/stream_chat/test/src/api/websocket_test.dart index 5993f0bf..0f3bd5d6 100644 --- a/packages/stream_chat/test/src/api/websocket_test.dart +++ b/packages/stream_chat/test/src/api/websocket_test.dart @@ -12,12 +12,10 @@ import 'package:web_socket_channel/web_socket_channel.dart'; class Functions { WebSocketChannel connectFunc( - String url, { - Iterable protocols, - Map headers, - Duration pingInterval, + String? url, { + Iterable? protocols, }) => - null; + WebSocketChannel.connect(Uri()); void handleFunc(Event event) {} } @@ -94,7 +92,7 @@ void main() { when(() => mockWSChannel.sink).thenAnswer((_) => MockWSSink()); when(() => mockWSChannel.stream).thenAnswer((_) => streamController.stream); - final connect = ws.connect().then((_) { + final connect = ws.connect()?.then((_) { streamController.sink.add('{}'); return Future.delayed(const Duration(milliseconds: 200)); }).then((value) { @@ -130,7 +128,7 @@ void main() { when(() => mockWSChannel.sink).thenAnswer((_) => MockWSSink()); when(() => mockWSChannel.stream).thenAnswer((_) => streamController.stream); - final connect = ws.connect().then((_) { + final connect = ws.connect()?.then((_) { streamController.sink.add('{}'); return Future.delayed(const Duration(milliseconds: 200)); }).then((value) { @@ -208,7 +206,7 @@ void main() { (_) => streamController.sink.add('{}'), ); - final connect = ws.connect().then((_) { + final connect = ws.connect()?.then((_) { streamController.sink.add('{}'); return Future.delayed(const Duration(milliseconds: 200)); }).then((value) async { @@ -249,7 +247,7 @@ void main() { when(() => mockWSChannel.stream).thenAnswer((_) => streamController.stream); when(() => mockWSChannel.sink).thenReturn(mockWSSink); - final connect = ws.connect().then((_) { + final connect = ws.connect()?.then((_) { streamController.sink.add('{}'); streamController.close(); streamController = StreamController.broadcast(); @@ -292,7 +290,7 @@ void main() { when(() => mockWSChannel.stream).thenAnswer((_) => streamController.stream); when(() => mockWSChannel.sink).thenReturn(mockWSSink); - final connect = ws.connect().then((_) { + final connect = ws.connect()?.then((_) { streamController.sink.add('{}'); return Future.delayed(const Duration(milliseconds: 200)); }).then((value) async { diff --git a/packages/stream_chat/test/src/models/attachment_test.dart b/packages/stream_chat/test/src/models/attachment_test.dart index 3124ddd2..899aec73 100644 --- a/packages/stream_chat/test/src/models/attachment_test.dart +++ b/packages/stream_chat/test/src/models/attachment_test.dart @@ -49,7 +49,7 @@ void main() { 'https://media0.giphy.com/media/3o7TKnCdBx5cMg0qti/giphy.gif', ); expect(attachment.actions, hasLength(3)); - expect(attachment.actions[0], isA()); + expect(attachment.actions![0], isA()); }); test('should serialize to json correctly', () { diff --git a/packages/stream_chat/test/src/models/channel_state_test.dart b/packages/stream_chat/test/src/models/channel_state_test.dart index c2d69b2b..9aa2b2d6 100644 --- a/packages/stream_chat/test/src/models/channel_state_test.dart +++ b/packages/stream_chat/test/src/models/channel_state_test.dart @@ -10,7 +10,8 @@ import 'package:stream_chat/stream_chat.dart'; void main() { group('src/models/channel_state', () { - const jsonExample = '''{ + const jsonExample = ''' + { "channel": { "id": "dev", "type": "team", @@ -844,36 +845,36 @@ void main() { test('should parse json correctly', () { final channelState = ChannelState.fromJson(json.decode(jsonExample)); - expect(channelState.channel.cid, 'team:dev'); - expect(channelState.channel.id, 'dev'); - expect(channelState.channel.team, 'test'); - expect(channelState.channel.type, 'team'); - expect(channelState.channel.config, isA()); - expect(channelState.channel.config, isNotNull); - expect(channelState.channel.config.commands, hasLength(1)); - expect(channelState.channel.config.commands[0], isA()); - expect(channelState.channel.lastMessageAt, + expect(channelState.channel?.cid, 'team:dev'); + expect(channelState.channel?.id, 'dev'); + expect(channelState.channel?.team, 'test'); + expect(channelState.channel?.type, 'team'); + expect(channelState.channel?.config, isA()); + expect(channelState.channel?.config, isNotNull); + expect(channelState.channel?.config?.commands, hasLength(1)); + expect(channelState.channel?.config?.commands![0], isA()); + expect(channelState.channel?.lastMessageAt, DateTime.parse('2020-01-30T13:43:41.062362Z')); - expect(channelState.channel.createdAt, + expect(channelState.channel?.createdAt, DateTime.parse('2019-04-03T18:43:33.213373Z')); - expect(channelState.channel.updatedAt, + expect(channelState.channel?.updatedAt, DateTime.parse('2019-04-03T18:43:33.213374Z')); - expect(channelState.channel.createdBy, isA()); - expect(channelState.channel.frozen, true); - expect(channelState.channel.extraData['example'], 1); - expect(channelState.channel.extraData['name'], '#dev'); + expect(channelState.channel?.createdBy, isA()); + expect(channelState.channel?.frozen, true); + expect(channelState.channel?.extraData!['example'], 1); + expect(channelState.channel?.extraData!['name'], '#dev'); expect( - channelState.channel.extraData['image'], + channelState.channel?.extraData!['image'], 'https://cdn.chrisshort.net/testing-certificate-chains-in-go/GOPHER_MIC_DROP.png', ); expect(channelState.messages, hasLength(25)); - expect(channelState.messages[0], isA()); - expect(channelState.messages[0], isNotNull); + expect(channelState.messages![0], isA()); + expect(channelState.messages![0], isNotNull); expect( - channelState.messages[0].createdAt, + channelState.messages![0].createdAt, DateTime.parse('2020-01-29T03:23:02.843948Z'), ); - expect(channelState.messages[0].user, isA()); + expect(channelState.messages![0].user, isA()); expect(channelState.watcherCount, 5); }); diff --git a/packages/stream_chat/test/src/models/channel_test.dart b/packages/stream_chat/test/src/models/channel_test.dart index 9f6aa5b7..4a221a8a 100644 --- a/packages/stream_chat/test/src/models/channel_test.dart +++ b/packages/stream_chat/test/src/models/channel_test.dart @@ -20,8 +20,8 @@ void main() { expect(channel.id, equals('test')); expect(channel.type, equals('livestream')); expect(channel.cid, equals('test:livestream')); - expect(channel.extraData['cats'], equals(true)); - expect(channel.extraData['fruit'], equals(['bananas', 'apples'])); + expect(channel.extraData!['cats'], equals(true)); + expect(channel.extraData!['fruit'], equals(['bananas', 'apples'])); }); test('should serialize to json correctly', () { diff --git a/packages/stream_chat/test/src/models/reaction_test.dart b/packages/stream_chat/test/src/models/reaction_test.dart index 2a1d5431..80d2964b 100644 --- a/packages/stream_chat/test/src/models/reaction_test.dart +++ b/packages/stream_chat/test/src/models/reaction_test.dart @@ -33,7 +33,7 @@ void main() { expect(reaction.createdAt, DateTime.parse('2020-01-28T22:17:31.108742Z')); expect(reaction.type, 'wow'); expect( - reaction.user.toJson(), + reaction.user?.toJson(), User.init('2de0297c-f3f2-489d-b930-ef77342edccf', extraData: { 'image': 'https://randomuser.me/api/portraits/women/45.jpg', 'name': 'Daisy Morgan' diff --git a/packages/stream_chat/test/src/models/read_test.dart b/packages/stream_chat/test/src/models/read_test.dart index 9fd68b6a..7575d35f 100644 --- a/packages/stream_chat/test/src/models/read_test.dart +++ b/packages/stream_chat/test/src/models/read_test.dart @@ -19,7 +19,7 @@ void main() { test('should parse json correctly', () { final read = Read.fromJson(json.decode(jsonExample)); expect(read.lastRead, DateTime.parse('2020-01-28T22:17:30.966485504Z')); - expect(read.user.id, 'bbb19d9a-ee50-45bc-84e5-0584e79d0c9e'); + expect(read.user?.id, 'bbb19d9a-ee50-45bc-84e5-0584e79d0c9e'); expect(read.unreadMessages, 10); });