diff --git a/packages/stream_chat/build.yaml b/packages/stream_chat/build.yaml index ddbd70dd..d439bddb 100644 --- a/packages/stream_chat/build.yaml +++ b/packages/stream_chat/build.yaml @@ -4,5 +4,4 @@ targets: json_serializable: options: explicit_to_json: true - field_rename: snake - any_map: true + field_rename: snake \ No newline at end of file diff --git a/packages/stream_chat/example/lib/main.dart b/packages/stream_chat/example/lib/main.dart index d13dda4c..11e9779f 100644 --- a/packages/stream_chat/example/lib/main.dart +++ b/packages/stream_chat/example/lib/main.dart @@ -44,9 +44,9 @@ class StreamExample extends StatelessWidget { /// To initialize this example, an instance of /// [client] and [channel] is required. const StreamExample({ - Key key, - @required this.client, - @required this.channel, + Key? key, + required this.client, + required this.channel, }) : super(key: key); /// Instance of [StreamChatClient] we created earlier. @@ -69,28 +69,31 @@ class StreamExample extends StatelessWidget { /// containing the channel name and a [MessageView] displaying recent messages. class HomeScreen extends StatelessWidget { /// [HomeScreen] is constructed using the [Channel] we defined earlier. - const HomeScreen({Key key, @required this.channel}) : super(key: key); + const HomeScreen({ + Key? key, + required this.channel, + }) : super(key: key); /// Channel object containing the [Channel.id] we'd like to observe. final Channel channel; @override Widget build(BuildContext context) { - final messages = channel.state.channelStateStream; + final messages = channel.state!.channelStateStream; return Scaffold( appBar: AppBar( title: Text('Channel: ${channel.id}'), ), body: SafeArea( - child: StreamBuilder( + child: StreamBuilder( stream: messages, builder: ( BuildContext context, - AsyncSnapshot snapshot, + AsyncSnapshot snapshot, ) { if (snapshot.hasData && snapshot.data != null) { return MessageView( - messages: snapshot.data.messages.reversed.toList(), + messages: snapshot.data!.messages.reversed.toList(), channel: channel, ); } else if (snapshot.hasError) { @@ -119,9 +122,9 @@ class HomeScreen extends StatelessWidget { class MessageView extends StatefulWidget { /// Message takes the latest list of messages and the current channel. const MessageView({ - Key key, - @required this.messages, - @required this.channel, + Key? key, + required this.messages, + required this.channel, }) : super(key: key); /// List of messages sent in the given channel. @@ -135,8 +138,8 @@ class MessageView extends StatefulWidget { } class _MessageViewState extends State { - TextEditingController _controller; - ScrollController _scrollController; + late final TextEditingController _controller; + late final ScrollController _scrollController; List get _messages => widget.messages; @@ -174,12 +177,12 @@ class _MessageViewState extends State { reverse: true, itemBuilder: (BuildContext context, int index) { final item = _messages[index]; - if (item.user.id == widget.channel.client.uid) { + if (item.user?.id == widget.channel.client.uid) { return Align( alignment: Alignment.centerRight, child: Padding( padding: const EdgeInsets.all(8), - child: Text(item.text), + child: Text(item.text ?? ''), ), ); } else { @@ -187,7 +190,7 @@ class _MessageViewState extends State { alignment: Alignment.centerLeft, child: Padding( padding: const EdgeInsets.all(8), - child: Text(item.text), + child: Text(item.text ?? ''), ), ); } @@ -246,5 +249,5 @@ class _MessageViewState extends State { /// Helper extension for quickly retrieving /// the current user id from a [StreamChatClient]. extension on StreamChatClient { - String get uid => state.user.id; + String get uid => state.user!.id; } diff --git a/packages/stream_chat/example/pubspec.yaml b/packages/stream_chat/example/pubspec.yaml index 2d010ab6..1b092b3e 100644 --- a/packages/stream_chat/example/pubspec.yaml +++ b/packages/stream_chat/example/pubspec.yaml @@ -5,7 +5,7 @@ publish_to: "none" version: 1.0.0+1 environment: - sdk: ">=2.7.0 <3.0.0" + sdk: '>=2.12.0 <3.0.0' dependencies: cupertino_icons: ^1.0.0 diff --git a/packages/stream_chat/lib/src/api/channel.dart b/packages/stream_chat/lib/src/api/channel.dart index aeacaf7d..63fc5595 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 ' @@ -58,12 +59,12 @@ class Channel { /// Returns true if the channel is muted bool get isMuted => _client.state.user?.channelMutes - ?.any((element) => element.channel.cid == cid) == + .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 +73,130 @@ class Channel { bool get isDistinct => id?.startsWith('!members') == true; /// Channel configuration - ChannelConfig get config => state?._channelState?.channel?.config; + ChannelConfig? get config { + _checkInitialized(); + return state?._channelState.channel?.config; + } /// Channel configuration as a stream - Stream get configStream => - state?.channelStateStream?.map((cs) => cs.channel?.config); + Stream? get configStream { + _checkInitialized(); + return state?.channelStateStream.map((cs) => cs!.channel?.config); + } /// Channel user creator - User get createdBy => state?._channelState?.channel?.createdBy; + User? get createdBy { + _checkInitialized(); + return state?._channelState.channel?.createdBy; + } /// Channel user creator as a stream - Stream get createdByStream => - state?.channelStateStream?.map((cs) => cs.channel?.createdBy); + Stream? get createdByStream { + _checkInitialized(); + return state?.channelStateStream.map((cs) => cs!.channel?.createdBy); + } /// Channel frozen status - bool get frozen => state?._channelState?.channel?.frozen; + bool? get frozen { + _checkInitialized(); + return state?._channelState.channel?.frozen; + } /// Channel frozen status as a stream - Stream get frozenStream => - state?.channelStateStream?.map((cs) => cs.channel?.frozen); + Stream? get frozenStream { + _checkInitialized(); + return state?.channelStateStream.map((cs) => cs!.channel?.frozen); + } /// Channel creation date - DateTime get createdAt => state?._channelState?.channel?.createdAt; + DateTime? get createdAt { + _checkInitialized(); + return state?._channelState.channel?.createdAt; + } /// Channel creation date as a stream - Stream get createdAtStream => - state?.channelStateStream?.map((cs) => cs.channel?.createdAt); + Stream? get createdAtStream { + _checkInitialized(); + return state?.channelStateStream.map((cs) => cs!.channel?.createdAt); + } /// Channel last message date - DateTime get lastMessageAt => state?._channelState?.channel?.lastMessageAt; + DateTime? get lastMessageAt { + _checkInitialized(); + + return state?._channelState.channel?.lastMessageAt; + } /// Channel last message date as a stream - Stream get lastMessageAtStream => - state?.channelStateStream?.map((cs) => cs.channel?.lastMessageAt); + Stream? get lastMessageAtStream { + _checkInitialized(); + + return state?.channelStateStream.map((cs) => cs!.channel?.lastMessageAt); + } /// Channel updated date - DateTime get updatedAt => state?._channelState?.channel?.updatedAt; + DateTime? get updatedAt { + _checkInitialized(); + + return state?._channelState.channel?.updatedAt; + } /// Channel updated date as a stream - Stream get updatedAtStream => - state?.channelStateStream?.map((cs) => cs.channel?.updatedAt); + Stream? get updatedAtStream { + _checkInitialized(); + + return state?.channelStateStream.map((cs) => cs!.channel?.updatedAt); + } /// Channel deletion date - DateTime get deletedAt => state?._channelState?.channel?.deletedAt; + DateTime? get deletedAt { + _checkInitialized(); + + return state?._channelState.channel?.deletedAt; + } /// Channel deletion date as a stream - Stream get deletedAtStream => - state?.channelStateStream?.map((cs) => cs.channel?.deletedAt); + Stream? get deletedAtStream { + _checkInitialized(); + + return state?.channelStateStream.map((cs) => cs!.channel?.deletedAt); + } /// Channel member count - int get memberCount => state?._channelState?.channel?.memberCount; + int? get memberCount { + _checkInitialized(); + + return state?._channelState.channel?.memberCount; + } /// Channel member count as a stream - Stream get memberCountStream => - state?.channelStateStream?.map((cs) => cs.channel?.memberCount); + Stream? get memberCountStream { + _checkInitialized(); + + return state?.channelStateStream.map((cs) => cs!.channel?.memberCount); + } /// Channel 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); + String? get id => state?._channelState.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; - - /// Channel cid as a stream - Stream get cidStream => - state?.channelStateStream?.map((cs) => cs.channel?.cid ?? _cid); + String? get team { + _checkInitialized(); + return state?._channelState.channel?.team; + } /// Channel extra data - Map get extraData => - state?._channelState?.channel?.extraData ?? _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 { + _checkInitialized(); + return state?.channelStateStream.map((cs) => cs!.channel?.extraData); + } /// The main Stream chat client StreamChatClient get client => _client; @@ -174,7 +220,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,9 +241,8 @@ 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) { @@ -248,13 +293,13 @@ class Channel { Future future; if (isImage) { future = sendImage( - it.file, + it.file!, onSendProgress: onSendProgress, cancelToken: cancelToken, ).then((it) => it.file); } else { future = sendFile( - it.file, + it.file!, onSendProgress: onSendProgress, cancelToken: cancelToken, ).then((it) => it.file); @@ -283,7 +328,7 @@ class Channel { it.copyWith(uploadState: UploadState.failed(error: e.toString())), ); }).whenComplete(() { - throttledUpdateAttachment?.cancel(); + throttledUpdateAttachment.cancel(); _cancelableAttachmentUploadRequest.remove(it.id); }); })).whenComplete(() { @@ -297,43 +342,34 @@ class Channel { /// Waits for a [_messageAttachmentsUploadCompleter] to complete /// before actually sending the message. Future sendMessage(Message message) async { + _checkInitialized(); // Cancelling previous completer in case it's called again in the process // Eg. Updating the message while the previous call is in progress. _messageAttachmentsUploadCompleter .remove(message.id) ?.completeError('Message Cancelled'); - final quotedMessage = state?.messages?.firstWhere( - (m) => m.id == message?.quotedMessageId, - orElse: () => null, + final quotedMessage = state!.messages.firstWhereOrNull( + (m) => m.id == message.quotedMessageId, ); // ignore: parameter_assignments message = message.copyWith( - createdAt: message.createdAt ?? DateTime.now(), + createdAt: message.createdAt, user: _client.state.user, quotedMessage: quotedMessage, status: MessageSendingStatus.sending, - attachments: message.attachments?.map( + attachments: message.attachments.map( (it) { if (it.uploadState.isSuccess) return it; return it.copyWith(uploadState: const UploadState.preparing()); }, - )?.toList(), + ).toList(), ); - if (message.parentId != null && message.id == null) { - final parentMessage = - state.messages.firstWhere((m) => m.id == message.parentId); - - state?.addMessage(parentMessage.copyWith( - replyCount: parentMessage.replyCount + 1, - )); - } - - state?.addMessage(message); + state!.addMessage(message); try { - if (message.attachments?.any((it) => !it.uploadState.isSuccess) == true) { + if (message.attachments.any((it) => !it.uploadState.isSuccess) == true) { final attachmentsUploadCompleter = Completer(); _messageAttachmentsUploadCompleter[message.id] = attachmentsUploadCompleter; @@ -348,12 +384,12 @@ class Channel { message = await attachmentsUploadCompleter.future; } - final response = await _client.sendMessage(message, id, type); - state?.addMessage(response.message); + final response = await _client.sendMessage(message, id!, type!); + state!.addMessage(response.message); return response; } catch (error) { if (error is DioError && error.type != DioErrorType.response) { - state?.retryQueue?.add([message]); + state!.retryQueue?.add([message]); } rethrow; } @@ -372,19 +408,19 @@ class Channel { // ignore: parameter_assignments message = message.copyWith( status: MessageSendingStatus.updating, - updatedAt: message.updatedAt ?? DateTime.now(), - attachments: message.attachments?.map( + updatedAt: message.updatedAt, + attachments: message.attachments.map( (it) { if (it.uploadState.isSuccess) return it; return it.copyWith(uploadState: const UploadState.preparing()); }, - )?.toList(), + ).toList(), ); state?.addMessage(message); try { - if (message.attachments?.any((it) => !it.uploadState.isSuccess) == true) { + if (message.attachments.any((it) => !it.uploadState.isSuccess) == true) { final attachmentsUploadCompleter = Completer(); _messageAttachmentsUploadCompleter[message.id] = attachmentsUploadCompleter; @@ -400,9 +436,13 @@ class Channel { } final response = await _client.updateMessage(message); - state?.addMessage(response?.message?.copyWith( + + final m = response.message.copyWith( ownReactions: message.ownReactions, - )); + ); + + state?.addMessage(m); + return response; } catch (error) { if (error is DioError && error.type != DioErrorType.response) { @@ -417,7 +457,7 @@ class Channel { // 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, )); @@ -456,18 +496,18 @@ class Channel { /// Pins provided message Future pinMessage( Message message, - Object timeoutOrExpirationDate, + Object? timeoutOrExpirationDate, ) { assert(() { if (timeoutOrExpirationDate is! DateTime && - timeoutOrExpirationDate is! num && - timeoutOrExpirationDate != null) { + timeoutOrExpirationDate != null && + timeoutOrExpirationDate is! num) { throw ArgumentError('Invalid timeout or Expiration date'); } return true; }(), 'Check for invalid token or expiration date'); - DateTime pinExpires; + DateTime? pinExpires; if (timeoutOrExpirationDate is DateTime) { pinExpires = timeoutOrExpirationDate; } else if (timeoutOrExpirationDate is num) { @@ -490,37 +530,41 @@ class Channel { /// Send a file to this channel Future sendFile( AttachmentFile file, { - ProgressCallback onSendProgress, - CancelToken cancelToken, - }) => - _client.sendFile( - file, - id, - type, - onSendProgress: onSendProgress, - cancelToken: cancelToken, - ); + ProgressCallback? onSendProgress, + CancelToken? cancelToken, + }) { + _checkInitialized(); + return _client.sendFile( + file, + id!, + type!, + onSendProgress: onSendProgress, + cancelToken: cancelToken, + ); + } /// Send an image to this channel Future sendImage( AttachmentFile file, { - ProgressCallback onSendProgress, - CancelToken cancelToken, - }) => - _client.sendImage( - file, - id, - type, - onSendProgress: onSendProgress, - cancelToken: cancelToken, - ); + ProgressCallback? onSendProgress, + CancelToken? cancelToken, + }) { + _checkInitialized(); + return _client.sendImage( + file, + id!, + type!, + onSendProgress: onSendProgress, + cancelToken: cancelToken, + ); + } /// A message search. Future search({ - String query, - Map messageFilters, - List sort, - PaginationParams paginationParams, + String? query, + Map? messageFilters, + List? sort, + PaginationParams? paginationParams, }) => _client.search( { @@ -537,16 +581,30 @@ class Channel { /// Delete a file from this channel Future deleteFile( String url, { - CancelToken cancelToken, - }) => - _client.deleteFile(url, id, type, cancelToken: cancelToken); + CancelToken? cancelToken, + }) { + _checkInitialized(); + return _client.deleteFile( + url, + id!, + type!, + cancelToken: cancelToken, + ); + } /// Delete an image from this channel Future deleteImage( String url, { - CancelToken cancelToken, - }) => - _client.deleteImage(url, id, type, cancelToken: cancelToken); + CancelToken? cancelToken, + }) { + _checkInitialized(); + return _client.deleteImage( + url, + id!, + type!, + cancelToken: cancelToken, + ); + } /// Send an event on this channel Future sendEvent(Event event) { @@ -554,7 +612,7 @@ 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 @@ -565,20 +623,21 @@ class Channel { Map extraData = const {}, bool enforceUnique = false, }) async { + _checkInitialized(); final messageId = message.id; final now = DateTime.now(); 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( messageId: messageId, createdAt: now, type: type, - user: user, + user: user!, score: 1, extraData: extraData, ); @@ -589,7 +648,7 @@ class Channel { ..removeWhere((it) => it.userId != user.id); final newMessage = message.copyWith( - reactionCounts: {...message?.reactionCounts ?? {}} + reactionCounts: {...message.reactionCounts ?? {}} ..update(type, (value) { if (enforceUnique) return value; return value + 1; @@ -649,8 +708,8 @@ class Channel { r.type == reaction.type && r.messageId == reaction.messageId); - final ownReactions = [...latestReactions ?? []] - ..removeWhere((it) => it.userId != user.id); + final ownReactions = [...latestReactions] + ..removeWhere((it) => it.userId != user!.id); final newMessage = message.copyWith( reactionCounts: reactionCounts..removeWhere((_, value) => value == 0), @@ -675,7 +734,7 @@ class Channel { /// Edit the channel custom data Future update( Map channelData, [ - Message updateMessage, + Message? updateMessage, ]) async { final response = await _client.post(_channelURL, data: { if (updateMessage != null) @@ -705,14 +764,14 @@ class Channel { } /// 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); @@ -721,7 +780,7 @@ class Channel { /// Add members to the channel Future addMembers( List memberIds, [ - Message message, + Message? message, ]) async { final res = await _client.post(_channelURL, data: { 'add_members': memberIds, @@ -733,7 +792,7 @@ class Channel { /// Invite members to the channel Future inviteMembers( List memberIds, [ - Message message, + Message? message, ]) async { final res = await _client.post(_channelURL, data: { 'invites': memberIds, @@ -745,7 +804,7 @@ class Channel { /// Remove members from the channel Future removeMembers( List memberIds, [ - Message message, + Message? message, ]) async { final res = await _client.post(_channelURL, data: { 'remove_members': memberIds, @@ -772,31 +831,30 @@ class Channel { 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; - if (oldIndex != null && oldIndex != -1) { - oldMessage = state.messages[oldIndex]; - state.updateChannelState(state._channelState.copyWith( - messages: state.messages..remove(oldMessage), + Message? oldMessage; + if (oldIndex != -1) { + 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)); } } @@ -809,9 +867,9 @@ class Channel { /// Mark all channel messages as read 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); } @@ -845,7 +903,7 @@ 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); } @@ -857,7 +915,7 @@ class Channel { '$_channelURL/stop-watching', data: {}, ); - return _client.decode(response?.data, EmptyResponse.fromJson); + return _client.decode(response.data, EmptyResponse.fromJson); } /// List the message replies for a parent message @@ -918,7 +976,9 @@ class Channel { GetMessagesByIdResponse.fromJson, ); - state?.updateChannelState(ChannelState(messages: res.messages)); + final messages = res.messages; + + state?.updateChannelState(ChannelState(messages: messages)); return res; } @@ -952,9 +1012,9 @@ class Channel { /// in the offline storage 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 +1044,11 @@ class Channel { if (preferOffline && cid != null) { final updatedState = - await _client.chatPersistenceClient?.getChannelStateByCid( - cid, + (await _client.chatPersistenceClient?.getChannelStateByCid( + cid!, messagePagination: messagesPagination, - ); - if (updatedState != null && updatedState.messages.isNotEmpty) { + ))!; + if (updatedState.messages.isNotEmpty) { if (state == null) { _initState(updatedState); } else { @@ -1003,8 +1063,8 @@ class Channel { 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,8 +1073,8 @@ class Channel { if (!_client.persistenceEnabled) { rethrow; } - return _client.chatPersistenceClient?.getChannelStateByCid( - cid, + return _client.chatPersistenceClient!.getChannelStateByCid( + cid!, messagePagination: messagesPagination, ); } @@ -1022,9 +1082,9 @@ class Channel { /// Query channel members Future queryMembers({ - Map filter, - List sort, - PaginationParams pagination, + Map? filter, + List? sort, + PaginationParams? pagination, }) async { final payload = { 'sort': sort, @@ -1038,8 +1098,8 @@ class Channel { if (id != null) { payload['id'] = id; - } else if (state?.members?.isNotEmpty == true) { - payload['members'] = state.members; + } else if (state?.members.isNotEmpty == true) { + payload['members'] = state!.members; } final rawRes = await _client.get('/members', queryParameters: { @@ -1050,7 +1110,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, @@ -1121,8 +1181,11 @@ class Channel { .post('$_channelURL/hide', data: {'clear_history': clearHistory}); if (clearHistory == true) { - state.truncate(); - await _client.chatPersistenceClient?.deleteMessageByCid(_cid); + state!.truncate(); + final cid = _cid; + if (cid != null) { + await _client.chatPersistenceClient?.deleteMessageByCid(cid); + } } return _client.decode(response.data, EmptyResponse.fromJson); @@ -1139,10 +1202,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 +1216,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 +1229,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 +1239,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,15 +1254,15 @@ class Channel { /// Call this method to dispose the channel client void dispose() { - state.dispose(); + state?.dispose(); } void _checkInitialized() { - if (!_initializedCompleter.isCompleted) { - throw Exception( - "Channel $cid hasn't been initialized yet. Make sure to call .watch()" - ' or to instantiate the client using [Channel.fromState]'); - } + assert( + _initializedCompleter.isCompleted, + "Channel $cid hasn't been initialized yet. Make sure to call .watch()" + ' or to instantiate the client using [Channel.fromState]', + ); } } @@ -1211,7 +1274,7 @@ class ChannelClientState { ChannelState channelState, //ignore: unnecessary_parenthesis ) : _debouncedUpdatePersistenceChannelState = ((ChannelState state) => - _channel?._client?.chatPersistenceClient + _channel._client.chatPersistenceClient ?.updateChannelState(state)) .debounced(const Duration(seconds: 1)) { retryQueue = RetryQueue( @@ -1252,13 +1315,13 @@ class ChannelClientState { _startCleaningPinnedMessages(); _channel._client.chatPersistenceClient - ?.getChannelThreads(_channel.cid) - ?.then((threads) { + ?.getChannelThreads(_channel.cid!) + .then((threads) { _threads = threads; - })?.then((_) { + }).then((_) { _channel._client.chatPersistenceClient - ?.getChannelStateByCid(_channel.cid) - ?.then((state) { + ?.getChannelStateByCid(_channel.cid!) + .then((state) { // Replacing the persistence state members with the latest // `channelState.members` as they may have changes over the time. updateChannelState(state.copyWith(members: channelState.members)); @@ -1270,21 +1333,20 @@ 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); + _unreadCountController.add(userRead.unreadMessages); } } void _checkExpiredAttachmentMessages(ChannelState channelState) { final expiredAttachmentMessagesId = channelState.messages - ?.where((m) => + .where((m) => !_updatedMessagesIds.contains(m.id) && - m.attachments?.isNotEmpty == true && - m.attachments?.any((e) { + m.attachments.isNotEmpty == true && + m.attachments.any((e) { final url = e.imageUrl ?? e.assetUrl; if (url == null || !url.contains('')) { return false; @@ -1295,13 +1357,13 @@ 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) { + .map((e) => e.id) + .toList(); + if (expiredAttachmentMessagesId.isNotEmpty == true) { _channel.getMessagesById(expiredAttachmentMessagesId); _updatedMessagesIds.addAll(expiredAttachmentMessagesId); } @@ -1310,10 +1372,10 @@ 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, - member, + ...channelState!.members, + member!, ], )); })); @@ -1322,17 +1384,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 +1405,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,26 +1416,25 @@ 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); /// [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( DateTime.now().subtract( @@ -1385,14 +1446,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 +1462,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,9 +1478,9 @@ 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); @@ -1427,7 +1488,7 @@ class ChannelClientState { if (message.pinned == true) { _channelState = _channelState.copyWith( pinnedMessages: [ - ..._channelState.pinnedMessages ?? [], + ..._channelState.pinnedMessages, message, ], ); @@ -1437,7 +1498,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 +1510,14 @@ class ChannelClientState { EventType.notificationMessageNew, ) .listen((event) { - final message = event.message; + 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); } })); } @@ -1467,7 +1528,7 @@ class ChannelClientState { 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( @@ -1481,7 +1542,7 @@ class ChannelClientState { _channelState = _channelState.copyWith( messages: newMessages, - channel: _channelState.channel.copyWith( + channel: _channelState.channel?.copyWith( lastMessageAt: message.createdAt, ), ); @@ -1493,7 +1554,7 @@ class ChannelClientState { } void _listenReadEvents() { - if (_channel.config?.readEvents == false) { + if (_channelState.channel?.config.readEvents == false) { return; } @@ -1505,18 +1566,19 @@ class ChannelClientState { ) .listen( (event) { - final readList = List.from(_channelState?.read ?? []); + 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, + user: event.user!, + lastRead: event.createdAt!, + unreadMessages: event.totalUnreadCount!, )); _channelState = _channelState.copyWith(read: readList); } @@ -1529,45 +1591,45 @@ class ChannelClientState { 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 + 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])) + .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), + 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 @@ -1575,18 +1637,19 @@ class ChannelClientState { .toList(); /// Channel watchers list as a stream - Stream> get watchersStream => - CombineLatestStream.combine2, Map, List>( - channelStateStream.map((cs) => cs.watchers), + 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(), + (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 +1657,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 userId = _channel.client.state.user?.id; + 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)) - ?.toList() ?? + ?.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; } @@ -1643,40 +1706,37 @@ class ChannelClientState { /// Update channelState with updated information void updateChannelState(ChannelState updatedState) { final newMessages = [ - ...updatedState?.messages ?? [], - ..._channelState?.messages - ?.where((m) => - updatedState.messages - ?.any((newMessage) => newMessage.id == m.id) != - true) - ?.toList() ?? - [], - ]..sort(_sortByCreatedAt); + ...updatedState.messages, + ..._channelState.messages + .where((m) => + updatedState.messages + .any((newMessage) => newMessage.id == m.id) != + true) + .toList(), + ]..sort(_sortByCreatedAt as int Function(Message, Message)?); final newWatchers = [ - ...updatedState?.watchers ?? [], - ..._channelState?.watchers - ?.where((w) => - updatedState.watchers - ?.any((newWatcher) => newWatcher.id == w.id) != - true) - ?.toList() ?? - [], + ...updatedState.watchers, + ..._channelState.watchers + .where((w) => + updatedState.watchers + .any((newWatcher) => newWatcher.id == w.id) != + true) + .toList(), ]; final newMembers = [ - ...updatedState?.members ?? [], + ...updatedState.members, ]; final newReads = [ - ...updatedState?.read ?? [], - ..._channelState?.read - ?.where((r) => - updatedState.read - ?.any((newRead) => newRead.user.id == r.user.id) != - true) - ?.toList() ?? - [], + ...updatedState.read, + ..._channelState.read + .where((r) => + updatedState.read + .any((newRead) => newRead.user.id == r.user.id) != + true) + .toList(), ]; _checkExpiredAttachmentMessages(updatedState); @@ -1692,7 +1752,7 @@ class ChannelClientState { ); } - int _sortByCreatedAt(a, b) { + int? _sortByCreatedAt(a, b) { if (a.createdAt == null) { return 1; } @@ -1705,52 +1765,54 @@ 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) { _channelStateController.add(v); - _debouncedUpdatePersistenceChannelState?.call([v]); + _debouncedUpdatePersistenceChannelState.call([v]); } /// The channel threads related to this channel - Map> get threads => _threadsController.value; + Map>? get threads => _threadsController.value + ?.map((key, value) => MapEntry(key ?? '', value ?? [])); /// 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(), + _channel.cid!, + 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) { + if (_channelState.channel?.config.typingEvents == false) { return; } @@ -1758,7 +1820,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 +1830,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 +1842,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,10 +1864,10 @@ class ChannelClientState { ); } - Timer _cleaningTimer; + late Timer _cleaningTimer; void _startCleaning() { - if (_channel.config?.typingEvents == false) { + if (_channelState.channel?.config.typingEvents == false) { return; } @@ -1813,7 +1875,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,15 +1883,14 @@ class ChannelClientState { }); } - Timer _pinnedMessagesTimer; + late Timer _pinnedMessagesTimer; void _startCleaningPinnedMessages() { _pinnedMessagesTimer = Timer.periodic(const Duration(seconds: 30), (_) { final now = DateTime.now(); - var expiredMessages = channelState.pinnedMessages - ?.where((m) => m.pinExpires?.isBefore(now) == true) - ?.toList() ?? - []; + var expiredMessages = channelState!.pinnedMessages + .where((m) => m.pinExpires?.isBefore(now) == true) + .toList(); if (expiredMessages.isNotEmpty) { expiredMessages = expiredMessages .map((m) => m.copyWith( @@ -1839,7 +1900,7 @@ class ChannelClientState { .toList(); updateChannelState(_channelState.copyWith( - pinnedMessages: pinnedMessages.where(_pinIsValid()).toList(), + pinnedMessages: pinnedMessages!.where(_pinIsValid()).toList(), messages: expiredMessages, )); } @@ -1863,9 +1924,9 @@ class ChannelClientState { /// Call this method to dispose this object void dispose() { - _debouncedUpdatePersistenceChannelState?.cancel(); + _debouncedUpdatePersistenceChannelState.cancel(); _unreadCountController.close(); - retryQueue.dispose(); + retryQueue!.dispose(); _subscriptions.forEach((s) => s.cancel()); _channelStateController.close(); _isUpToDateController.close(); @@ -1878,5 +1939,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..e78a80ec 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, @@ -106,6 +106,7 @@ class PaginationParams { ); @override + @JsonKey(ignore: true) int get hashCode => runtimeType.hashCode ^ limit.hashCode ^ diff --git a/packages/stream_chat/lib/src/api/requests.g.dart b/packages/stream_chat/lib/src/api/requests.g.dart index f020c1ea..1ec7b5fc 100644 --- a/packages/stream_chat/lib/src/api/requests.g.dart +++ b/packages/stream_chat/lib/src/api/requests.g.dart @@ -13,7 +13,10 @@ Map _$SortOptionToJson(SortOption instance) => }; Map _$PaginationParamsToJson(PaginationParams instance) { - final val = {}; + final val = { + 'limit': instance.limit, + 'offset': instance.offset, + }; void writeNotNull(String key, dynamic value) { if (value != null) { @@ -21,8 +24,6 @@ Map _$PaginationParamsToJson(PaginationParams instance) { } } - writeNotNull('limit', instance.limit); - writeNotNull('offset', instance.offset); writeNotNull('id_gt', instance.greaterThan); writeNotNull('id_gte', instance.greaterThanOrEqual); writeNotNull('id_lt', instance.lessThan); diff --git a/packages/stream_chat/lib/src/api/responses.dart b/packages/stream_chat/lib/src/api/responses.dart index 431a57e7..a64cd64e 100644 --- a/packages/stream_chat/lib/src/api/responses.dart +++ b/packages/stream_chat/lib/src/api/responses.dart @@ -13,14 +13,15 @@ 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; + @JsonKey(defaultValue: []) + late List events; /// Create a new instance from a json static SyncResponse fromJson(Map json) => @@ -31,7 +32,8 @@ class SyncResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class QueryChannelsResponse extends _BaseResponse { /// List of channels state returned by the query - List channels; + @JsonKey(defaultValue: []) + late List channels; /// Create a new instance from a json static QueryChannelsResponse fromJson(Map json) => @@ -41,8 +43,8 @@ class QueryChannelsResponse extends _BaseResponse { /// Model response for [StreamChatClient.queryChannels] api call @JsonSerializable(createToJson: false) class TranslateMessageResponse extends _BaseResponse { - /// List of channels state returned by the query - TranslatedMessage message; + /// Translated message + late TranslatedMessage message; /// Create a new instance from a json static TranslateMessageResponse fromJson(Map json) => @@ -53,7 +55,8 @@ class TranslateMessageResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class QueryMembersResponse extends _BaseResponse { /// List of channels state returned by the query - List members; + @JsonKey(defaultValue: []) + late List members; /// Create a new instance from a json static QueryMembersResponse fromJson(Map json) => @@ -64,7 +67,8 @@ class QueryMembersResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class QueryUsersResponse extends _BaseResponse { /// List of users returned by the query - List users; + @JsonKey(defaultValue: []) + late List users; /// Create a new instance from a json static QueryUsersResponse fromJson(Map json) => @@ -75,7 +79,8 @@ class QueryUsersResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class QueryReactionsResponse extends _BaseResponse { /// List of reactions returned by the query - List reactions; + @JsonKey(defaultValue: []) + late List reactions; /// Create a new instance from a json static QueryReactionsResponse fromJson(Map json) => @@ -86,7 +91,8 @@ class QueryReactionsResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class QueryRepliesResponse extends _BaseResponse { /// List of messages returned by the api call - List messages; + @JsonKey(defaultValue: []) + late List messages; /// Create a new instance from a json static QueryRepliesResponse fromJson(Map json) => @@ -97,7 +103,8 @@ class QueryRepliesResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class ListDevicesResponse extends _BaseResponse { /// List of user devices - List devices; + @JsonKey(defaultValue: []) + late List devices; /// Create a new instance from a json static ListDevicesResponse fromJson(Map json) => @@ -108,7 +115,7 @@ class ListDevicesResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class SendFileResponse extends _BaseResponse { /// The url of the uploaded file - String file; + late String file; /// Create a new instance from a json static SendFileResponse fromJson(Map json) => @@ -119,7 +126,7 @@ class SendFileResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class SendImageResponse extends _BaseResponse { /// The url of the uploaded file - String file; + late String file; /// Create a new instance from a json static SendImageResponse fromJson(Map json) => @@ -130,10 +137,10 @@ class SendImageResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class SendReactionResponse extends _BaseResponse { /// Message returned by the api call - Message message; + late Message message; /// The reaction created by the api call - Reaction reaction; + late Reaction reaction; /// Create a new instance from a json static SendReactionResponse fromJson(Map json) => @@ -144,10 +151,10 @@ class SendReactionResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class ConnectGuestUserResponse extends _BaseResponse { /// Guest user access token - String accessToken; + late String accessToken; /// Guest user - User user; + late User user; /// Create a new instance from a json static ConnectGuestUserResponse fromJson(Map json) => @@ -158,7 +165,8 @@ class ConnectGuestUserResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class UpdateUsersResponse extends _BaseResponse { /// Updated users - Map users; + @JsonKey(defaultValue: {}) + late Map users; /// Create a new instance from a json static UpdateUsersResponse fromJson(Map json) => @@ -169,7 +177,7 @@ class UpdateUsersResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class UpdateMessageResponse extends _BaseResponse { /// Message returned by the api call - Message message; + late Message message; /// Create a new instance from a json static UpdateMessageResponse fromJson(Map json) => @@ -180,7 +188,7 @@ class UpdateMessageResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class SendMessageResponse extends _BaseResponse { /// Message returned by the api call - Message message; + late Message message; /// Create a new instance from a json static SendMessageResponse fromJson(Map json) => @@ -191,15 +199,15 @@ class SendMessageResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class GetMessageResponse extends _BaseResponse { /// Message returned by the api call - Message message; + late 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); - final jsonChannel = res.message?.extraData?.remove('channel'); + final jsonChannel = res.message.extraData.remove('channel'); if (jsonChannel != null) { res.channel = ChannelModel.fromJson(jsonChannel); } @@ -211,7 +219,8 @@ class GetMessageResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class SearchMessagesResponse extends _BaseResponse { /// List of messages returned by the api call - List results; + @JsonKey(defaultValue: []) + late List results; /// Create a new instance from a json static SearchMessagesResponse fromJson(Map json) => @@ -222,7 +231,8 @@ class SearchMessagesResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class GetMessagesByIdResponse extends _BaseResponse { /// Message returned by the api call - List messages; + @JsonKey(defaultValue: []) + late List messages; /// Create a new instance from a json static GetMessagesByIdResponse fromJson(Map json) => @@ -233,13 +243,13 @@ class GetMessagesByIdResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class UpdateChannelResponse extends _BaseResponse { /// Updated channel - ChannelModel channel; + late 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) => @@ -250,10 +260,10 @@ class UpdateChannelResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class PartialUpdateChannelResponse extends _BaseResponse { /// Updated channel - ChannelModel channel; + late ChannelModel channel; /// Channel members - List members; + List? members; /// Create a new instance from a json static PartialUpdateChannelResponse fromJson(Map json) => @@ -264,13 +274,14 @@ class PartialUpdateChannelResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class InviteMembersResponse extends _BaseResponse { /// Updated channel - ChannelModel channel; + late ChannelModel channel; /// Channel members - List members; + @JsonKey(defaultValue: []) + late List members; /// Message returned by the api call - Message message; + Message? message; /// Create a new instance from a json static InviteMembersResponse fromJson(Map json) => @@ -281,13 +292,14 @@ class InviteMembersResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class RemoveMembersResponse extends _BaseResponse { /// Updated channel - ChannelModel channel; + late ChannelModel channel; /// Channel members - List members; + @JsonKey(defaultValue: []) + late List members; /// Message returned by the api call - Message message; + Message? message; /// Create a new instance from a json static RemoveMembersResponse fromJson(Map json) => @@ -298,7 +310,7 @@ class RemoveMembersResponse extends _BaseResponse { @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) => @@ -309,13 +321,14 @@ class SendActionResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class AddMembersResponse extends _BaseResponse { /// Updated channel - ChannelModel channel; + late ChannelModel channel; /// Channel members - List members; + @JsonKey(defaultValue: []) + late List members; /// Message returned by the api call - Message message; + Message? message; /// Create a new instance from a json static AddMembersResponse fromJson(Map json) => @@ -326,13 +339,14 @@ class AddMembersResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class AcceptInviteResponse extends _BaseResponse { /// Updated channel - ChannelModel channel; + late ChannelModel channel; /// Channel members - List members; + @JsonKey(defaultValue: []) + late List members; /// Message returned by the api call - Message message; + Message? message; /// Create a new instance from a json static AcceptInviteResponse fromJson(Map json) => @@ -343,13 +357,14 @@ class AcceptInviteResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class RejectInviteResponse extends _BaseResponse { /// Updated channel - ChannelModel channel; + late ChannelModel channel; /// Channel members - List members; + @JsonKey(defaultValue: []) + late List members; /// Message returned by the api call - Message message; + Message? message; /// Create a new instance from a json static RejectInviteResponse fromJson(Map json) => @@ -368,19 +383,23 @@ class EmptyResponse extends _BaseResponse { @JsonSerializable(createToJson: false) class ChannelStateResponse extends _BaseResponse { /// Updated channel - ChannelModel channel; + late ChannelModel channel; /// List of messages returned by the api call - List messages; + @JsonKey(defaultValue: []) + late List messages; /// Channel members - List members; + @JsonKey(defaultValue: []) + late List members; /// Number of users watching the channel - int watcherCount; + @JsonKey(defaultValue: 0) + late int watcherCount; /// List of read states - List read; + @JsonKey(defaultValue: []) + late List read; /// Create a new instance from a json static ChannelStateResponse fromJson(Map json) => diff --git a/packages/stream_chat/lib/src/api/responses.g.dart b/packages/stream_chat/lib/src/api/responses.g.dart index 1f290d6e..2358593d 100644 --- a/packages/stream_chat/lib/src/api/responses.g.dart +++ b/packages/stream_chat/lib/src/api/responses.g.dart @@ -6,394 +6,274 @@ part of 'responses.dart'; // JsonSerializableGenerator // ************************************************************************** -SyncResponse _$SyncResponseFromJson(Map json) { +SyncResponse _$SyncResponseFromJson(Map json) { return SyncResponse() - ..duration = json['duration'] as String - ..events = (json['events'] as List) - ?.map((e) => e == null - ? null - : Event.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(); + ..duration = json['duration'] as String? + ..events = (json['events'] as List?) + ?.map((e) => Event.fromJson(e as Map)) + .toList() ?? + []; } -QueryChannelsResponse _$QueryChannelsResponseFromJson(Map json) { +QueryChannelsResponse _$QueryChannelsResponseFromJson( + Map json) { return QueryChannelsResponse() - ..duration = json['duration'] as String - ..channels = (json['channels'] as List) - ?.map((e) => e == null ? null : ChannelState.fromJson(e as Map)) - ?.toList(); + ..duration = json['duration'] as String? + ..channels = (json['channels'] as List?) + ?.map((e) => ChannelState.fromJson(e as Map)) + .toList() ?? + []; } -TranslateMessageResponse _$TranslateMessageResponseFromJson(Map json) { +TranslateMessageResponse _$TranslateMessageResponseFromJson( + Map json) { return TranslateMessageResponse() - ..duration = json['duration'] as String - ..message = json['message'] == null - ? null - : TranslatedMessage.fromJson((json['message'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )); + ..duration = json['duration'] as String? + ..message = + TranslatedMessage.fromJson(json['message'] as Map); } -QueryMembersResponse _$QueryMembersResponseFromJson(Map json) { +QueryMembersResponse _$QueryMembersResponseFromJson(Map json) { return QueryMembersResponse() - ..duration = json['duration'] as String - ..members = (json['members'] as List) - ?.map((e) => e == null - ? null - : Member.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(); + ..duration = json['duration'] as String? + ..members = (json['members'] as List?) + ?.map((e) => Member.fromJson(e as Map)) + .toList() ?? + []; } -QueryUsersResponse _$QueryUsersResponseFromJson(Map json) { +QueryUsersResponse _$QueryUsersResponseFromJson(Map json) { return QueryUsersResponse() - ..duration = json['duration'] as String - ..users = (json['users'] as List) - ?.map((e) => e == null - ? null - : User.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(); + ..duration = json['duration'] as String? + ..users = (json['users'] as List?) + ?.map((e) => User.fromJson(e as Map)) + .toList() ?? + []; } -QueryReactionsResponse _$QueryReactionsResponseFromJson(Map json) { +QueryReactionsResponse _$QueryReactionsResponseFromJson( + Map json) { return QueryReactionsResponse() - ..duration = json['duration'] as String - ..reactions = (json['reactions'] as List) - ?.map((e) => e == null - ? null - : Reaction.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(); + ..duration = json['duration'] as String? + ..reactions = (json['reactions'] as List?) + ?.map((e) => Reaction.fromJson(e as Map)) + .toList() ?? + []; } -QueryRepliesResponse _$QueryRepliesResponseFromJson(Map json) { +QueryRepliesResponse _$QueryRepliesResponseFromJson(Map json) { return QueryRepliesResponse() - ..duration = json['duration'] as String - ..messages = (json['messages'] as List) - ?.map((e) => e == null - ? null - : Message.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(); + ..duration = json['duration'] as String? + ..messages = (json['messages'] as List?) + ?.map((e) => Message.fromJson(e as Map)) + .toList() ?? + []; } -ListDevicesResponse _$ListDevicesResponseFromJson(Map json) { +ListDevicesResponse _$ListDevicesResponseFromJson(Map json) { return ListDevicesResponse() - ..duration = json['duration'] as String - ..devices = (json['devices'] as List) - ?.map((e) => e == null - ? null - : Device.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(); + ..duration = json['duration'] as String? + ..devices = (json['devices'] as List?) + ?.map((e) => Device.fromJson(e as Map)) + .toList() ?? + []; } -SendFileResponse _$SendFileResponseFromJson(Map json) { +SendFileResponse _$SendFileResponseFromJson(Map json) { return SendFileResponse() - ..duration = json['duration'] as String + ..duration = json['duration'] as String? ..file = json['file'] as String; } -SendImageResponse _$SendImageResponseFromJson(Map json) { +SendImageResponse _$SendImageResponseFromJson(Map json) { return SendImageResponse() - ..duration = json['duration'] as String + ..duration = json['duration'] as String? ..file = json['file'] as String; } -SendReactionResponse _$SendReactionResponseFromJson(Map json) { +SendReactionResponse _$SendReactionResponseFromJson(Map json) { return SendReactionResponse() - ..duration = json['duration'] as String - ..message = json['message'] == null - ? null - : Message.fromJson((json['message'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )) - ..reaction = json['reaction'] == null - ? null - : Reaction.fromJson((json['reaction'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )); + ..duration = json['duration'] as String? + ..message = Message.fromJson(json['message'] as Map) + ..reaction = Reaction.fromJson(json['reaction'] as Map); } -ConnectGuestUserResponse _$ConnectGuestUserResponseFromJson(Map json) { +ConnectGuestUserResponse _$ConnectGuestUserResponseFromJson( + Map json) { return ConnectGuestUserResponse() - ..duration = json['duration'] as String + ..duration = json['duration'] as String? ..accessToken = json['access_token'] as String - ..user = json['user'] == null - ? null - : User.fromJson((json['user'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )); + ..user = User.fromJson(json['user'] as Map); } -UpdateUsersResponse _$UpdateUsersResponseFromJson(Map json) { +UpdateUsersResponse _$UpdateUsersResponseFromJson(Map json) { return UpdateUsersResponse() - ..duration = json['duration'] as String - ..users = (json['users'] as Map)?.map( - (k, e) => MapEntry( - k as String, - e == null - ? null - : User.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))), - ); + ..duration = json['duration'] as String? + ..users = (json['users'] as Map?)?.map( + (k, e) => MapEntry(k, User.fromJson(e as Map)), + ) ?? + {}; } -UpdateMessageResponse _$UpdateMessageResponseFromJson(Map json) { +UpdateMessageResponse _$UpdateMessageResponseFromJson( + Map json) { return UpdateMessageResponse() - ..duration = json['duration'] as String - ..message = json['message'] == null - ? null - : Message.fromJson((json['message'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )); + ..duration = json['duration'] as String? + ..message = Message.fromJson(json['message'] as Map); } -SendMessageResponse _$SendMessageResponseFromJson(Map json) { +SendMessageResponse _$SendMessageResponseFromJson(Map json) { return SendMessageResponse() - ..duration = json['duration'] as String - ..message = json['message'] == null - ? null - : Message.fromJson((json['message'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )); + ..duration = json['duration'] as String? + ..message = Message.fromJson(json['message'] as Map); } -GetMessageResponse _$GetMessageResponseFromJson(Map json) { +GetMessageResponse _$GetMessageResponseFromJson(Map json) { return GetMessageResponse() - ..duration = json['duration'] as String - ..message = json['message'] == null - ? null - : Message.fromJson((json['message'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )) + ..duration = json['duration'] as String? + ..message = Message.fromJson(json['message'] as Map) ..channel = json['channel'] == null ? null - : ChannelModel.fromJson((json['channel'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )); + : ChannelModel.fromJson(json['channel'] as Map); } -SearchMessagesResponse _$SearchMessagesResponseFromJson(Map json) { +SearchMessagesResponse _$SearchMessagesResponseFromJson( + Map json) { return SearchMessagesResponse() - ..duration = json['duration'] as String - ..results = (json['results'] as List) - ?.map((e) => e == null ? null : GetMessageResponse.fromJson(e as Map)) - ?.toList(); + ..duration = json['duration'] as String? + ..results = (json['results'] as List?) + ?.map((e) => GetMessageResponse.fromJson(e as Map)) + .toList() ?? + []; } -GetMessagesByIdResponse _$GetMessagesByIdResponseFromJson(Map json) { +GetMessagesByIdResponse _$GetMessagesByIdResponseFromJson( + Map json) { return GetMessagesByIdResponse() - ..duration = json['duration'] as String - ..messages = (json['messages'] as List) - ?.map((e) => e == null - ? null - : Message.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(); + ..duration = json['duration'] as String? + ..messages = (json['messages'] as List?) + ?.map((e) => Message.fromJson(e as Map)) + .toList() ?? + []; } -UpdateChannelResponse _$UpdateChannelResponseFromJson(Map json) { +UpdateChannelResponse _$UpdateChannelResponseFromJson( + Map json) { return UpdateChannelResponse() - ..duration = json['duration'] as String - ..channel = json['channel'] == null - ? null - : ChannelModel.fromJson((json['channel'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )) - ..members = (json['members'] as List) - ?.map((e) => e == null - ? null - : Member.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList() + ..duration = json['duration'] as String? + ..channel = ChannelModel.fromJson(json['channel'] as Map) + ..members = (json['members'] as List?) + ?.map((e) => Member.fromJson(e as Map)) + .toList() ..message = json['message'] == null ? null - : Message.fromJson((json['message'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )); + : Message.fromJson(json['message'] as Map); } -PartialUpdateChannelResponse _$PartialUpdateChannelResponseFromJson(Map json) { +PartialUpdateChannelResponse _$PartialUpdateChannelResponseFromJson( + Map json) { return PartialUpdateChannelResponse() - ..duration = json['duration'] as String - ..channel = json['channel'] == null - ? null - : ChannelModel.fromJson((json['channel'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )) - ..members = (json['members'] as List) - ?.map((e) => e == null - ? null - : Member.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(); + ..duration = json['duration'] as String? + ..channel = ChannelModel.fromJson(json['channel'] as Map) + ..members = (json['members'] as List?) + ?.map((e) => Member.fromJson(e as Map)) + .toList(); } -InviteMembersResponse _$InviteMembersResponseFromJson(Map json) { +InviteMembersResponse _$InviteMembersResponseFromJson( + Map json) { return InviteMembersResponse() - ..duration = json['duration'] as String - ..channel = json['channel'] == null - ? null - : ChannelModel.fromJson((json['channel'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )) - ..members = (json['members'] as List) - ?.map((e) => e == null - ? null - : Member.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList() + ..duration = json['duration'] as String? + ..channel = ChannelModel.fromJson(json['channel'] as Map) + ..members = (json['members'] as List?) + ?.map((e) => Member.fromJson(e as Map)) + .toList() ?? + [] ..message = json['message'] == null ? null - : Message.fromJson((json['message'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )); + : Message.fromJson(json['message'] as Map); } -RemoveMembersResponse _$RemoveMembersResponseFromJson(Map json) { +RemoveMembersResponse _$RemoveMembersResponseFromJson( + Map json) { return RemoveMembersResponse() - ..duration = json['duration'] as String - ..channel = json['channel'] == null - ? null - : ChannelModel.fromJson((json['channel'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )) - ..members = (json['members'] as List) - ?.map((e) => e == null - ? null - : Member.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList() + ..duration = json['duration'] as String? + ..channel = ChannelModel.fromJson(json['channel'] as Map) + ..members = (json['members'] as List?) + ?.map((e) => Member.fromJson(e as Map)) + .toList() ?? + [] ..message = json['message'] == null ? null - : Message.fromJson((json['message'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )); + : Message.fromJson(json['message'] as Map); } -SendActionResponse _$SendActionResponseFromJson(Map json) { +SendActionResponse _$SendActionResponseFromJson(Map json) { return SendActionResponse() - ..duration = json['duration'] as String + ..duration = json['duration'] as String? ..message = json['message'] == null ? null - : Message.fromJson((json['message'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )); + : Message.fromJson(json['message'] as Map); } -AddMembersResponse _$AddMembersResponseFromJson(Map json) { +AddMembersResponse _$AddMembersResponseFromJson(Map json) { return AddMembersResponse() - ..duration = json['duration'] as String - ..channel = json['channel'] == null - ? null - : ChannelModel.fromJson((json['channel'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )) - ..members = (json['members'] as List) - ?.map((e) => e == null - ? null - : Member.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList() + ..duration = json['duration'] as String? + ..channel = ChannelModel.fromJson(json['channel'] as Map) + ..members = (json['members'] as List?) + ?.map((e) => Member.fromJson(e as Map)) + .toList() ?? + [] ..message = json['message'] == null ? null - : Message.fromJson((json['message'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )); + : Message.fromJson(json['message'] as Map); } -AcceptInviteResponse _$AcceptInviteResponseFromJson(Map json) { +AcceptInviteResponse _$AcceptInviteResponseFromJson(Map json) { return AcceptInviteResponse() - ..duration = json['duration'] as String - ..channel = json['channel'] == null - ? null - : ChannelModel.fromJson((json['channel'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )) - ..members = (json['members'] as List) - ?.map((e) => e == null - ? null - : Member.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList() + ..duration = json['duration'] as String? + ..channel = ChannelModel.fromJson(json['channel'] as Map) + ..members = (json['members'] as List?) + ?.map((e) => Member.fromJson(e as Map)) + .toList() ?? + [] ..message = json['message'] == null ? null - : Message.fromJson((json['message'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )); + : Message.fromJson(json['message'] as Map); } -RejectInviteResponse _$RejectInviteResponseFromJson(Map json) { +RejectInviteResponse _$RejectInviteResponseFromJson(Map json) { return RejectInviteResponse() - ..duration = json['duration'] as String - ..channel = json['channel'] == null - ? null - : ChannelModel.fromJson((json['channel'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )) - ..members = (json['members'] as List) - ?.map((e) => e == null - ? null - : Member.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList() + ..duration = json['duration'] as String? + ..channel = ChannelModel.fromJson(json['channel'] as Map) + ..members = (json['members'] as List?) + ?.map((e) => Member.fromJson(e as Map)) + .toList() ?? + [] ..message = json['message'] == null ? null - : Message.fromJson((json['message'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )); + : Message.fromJson(json['message'] as Map); } -EmptyResponse _$EmptyResponseFromJson(Map json) { - return EmptyResponse()..duration = json['duration'] as String; +EmptyResponse _$EmptyResponseFromJson(Map json) { + return EmptyResponse()..duration = json['duration'] as String?; } -ChannelStateResponse _$ChannelStateResponseFromJson(Map json) { +ChannelStateResponse _$ChannelStateResponseFromJson(Map json) { return ChannelStateResponse() - ..duration = json['duration'] as String - ..channel = json['channel'] == null - ? null - : ChannelModel.fromJson((json['channel'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )) - ..messages = (json['messages'] as List) - ?.map((e) => e == null - ? null - : Message.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList() - ..members = (json['members'] as List) - ?.map((e) => e == null - ? null - : Member.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList() - ..watcherCount = json['watcher_count'] as int - ..read = (json['read'] as List) - ?.map((e) => e == null - ? null - : Read.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(); + ..duration = json['duration'] as String? + ..channel = ChannelModel.fromJson(json['channel'] as Map) + ..messages = (json['messages'] as List?) + ?.map((e) => Message.fromJson(e as Map)) + .toList() ?? + [] + ..members = (json['members'] as List?) + ?.map((e) => Member.fromJson(e as Map)) + .toList() ?? + [] + ..watcherCount = json['watcher_count'] as int? ?? 0 + ..read = (json['read'] as List?) + ?.map((e) => Read.fromJson(e as Map)) + .toList() ?? + []; } diff --git a/packages/stream_chat/lib/src/api/retry_policy.dart b/packages/stream_chat/lib/src/api/retry_policy.dart index a4d1e7bd..f0f2c67b 100644 --- a/packages/stream_chat/lib/src/api/retry_policy.dart +++ b/packages/stream_chat/lib/src/api/retry_policy.dart @@ -1,4 +1,3 @@ -import 'package:meta/meta.dart'; import 'package:stream_chat/src/client.dart'; import 'package:stream_chat/src/exceptions.dart'; @@ -6,30 +5,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..c13db9c6 100644 --- a/packages/stream_chat/lib/src/api/retry_queue.dart +++ b/packages/stream_chat/lib/src/api/retry_queue.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'package:collection/collection.dart'; import 'package:logging/logging.dart'; -import 'package:meta/meta.dart'; import 'package:stream_chat/src/api/channel.dart'; import 'package:stream_chat/src/api/retry_policy.dart'; import 'package:stream_chat/src/event_type.dart'; @@ -14,7 +13,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,14 +27,14 @@ 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(); } })); @@ -43,12 +42,13 @@ class RetryQueue { final HeapPriorityQueue _messageQueue = HeapPriorityQueue(_byDate); bool _isRetrying = false; - RetryPolicy _retryPolicy; + RetryPolicy? _retryPolicy; /// Add a list of 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))); @@ -60,7 +60,7 @@ 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; @@ -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,24 @@ 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]); + final m = event.message; + + if (m != null) { + add([m]); + } } else if (messageIndex != -1 && [ MessageSendingStatus.sent, null, - ].contains(event.message.status)) { + ].contains(event.message!.status)) { _messageQueue.remove(messageList[messageIndex]); } } @@ -171,10 +176,14 @@ class RetryQueue { final date1 = _getMessageDate(m1); final date2 = _getMessageDate(m2); + if (date1 == null || date2 == null) { + return 0; + } + 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..8b670f7f 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,11 +27,11 @@ class WebSocket { /// Creates a new websocket /// To connect the WS call [connect] WebSocket({ - @required this.baseUrl, - this.user, - this.connectParams, - this.connectPayload, - this.handler, + required this.baseUrl, + required this.user, + required this.handler, + this.connectParams = const {}, + this.connectPayload = const {}, this.logger, this.connectFunc, this.reconnectionMonitorInterval = 1, @@ -78,12 +78,12 @@ class WebSocket { 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,43 +107,43 @@ 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; + late final 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; Event _decodeEvent(String source) => Event.fromJson(json.decode(source)); - Completer _connectionCompleter = Completer(); + Completer _connectionCompleter = Completer(); /// Connect the WS using the parameters passed in the constructor - Future connect() { + Future connect() async { _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)); _channel.stream.listen( - (data) { + (data) async { final jsonData = json.decode(data); if (jsonData['error'] != null) { return _onConnectionError(jsonData['error']); @@ -153,9 +153,7 @@ class WebSocket { onError: (error, stacktrace) { _onConnectionError(error, stacktrace); }, - onDone: () { - _onDone(); - }, + onDone: _onDone, ); return _connectionCompleter.future; } @@ -166,7 +164,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 +178,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(); @@ -204,9 +202,9 @@ class WebSocket { } 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 +223,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 +242,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 +265,7 @@ class WebSocket { } Future _reconnect() async { - logger.info('reconnect'); + logger?.info('reconnect'); if (!_reconnecting) { _reconnecting = true; _connectionStatus = ConnectionStatus.connecting; @@ -279,20 +277,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,13 +309,13 @@ class WebSocket { if (_manuallyDisconnected) { return; } - logger.info('disconnecting'); + logger?.info('disconnecting'); _connectionCompleter = Completer(); _cancelTimers(); _reconnecting = false; _manuallyDisconnected = true; _connectionStatus = ConnectionStatus.disconnected; await _connectionStatusController.close(); - return _channel.sink.close(); + await _channel.sink.close(); } } diff --git a/packages/stream_chat/lib/src/attachment_file_uploader.dart b/packages/stream_chat/lib/src/attachment_file_uploader.dart index 7d9e8c40..d00dd4e7 100644 --- a/packages/stream_chat/lib/src/attachment_file_uploader.dart +++ b/packages/stream_chat/lib/src/attachment_file_uploader.dart @@ -1,8 +1,8 @@ import 'package:dio/dio.dart'; import 'package:stream_chat/src/api/responses.dart'; import 'package:stream_chat/src/client.dart'; -import 'package:stream_chat/src/models/attachment_file.dart'; import 'package:stream_chat/src/extensions/string_extension.dart'; +import 'package:stream_chat/src/models/attachment_file.dart'; /// Class responsible for uploading images and files to a given channel abstract class AttachmentFileUploader { @@ -15,8 +15,8 @@ abstract class AttachmentFileUploader { AttachmentFile image, String channelId, String channelType, { - ProgressCallback onSendProgress, - CancelToken cancelToken, + ProgressCallback? onSendProgress, + CancelToken? cancelToken, }); /// Uploads a [file] to the given channel. @@ -28,8 +28,8 @@ abstract class AttachmentFileUploader { AttachmentFile file, String channelId, String channelType, { - ProgressCallback onSendProgress, - CancelToken cancelToken, + ProgressCallback? onSendProgress, + CancelToken? cancelToken, }); /// Deletes a image using its [url] from the given channel. @@ -40,7 +40,7 @@ abstract class AttachmentFileUploader { String url, String channelId, String channelType, { - CancelToken cancelToken, + CancelToken? cancelToken, }); /// Deletes a file using its [url] from the given channel. @@ -51,7 +51,7 @@ abstract class AttachmentFileUploader { String url, String channelId, String channelType, { - CancelToken cancelToken, + CancelToken? cancelToken, }); } @@ -67,22 +67,22 @@ class StreamAttachmentFileUploader implements AttachmentFileUploader { AttachmentFile file, String channelId, String channelType, { - ProgressCallback onSendProgress, - CancelToken cancelToken, + ProgressCallback? onSendProgress, + CancelToken? cancelToken, }) async { - final filename = file.path?.split('/')?.last ?? file.name; - final mimeType = filename.mimeType; + 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, ); @@ -104,22 +104,22 @@ class StreamAttachmentFileUploader implements AttachmentFileUploader { AttachmentFile file, String channelId, String channelType, { - ProgressCallback onSendProgress, - CancelToken cancelToken, + ProgressCallback? onSendProgress, + CancelToken? cancelToken, }) async { - final filename = file.path?.split('/')?.last ?? file.name; - final mimeType = filename.mimeType; + 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, ); @@ -141,7 +141,7 @@ class StreamAttachmentFileUploader implements AttachmentFileUploader { String url, String channelId, String channelType, { - CancelToken cancelToken, + CancelToken? cancelToken, }) async { final response = await _client.delete( '/channels/$channelType/$channelId/image', @@ -156,7 +156,7 @@ class StreamAttachmentFileUploader implements AttachmentFileUploader { String url, String channelId, String channelType, { - CancelToken cancelToken, + 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..403e5a2b 100644 --- a/packages/stream_chat/lib/src/client.dart +++ b/packages/stream_chat/lib/src/client.dart @@ -38,7 +38,7 @@ 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 { @@ -82,58 +82,59 @@ class StreamChatClient { this.tokenProvider, this.baseURL = _defaultBaseURL, this.logLevel = Level.WARNING, - this.logHandlerFunction, + LogHandlerFunction? 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); state = ClientState(this); - _setupLogger(); + _setupLogger(logHandlerFunction); _setupDio(httpClient, receiveTimeout, connectTimeout); 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; + late 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; + late 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, ) { @@ -267,9 +268,8 @@ class StreamChatClient { var stringData = options.data.toString(); if (options.data is FormData) { - final multiPart = (options.data as FormData).files[0]?.value; - stringData = - '${multiPart?.filename} - ${multiPart?.contentType}'; + final multiPart = (options.data as FormData).files[0].value; + stringData = '${multiPart.filename} - ${multiPart.contentType}'; } logger.info(''' @@ -301,11 +301,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 +341,11 @@ class StreamChatClient { ), ), ); - } catch (err) { + } on DioError { handler.reject(err); } } } - - return err; } LogHandlerFunction _getDefaultLogHandler() { @@ -373,14 +371,14 @@ class StreamChatClient { ) => Logger.detached(name) ..level = logLevel - ..onRecord.listen(logHandlerFunction ?? _getDefaultLogHandler()); + ..onRecord.listen(logHandlerFunction); - void _setupLogger() { + void _setupLogger(LogHandlerFunction? logHandlerFunction) { logger.level = logLevel; - logHandlerFunction ??= _getDefaultLogHandler(); + this.logHandlerFunction = logHandlerFunction ?? _getDefaultLogHandler(); - logger.onRecord.listen(logHandlerFunction); + logger.onRecord.listen(this.logHandlerFunction); logger.info('logger setup'); } @@ -395,7 +393,7 @@ class StreamChatClient { await _wsConnectionStatusController.close(); } - Map get _httpHeaders => { + Map get _httpHeaders => { 'Authorization': token, 'stream-auth-type': _authType, 'X-Stream-Client': _userAgent, @@ -405,12 +403,12 @@ class StreamChatClient { /// Set the current user, this triggers a connection to the API. /// It returns a [Future] that resolves when the connection is setup. @Deprecated('Use `connectUser` instead. Will be removed in Future releases') - Future setUser(User user, String token) => connectUser(user, token); + Future setUser(User user, String token) => connectUser(user, token); /// 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 +416,23 @@ class StreamChatClient { _connectCompleter = Completer(); logger.info('connect user'); + + if (user == null) { + final e = Error(); + _connectCompleter! + .completeError(e, StackTrace.fromString('No user provided.')); + throw e; + } + 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; }); } @@ -436,28 +442,29 @@ class StreamChatClient { @Deprecated( 'Use `connectUserWithProvider` instead. Will be removed in Future releases', ) - Future setUserWithProvider(User user) => connectUserWithProvider(user); + Future setUserWithProvider(User user) => + connectUserWithProvider(user); /// Connects the current user using the [tokenProvider] to fetch the token. /// It returns a [Future] that resolves when the connection is setup. - Future connectUserWithProvider(User user) async { + Future connectUserWithProvider(User user) async { if (tokenProvider == null) { throw Exception(''' TokenProvider must be provided in the constructor in order to use `connectUserWithProvider` method. 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 || @@ -475,9 +482,10 @@ class StreamChatClient { } if (!event.isLocal) { - if (_synced && event.createdAt != null) { + final createdAt = event.createdAt; + if (_synced && createdAt != null) { await _chatPersistenceClient?.updateConnectionInfo(event); - await _chatPersistenceClient?.updateLastSyncAt(event.createdAt); + await _chatPersistenceClient?.updateLastSyncAt(createdAt); } } @@ -491,10 +499,10 @@ class StreamChatClient { _controller.add(event); } - Completer _connectCompleter; + Completer? _connectCompleter; /// Connect the client websocket - Future connect() async { + Future connect() async { logger.info('connecting'); if (wsConnectionStatus == ConnectionStatus.connecting) { logger.warning('Already connecting'); @@ -510,20 +518,20 @@ 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, + 'authorization': token!, 'stream-auth-type': _authType, 'X-Stream-Client': _userAgent, }, connectPayload: { - 'user_id': state.user.id, + 'user_id': state.user!.id, 'server_determines_connection_id': true, }, handler: handleEvent, @@ -540,7 +548,7 @@ class StreamChatClient { ); if (status == ConnectionStatus.connected) { - handleEvent(Event( + handleEvent(const Event( type: EventType.connectionRecovered, online: true, )); @@ -548,7 +556,7 @@ class StreamChatClient { // ignore: unawaited_futures queryChannelsOnline(filter: { 'cid': { - '\$in': state.channels.keys.toList(), + '\$in': state.channels!.keys.toList(), }, }).then( (_) async { @@ -567,8 +575,10 @@ class StreamChatClient { var event = await _chatPersistenceClient?.getConnectionInfo(); await _ws.connect().then((e) async { - await _chatPersistenceClient?.updateConnectionInfo(e); - event = e; + if (e != null) { + await _chatPersistenceClient?.updateConnectionInfo(e); + event = e; + } await resync(); }).catchError((err, stacktrace) { logger.severe('error connecting ws', err, stacktrace); @@ -582,7 +592,7 @@ class StreamChatClient { } /// Get the events missed while offline to sync the offline storage - Future resync([List cids]) async { + Future resync([List? cids]) async { final lastSyncAt = await _chatPersistenceClient?.getLastSyncAt(); if (lastSyncAt == null) { @@ -608,7 +618,7 @@ class StreamChatClient { 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) { logger @@ -625,26 +635,26 @@ 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( - '$filter${_asMap(sort)}$options${paginationParams?.toJson()}' + '$filter${_asMap(sort)}$options${paginationParams.toJson()}' '$messageLimit', )); if (_queryChannelsStreams.containsKey(hash)) { - yield await _queryChannelsStreams[hash]; + yield await _queryChannelsStreams[hash]!; } else { final channels = await queryChannelsOffline( filter: filter, @@ -674,17 +684,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( @@ -716,9 +726,7 @@ class StreamChatClient { payload.addAll(options); } - if (paginationParams != null) { - payload.addAll(paginationParams.toJson()); - } + payload.addAll(paginationParams.toJson()); final response = await get( '/channels', @@ -732,7 +740,7 @@ class StreamChatClient { QueryChannelsResponse.fromJson, ); - if ((res.channels ?? []).isEmpty && (paginationParams?.offset ?? 0) == 0) { + if (res.channels.isEmpty && paginationParams.offset == 0) { logger.warning( ''' We could not find any channel for this query. @@ -751,15 +759,14 @@ class StreamChatClient { state._updateUsers(users); - logger.info('Got ${res.channels?.length} channels from api'); + logger.info('Got ${res.channels.length} channels from api'); final updateData = _mapChannelStateToChannel(channels); await _chatPersistenceClient?.updateChannelQueries( - filter, - channels.map((c) => c.channel.cid).toList(), - clearQueryCache: - paginationParams?.offset == null || paginationParams.offset == 0, + filter ?? {}, + channels.map((c) => c.channel!.cid).toList(), + clearQueryCache: paginationParams.offset == 0, ); state.channels = updateData.key; @@ -768,36 +775,34 @@ class StreamChatClient { /// 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; return updatedData.value; } - MapEntry, List> _mapChannelStateToChannel( + MapEntry, List> _mapChannelStateToChannel( List channelStates, ) { final channels = {...state.channels ?? {}}; final newChannels = []; - if (channelStates != null) { - for (final channelState in channelStates) { - final channel = channels[channelState.channel.cid]; - if (channel != null) { - channel.state?.updateChannelState(channelState); - newChannels.add(channel); - } else { - final newChannel = Channel.fromState(this, channelState); - channels[newChannel.cid] = newChannel; - newChannels.add(newChannel); - } + for (final channelState in channelStates) { + final channel = channels[channelState.channel!.cid]; + if (channel != null) { + channel.state!.updateChannelState(channelState); + newChannels.add(channel); + } else { + final newChannel = Channel.fromState(this, channelState); + channels[newChannel.cid] = newChannel; + newChannels.add(newChannel); } } return MapEntry(channels, newChannels); @@ -817,7 +822,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 +840,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 +860,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 +879,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 +898,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,12 +915,10 @@ 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; - } - return decoderFunction(json.decode(j)); + final data = j ?? '{}'; + return decoderFunction(json.decode(data)); } catch (error, stacktrace) { logger.severe('Error decoding response', error, stacktrace); rethrow; @@ -927,7 +930,7 @@ class StreamChatClient { String get _userAgent => 'stream-chat-dart-client-${CurrentPlatform.name}-' '${PACKAGE_VERSION.split('+')[0]}'; - Map get _commonQueryParams => { + Map get _commonQueryParams => { 'user_id': state.user?.id, 'api_key': apiKey, 'connection_id': _connectionId, @@ -937,13 +940,13 @@ class StreamChatClient { /// the API. It returns a [Future] that resolves when the connection is setup. @Deprecated( 'Use `connectAnonymousUser` instead. Will be removed in Future releases') - Future setAnonymousUser() => connectAnonymousUser(); + Future setAnonymousUser() => connectAnonymousUser(); /// Connects the current user with an anonymous id, this triggers a connection /// to the API. It returns a [Future] that resolves when the connection is /// setup. - Future connectAnonymousUser() async { - if (_connectCompleter != null && !_connectCompleter.isCompleted) { + Future connectAnonymousUser() async { + if (_connectCompleter != null && !_connectCompleter!.isCompleted) { logger.warning('Already connecting'); throw Exception('Already connecting'); } @@ -955,10 +958,10 @@ class StreamChatClient { 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; }); } @@ -967,16 +970,17 @@ class StreamChatClient { /// It returns a [Future] that resolves when the connection is setup. @Deprecated( 'Use `connectGuestUser` instead. Will be removed in Future releases') - Future setGuestUser(User user) => connectGuestUser(user); + Future setGuestUser(User user) => connectGuestUser(user); /// Connects the current user as guest, this triggers a connection to the API. /// It returns a [Future] that resolves when the connection is setup. - Future connectGuestUser(User user) async { + Future connectGuestUser(User user) async { _anonymous = true; final response = await post('/guest', data: {'user': user.toJson()}) .then((res) => decode( res.data, ConnectGuestUserResponse.fromJson)) .whenComplete(() => _anonymous = false); + return connectUser( response.user, response.accessToken, @@ -1009,16 +1013,16 @@ class StreamChatClient { Future _disconnect() async { logger.info('Client disconnecting'); - await _ws?.disconnect(); + await _ws.disconnect(); await _connectionStatusSubscription?.cancel(); } /// 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, @@ -1049,7 +1053,7 @@ class StreamChatClient { QueryUsersResponse.fromJson, ); - state?._updateUsers(response.users); + state._updateUsers(response.users); return response; } @@ -1057,13 +1061,13 @@ class StreamChatClient { /// A message 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) { + if (filters.isEmpty) { throw ArgumentError('`filters` cannot be set as null or empty'); } if (query == null && messageFilters == null) { @@ -1098,10 +1102,10 @@ class StreamChatClient { AttachmentFile file, String channelId, String channelType, { - ProgressCallback onSendProgress, - CancelToken cancelToken, + ProgressCallback? onSendProgress, + CancelToken? cancelToken, }) => - attachmentFileUploader.sendFile( + attachmentFileUploader!.sendFile( file, channelId, channelType, @@ -1114,10 +1118,10 @@ class StreamChatClient { AttachmentFile image, String channelId, String channelType, { - ProgressCallback onSendProgress, - CancelToken cancelToken, + ProgressCallback? onSendProgress, + CancelToken? cancelToken, }) => - attachmentFileUploader.sendImage( + attachmentFileUploader!.sendImage( image, channelId, channelType, @@ -1130,9 +1134,9 @@ class StreamChatClient { String url, String channelId, String channelType, { - CancelToken cancelToken, + CancelToken? cancelToken, }) => - attachmentFileUploader.deleteFile( + attachmentFileUploader!.deleteFile( url, channelId, channelType, @@ -1144,9 +1148,9 @@ class StreamChatClient { String url, String channelId, String channelType, { - CancelToken cancelToken, + CancelToken? cancelToken, }) => - attachmentFileUploader.deleteImage( + attachmentFileUploader!.deleteImage( url, channelId, channelType, @@ -1188,13 +1192,13 @@ 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']; + if (id != null && 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); @@ -1323,7 +1327,10 @@ class StreamChatClient { /// Sends the message to the given channel Future sendMessage( - Message message, String channelId, String channelType) async { + Message message, + String channelId, + String channelType, + ) async { final response = await post( '/channels/$channelType/$channelId/message', data: {'message': message.toJson()}, @@ -1359,14 +1366,13 @@ class StreamChatClient { ) { assert(() { if (timeoutOrExpirationDate is! DateTime && - timeoutOrExpirationDate is! num && - timeoutOrExpirationDate != null) { + timeoutOrExpirationDate is! num) { throw ArgumentError('Invalid timeout or Expiration date'); } return true; }(), 'Check whether time out is valid'); - DateTime pinExpires; + DateTime? pinExpires; if (timeoutOrExpirationDate is DateTime) { pinExpires = timeoutOrExpirationDate.toUtc(); } else if (timeoutOrExpirationDate is num) { @@ -1395,12 +1401,12 @@ class ClientState { .map((e) => e.me) .listen((user) { _userController.add(user); - if (user.totalUnreadCount != null) { - _totalUnreadCountController.add(user.totalUnreadCount); + if (user?.totalUnreadCount != null) { + _totalUnreadCountController.add(user?.totalUnreadCount); } - if (user.unreadChannels != null) { - _unreadChannelsController.add(user.unreadChannels); + if (user?.unreadChannels != null) { + _unreadChannelsController.add(user?.unreadChannels); } }), _client @@ -1425,23 +1431,27 @@ class ClientState { final _subscriptions = []; /// Used internally for optimistic update of unread count - set totalUnreadCount(int unreadCount) { - _totalUnreadCountController?.add(unreadCount ?? 0); + set totalUnreadCount(int? unreadCount) { + _totalUnreadCountController.add(unreadCount ?? 0); } void _listenChannelHidden() { _subscriptions.add(_client.on(EventType.channelHidden).listen((event) { - _client.chatPersistenceClient?.deleteChannels([event.cid]); + final cid = event.cid; + + if (cid != null) { + _client.chatPersistenceClient?.deleteChannels([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 +1465,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 +1476,63 @@ 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, + ...users, + 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 +1540,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..c0f85f94 100644 --- a/packages/stream_chat/lib/src/db/chat_persistence_client.dart +++ b/packages/stream_chat/lib/src/db/chat_persistence_client.dart @@ -20,7 +20,7 @@ abstract class ChatPersistenceClient { /// Get stored replies by messageId Future> getReplies( String parentId, { - PaginationParams options, + PaginationParams? options, }); /// Get stored connection event @@ -53,20 +53,20 @@ abstract class ChatPersistenceClient { /// for filtering out messages Future> getMessagesByCid( String cid, { - PaginationParams messagePagination, + PaginationParams? messagePagination, }); /// Get stored pinned [Message]s by providing channel [cid] Future> getPinnedMessagesByCid( String cid, { - PaginationParams messagePagination, + PaginationParams? messagePagination, }); /// Get [ChannelState] data by providing channel [cid] Future getChannelStateByCid( String cid, { - PaginationParams messagePagination, - PaginationParams pinnedMessagePagination, + 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. @@ -180,8 +180,11 @@ abstract class ChatPersistenceClient { .map((m) => m.id) .toList(growable: false)); + final cleanedChannelStates = + channelStates.where((it) => it.channel != null); + final deleteMembers = deleteMembersByCids( - channelStates.map((it) => it.channel.cid).toList(growable: false), + cleanedChannelStates.map((it) => it.channel!.cid).toList(growable: false), ); await Future.wait([ @@ -189,58 +192,57 @@ abstract class ChatPersistenceClient { deleteMembers, ]); - final channels = - channelStates.map((it) => it.channel).where((it) => it != null); + final channels = cleanedChannelStates + .map((it) => it.channel) + .where((it) => it != null) as Iterable; - final reactions = channelStates - .expand((it) => it.messages) - .expand((it) => [ + final reactions = + cleanedChannelStates.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) - ]) - .where((it) => it != null); + ...it.latestReactions!.where((r) => r.userId != null), + ]); - final users = channelStates + final users = cleanedChannelStates .map((cs) => [ cs.channel?.createdBy, ...cs.messages - ?.map((m) => [ + .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), + .expand((v) => v), + ...cs.read.map((r) => r.user), + ...cs.members.map((m) => m.user), ]) .expand((it) => it) - .where((it) => it != null); + .where((it) => it != null) as Iterable; - final updateMessagesFuture = channelStates.map((it) { - final cid = it.channel.cid; - final messages = it.messages.where((it) => it != null); + final updateMessagesFuture = cleanedChannelStates.map((it) { + final cid = it.channel!.cid; + final messages = it.messages; 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 updatePinnedMessagesFuture = cleanedChannelStates.map((it) { + final cid = it.channel!.cid; + final messages = it.pinnedMessages; return updatePinnedMessages(cid, messages.toList(growable: false)); }).toList(growable: false); - final updateReadsFuture = channelStates.map((it) { - final cid = it.channel.cid; - final reads = it.read?.where((it) => it != null) ?? []; + final updateReadsFuture = cleanedChannelStates.map((it) { + final cid = it.channel!.cid; + final reads = it.read; 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 updateMembersFuture = cleanedChannelStates.map((it) { + final cid = it.channel!.cid; + final members = it.members; 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..d99ab96a 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 { +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 => + Map.from(this)..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..bf54fe5c 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, + _wait = wait.inMilliseconds, _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..440e2cb6 100644 --- a/packages/stream_chat/lib/src/extensions/string_extension.dart +++ b/packages/stream_chat/lib/src/extensions/string_extension.dart @@ -4,12 +4,15 @@ import 'package:mime/mime.dart'; /// Useful extension functions for [String] extension StringX on String { /// Returns the mime type from the passed file name. - http_parser.MediaType get mimeType { - if (this == null) return null; + http_parser.MediaType? get mimeType { if (toLowerCase().endsWith('heic')) { return http_parser.MediaType.parse('image/heic'); } else { - return http_parser.MediaType.parse(lookupMimeType(this)); + final mimeType = lookupMimeType(this); + if (mimeType == null) { + return null; + } + return http_parser.MediaType.parse(mimeType); } } } diff --git a/packages/stream_chat/lib/src/models/action.dart b/packages/stream_chat/lib/src/models/action.dart index 62d0f104..16a307e7 100644 --- a/packages/stream_chat/lib/src/models/action.dart +++ b/packages/stream_chat/lib/src/models/action.dart @@ -6,7 +6,13 @@ part 'action.g.dart'; @JsonSerializable() class Action { /// Constructor used for json serialization - Action({this.name, this.style, this.text, this.type, this.value}); + Action({ + required this.name, + this.style = 'default', + required this.text, + required this.type, + this.value, + }); /// Create a new instance from a json factory Action.fromJson(Map json) => _$ActionFromJson(json); @@ -15,6 +21,7 @@ class Action { final String name; /// The style of the action + @JsonKey(defaultValue: 'default') final String style; /// The test of the action @@ -24,7 +31,7 @@ class Action { 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/action.g.dart b/packages/stream_chat/lib/src/models/action.g.dart index 3c567843..9ae999ed 100644 --- a/packages/stream_chat/lib/src/models/action.g.dart +++ b/packages/stream_chat/lib/src/models/action.g.dart @@ -6,13 +6,13 @@ part of 'action.dart'; // JsonSerializableGenerator // ************************************************************************** -Action _$ActionFromJson(Map json) { +Action _$ActionFromJson(Map json) { return Action( name: json['name'] as String, - style: json['style'] as String, + style: json['style'] as String? ?? 'default', text: json['text'] as String, type: json['type'] as String, - value: json['value'] as String, + value: json['value'] as String?, ); } diff --git a/packages/stream_chat/lib/src/models/attachment.dart b/packages/stream_chat/lib/src/models/attachment.dart index 8c9406c8..fd91454a 100644 --- a/packages/stream_chat/lib/src/models/attachment.dart +++ b/packages/stream_chat/lib/src/models/attachment.dart @@ -14,10 +14,10 @@ part 'attachment.g.dart'; class Attachment extends Equatable { /// Constructor used for json serialization Attachment({ - String id, + String? id, this.type, this.titleLink, - String title, + String? title, this.thumbUrl, this.text, this.pretext, @@ -32,17 +32,19 @@ class Attachment extends Equatable { this.authorLink, this.authorIcon, this.assetUrl, - this.actions, + List? 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, - uploadState = uploadState ?? - ((assetUrl != null || imageUrl != null) - ? const UploadState.success() - : const UploadState.preparing()); + localUri = file?.path != null ? Uri.parse(file!.path!) : null, + actions = actions ?? [] { + this.uploadState = uploadState ?? + ((assetUrl != null || imageUrl != null) + ? const UploadState.success() + : const UploadState.preparing()); + } /// Create a new instance from a json factory Attachment.fromJson(Map json) => @@ -56,59 +58,60 @@ class Attachment extends Equatable { ///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 + @JsonKey(defaultValue: []) 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 - final UploadState uploadState; + late final UploadState uploadState; /// Map of custom channel extraData @JsonKey(includeIfNull: false) - final Map extraData; + final Map? extraData; /// The attachment ID. /// @@ -147,37 +150,37 @@ class Attachment extends Equatable { ]; /// Serialize to json - Map toJson() => Serialization.moveFromExtraDataToRoot( - _$AttachmentToJson(this), topLevelFields) - ..removeWhere((key, value) => dbSpecificTopLevelFields.contains(key)); + Map toJson() => + Serialization.moveFromExtraDataToRoot(_$AttachmentToJson(this)) + ..removeWhere((key, value) => dbSpecificTopLevelFields.contains(key)); /// Serialize to db data - Map toData() => Serialization.moveFromExtraDataToRoot( - _$AttachmentToJson(this), topLevelFields + dbSpecificTopLevelFields); + Map toData() => + Serialization.moveFromExtraDataToRoot(_$AttachmentToJson(this)); 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, @@ -205,7 +208,7 @@ class Attachment extends Equatable { ); @override - List get props => [ + List get props => [ id, type, titleLink, diff --git a/packages/stream_chat/lib/src/models/attachment.g.dart b/packages/stream_chat/lib/src/models/attachment.g.dart index c45771c2..b7a7304d 100644 --- a/packages/stream_chat/lib/src/models/attachment.g.dart +++ b/packages/stream_chat/lib/src/models/attachment.g.dart @@ -6,46 +6,37 @@ part of 'attachment.dart'; // JsonSerializableGenerator // ************************************************************************** -Attachment _$AttachmentFromJson(Map json) { +Attachment _$AttachmentFromJson(Map json) { return Attachment( - id: json['id'] as String, - type: json['type'] as String, - titleLink: json['title_link'] as String, - title: json['title'] as String, - thumbUrl: json['thumb_url'] as String, - text: json['text'] as String, - pretext: json['pretext'] as String, - ogScrapeUrl: json['og_scrape_url'] as String, - imageUrl: json['image_url'] as String, - footerIcon: json['footer_icon'] as String, - footer: json['footer'] as String, + id: json['id'] as String?, + type: json['type'] as String?, + titleLink: json['title_link'] as String?, + title: json['title'] as String?, + thumbUrl: json['thumb_url'] as String?, + text: json['text'] as String?, + pretext: json['pretext'] as String?, + ogScrapeUrl: json['og_scrape_url'] as String?, + imageUrl: json['image_url'] as String?, + footerIcon: json['footer_icon'] as String?, + footer: json['footer'] as String?, fields: json['fields'], - fallback: json['fallback'] as String, - color: json['color'] as String, - authorName: json['author_name'] as String, - authorLink: json['author_link'] as String, - authorIcon: json['author_icon'] as String, - assetUrl: json['asset_url'] as String, - actions: (json['actions'] as List) - ?.map((e) => e == null - ? null - : Action.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(), - extraData: (json['extra_data'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - ), + fallback: json['fallback'] as String?, + color: json['color'] as String?, + authorName: json['author_name'] as String?, + authorLink: json['author_link'] as String?, + authorIcon: json['author_icon'] as String?, + assetUrl: json['asset_url'] as String?, + actions: (json['actions'] as List?) + ?.map((e) => Action.fromJson(e as Map)) + .toList() ?? + [], + extraData: json['extra_data'] as Map?, file: json['file'] == null ? null - : AttachmentFile.fromJson((json['file'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), + : AttachmentFile.fromJson(json['file'] as Map), uploadState: json['upload_state'] == null ? null - : UploadState.fromJson((json['upload_state'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), + : UploadState.fromJson(json['upload_state'] as Map), ); } @@ -75,10 +66,10 @@ Map _$AttachmentToJson(Attachment instance) { writeNotNull('author_link', instance.authorLink); writeNotNull('author_icon', instance.authorIcon); writeNotNull('asset_url', instance.assetUrl); - writeNotNull('actions', instance.actions?.map((e) => e?.toJson())?.toList()); + val['actions'] = instance.actions.map((e) => e.toJson()).toList(); writeNotNull('file', instance.file?.toJson()); - writeNotNull('upload_state', instance.uploadState?.toJson()); + val['upload_state'] = instance.uploadState.toJson(); writeNotNull('extra_data', instance.extraData); - writeNotNull('id', instance.id); + val['id'] = instance.id; return val; } diff --git a/packages/stream_chat/lib/src/models/attachment_file.dart b/packages/stream_chat/lib/src/models/attachment_file.dart index bae7810f..d72cecbd 100644 --- a/packages/stream_chat/lib/src/models/attachment_file.dart +++ b/packages/stream_chat/lib/src/models/attachment_file.dart @@ -8,18 +8,21 @@ part 'attachment_file.g.dart'; /// Union class to hold various [UploadState] of a attachment. @freezed -abstract class UploadState with _$UploadState { +class UploadState with _$UploadState { /// Preparing state of the union const factory UploadState.preparing() = Preparing; /// InProgress state of the union - const factory UploadState.inProgress({int uploaded, int total}) = InProgress; + const factory UploadState.inProgress({ + required int uploaded, + required int total, + }) = InProgress; /// Success state of the union 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 +30,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; @@ -41,9 +44,15 @@ extension UploadStateX on UploadState { bool get isFailed => this is Failed; } -Uint8List _fromString(String bytes) => Uint8List.fromList(bytes.codeUnits); +Uint8List? _fromString(String? bytes) { + if (bytes == null) return null; + return Uint8List.fromList(bytes.codeUnits); +} -String _toString(Uint8List bytes) => String.fromCharCodes(bytes); +String? _toString(Uint8List? bytes) { + if (bytes == null) return null; + return String.fromCharCodes(bytes); +} /// The class that contains the information about an attachment file @JsonSerializable() @@ -54,7 +63,10 @@ class AttachmentFile { this.name, this.bytes, this.size, - }); + }) : assert( + path != null || bytes != null, + 'Either path or bytes should be != null', + ); /// Create a new instance from a json factory AttachmentFile.fromJson(Map json) => @@ -65,21 +77,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/attachment_file.freezed.dart b/packages/stream_chat/lib/src/models/attachment_file.freezed.dart index 4eea1f13..a6c5e0c6 100644 --- a/packages/stream_chat/lib/src/models/attachment_file.freezed.dart +++ b/packages/stream_chat/lib/src/models/attachment_file.freezed.dart @@ -1,5 +1,5 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides part of 'attachment_file.dart'; @@ -8,6 +8,10 @@ part of 'attachment_file.dart'; // ************************************************************************** T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + UploadState _$UploadStateFromJson(Map json) { switch (json['runtimeType'] as String) { case 'preparing': @@ -28,74 +32,72 @@ UploadState _$UploadStateFromJson(Map json) { class _$UploadStateTearOff { const _$UploadStateTearOff(); -// ignore: unused_element Preparing preparing() { return const Preparing(); } -// ignore: unused_element - InProgress inProgress({int uploaded, int total}) { + InProgress inProgress({required int uploaded, required int total}) { return InProgress( uploaded: uploaded, total: total, ); } -// ignore: unused_element Success success() { return const Success(); } -// ignore: unused_element - Failed failed({@required String error}) { + Failed failed({required String error}) { return Failed( error: error, ); } -// ignore: unused_element UploadState fromJson(Map json) { return UploadState.fromJson(json); } } /// @nodoc -// ignore: unused_element const $UploadState = _$UploadStateTearOff(); /// @nodoc mixin _$UploadState { @optionalTypeArgs - TResult when({ - @required TResult preparing(), - @required TResult inProgress(int uploaded, int total), - @required TResult success(), - @required TResult failed(String error), - }); + TResult when({ + required TResult Function() preparing, + required TResult Function(int uploaded, int total) inProgress, + required TResult Function() success, + required TResult Function(String error) failed, + }) => + throw _privateConstructorUsedError; @optionalTypeArgs - TResult maybeWhen({ - TResult preparing(), - TResult inProgress(int uploaded, int total), - TResult success(), - TResult failed(String error), - @required TResult orElse(), - }); + TResult maybeWhen({ + TResult Function()? preparing, + TResult Function(int uploaded, int total)? inProgress, + TResult Function()? success, + TResult Function(String error)? failed, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; @optionalTypeArgs - TResult map({ - @required TResult preparing(Preparing value), - @required TResult inProgress(InProgress value), - @required TResult success(Success value), - @required TResult failed(Failed value), - }); + TResult map({ + required TResult Function(Preparing value) preparing, + required TResult Function(InProgress value) inProgress, + required TResult Function(Success value) success, + required TResult Function(Failed value) failed, + }) => + throw _privateConstructorUsedError; @optionalTypeArgs - TResult maybeMap({ - TResult preparing(Preparing value), - TResult inProgress(InProgress value), - TResult success(Success value), - TResult failed(Failed value), - @required TResult orElse(), - }); - Map toJson(); + TResult maybeMap({ + TResult Function(Preparing value)? preparing, + TResult Function(InProgress value)? inProgress, + TResult Function(Success value)? success, + TResult Function(Failed value)? failed, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + Map toJson() => throw _privateConstructorUsedError; } /// @nodoc @@ -154,29 +156,24 @@ class _$Preparing implements Preparing { @override @optionalTypeArgs - TResult when({ - @required TResult preparing(), - @required TResult inProgress(int uploaded, int total), - @required TResult success(), - @required TResult failed(String error), + TResult when({ + required TResult Function() preparing, + required TResult Function(int uploaded, int total) inProgress, + required TResult Function() success, + required TResult Function(String error) failed, }) { - assert(preparing != null); - assert(inProgress != null); - assert(success != null); - assert(failed != null); return preparing(); } @override @optionalTypeArgs - TResult maybeWhen({ - TResult preparing(), - TResult inProgress(int uploaded, int total), - TResult success(), - TResult failed(String error), - @required TResult orElse(), + TResult maybeWhen({ + TResult Function()? preparing, + TResult Function(int uploaded, int total)? inProgress, + TResult Function()? success, + TResult Function(String error)? failed, + required TResult orElse(), }) { - assert(orElse != null); if (preparing != null) { return preparing(); } @@ -185,29 +182,24 @@ class _$Preparing implements Preparing { @override @optionalTypeArgs - TResult map({ - @required TResult preparing(Preparing value), - @required TResult inProgress(InProgress value), - @required TResult success(Success value), - @required TResult failed(Failed value), + TResult map({ + required TResult Function(Preparing value) preparing, + required TResult Function(InProgress value) inProgress, + required TResult Function(Success value) success, + required TResult Function(Failed value) failed, }) { - assert(preparing != null); - assert(inProgress != null); - assert(success != null); - assert(failed != null); return preparing(this); } @override @optionalTypeArgs - TResult maybeMap({ - TResult preparing(Preparing value), - TResult inProgress(InProgress value), - TResult success(Success value), - TResult failed(Failed value), - @required TResult orElse(), + TResult maybeMap({ + TResult Function(Preparing value)? preparing, + TResult Function(InProgress value)? inProgress, + TResult Function(Success value)? success, + TResult Function(Failed value)? failed, + required TResult orElse(), }) { - assert(orElse != null); if (preparing != null) { return preparing(this); } @@ -245,12 +237,18 @@ class _$InProgressCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res> @override $Res call({ - Object uploaded = freezed, - Object total = freezed, + Object? uploaded = freezed, + Object? total = freezed, }) { return _then(InProgress( - uploaded: uploaded == freezed ? _value.uploaded : uploaded as int, - total: total == freezed ? _value.total : total as int, + uploaded: uploaded == freezed + ? _value.uploaded + : uploaded // ignore: cast_nullable_to_non_nullable + as int, + total: total == freezed + ? _value.total + : total // ignore: cast_nullable_to_non_nullable + as int, )); } } @@ -259,7 +257,7 @@ class _$InProgressCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res> /// @nodoc class _$InProgress implements InProgress { - const _$InProgress({this.uploaded, this.total}); + const _$InProgress({required this.uploaded, required this.total}); factory _$InProgress.fromJson(Map json) => _$_$InProgressFromJson(json); @@ -298,29 +296,24 @@ class _$InProgress implements InProgress { @override @optionalTypeArgs - TResult when({ - @required TResult preparing(), - @required TResult inProgress(int uploaded, int total), - @required TResult success(), - @required TResult failed(String error), + TResult when({ + required TResult Function() preparing, + required TResult Function(int uploaded, int total) inProgress, + required TResult Function() success, + required TResult Function(String error) failed, }) { - assert(preparing != null); - assert(inProgress != null); - assert(success != null); - assert(failed != null); return inProgress(uploaded, total); } @override @optionalTypeArgs - TResult maybeWhen({ - TResult preparing(), - TResult inProgress(int uploaded, int total), - TResult success(), - TResult failed(String error), - @required TResult orElse(), + TResult maybeWhen({ + TResult Function()? preparing, + TResult Function(int uploaded, int total)? inProgress, + TResult Function()? success, + TResult Function(String error)? failed, + required TResult orElse(), }) { - assert(orElse != null); if (inProgress != null) { return inProgress(uploaded, total); } @@ -329,29 +322,24 @@ class _$InProgress implements InProgress { @override @optionalTypeArgs - TResult map({ - @required TResult preparing(Preparing value), - @required TResult inProgress(InProgress value), - @required TResult success(Success value), - @required TResult failed(Failed value), + TResult map({ + required TResult Function(Preparing value) preparing, + required TResult Function(InProgress value) inProgress, + required TResult Function(Success value) success, + required TResult Function(Failed value) failed, }) { - assert(preparing != null); - assert(inProgress != null); - assert(success != null); - assert(failed != null); return inProgress(this); } @override @optionalTypeArgs - TResult maybeMap({ - TResult preparing(Preparing value), - TResult inProgress(InProgress value), - TResult success(Success value), - TResult failed(Failed value), - @required TResult orElse(), + TResult maybeMap({ + TResult Function(Preparing value)? preparing, + TResult Function(InProgress value)? inProgress, + TResult Function(Success value)? success, + TResult Function(Failed value)? failed, + required TResult orElse(), }) { - assert(orElse != null); if (inProgress != null) { return inProgress(this); } @@ -365,15 +353,17 @@ class _$InProgress implements InProgress { } abstract class InProgress implements UploadState { - const factory InProgress({int uploaded, int total}) = _$InProgress; + const factory InProgress({required int uploaded, required int total}) = + _$InProgress; factory InProgress.fromJson(Map json) = _$InProgress.fromJson; - int get uploaded; - int get total; + int get uploaded => throw _privateConstructorUsedError; + int get total => throw _privateConstructorUsedError; @JsonKey(ignore: true) - $InProgressCopyWith get copyWith; + $InProgressCopyWith get copyWith => + throw _privateConstructorUsedError; } /// @nodoc @@ -416,29 +406,24 @@ class _$Success implements Success { @override @optionalTypeArgs - TResult when({ - @required TResult preparing(), - @required TResult inProgress(int uploaded, int total), - @required TResult success(), - @required TResult failed(String error), + TResult when({ + required TResult Function() preparing, + required TResult Function(int uploaded, int total) inProgress, + required TResult Function() success, + required TResult Function(String error) failed, }) { - assert(preparing != null); - assert(inProgress != null); - assert(success != null); - assert(failed != null); return success(); } @override @optionalTypeArgs - TResult maybeWhen({ - TResult preparing(), - TResult inProgress(int uploaded, int total), - TResult success(), - TResult failed(String error), - @required TResult orElse(), + TResult maybeWhen({ + TResult Function()? preparing, + TResult Function(int uploaded, int total)? inProgress, + TResult Function()? success, + TResult Function(String error)? failed, + required TResult orElse(), }) { - assert(orElse != null); if (success != null) { return success(); } @@ -447,29 +432,24 @@ class _$Success implements Success { @override @optionalTypeArgs - TResult map({ - @required TResult preparing(Preparing value), - @required TResult inProgress(InProgress value), - @required TResult success(Success value), - @required TResult failed(Failed value), + TResult map({ + required TResult Function(Preparing value) preparing, + required TResult Function(InProgress value) inProgress, + required TResult Function(Success value) success, + required TResult Function(Failed value) failed, }) { - assert(preparing != null); - assert(inProgress != null); - assert(success != null); - assert(failed != null); return success(this); } @override @optionalTypeArgs - TResult maybeMap({ - TResult preparing(Preparing value), - TResult inProgress(InProgress value), - TResult success(Success value), - TResult failed(Failed value), - @required TResult orElse(), + TResult maybeMap({ + TResult Function(Preparing value)? preparing, + TResult Function(InProgress value)? inProgress, + TResult Function(Success value)? success, + TResult Function(Failed value)? failed, + required TResult orElse(), }) { - assert(orElse != null); if (success != null) { return success(this); } @@ -506,10 +486,13 @@ class _$FailedCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res> @override $Res call({ - Object error = freezed, + Object? error = freezed, }) { return _then(Failed( - error: error == freezed ? _value.error : error as String, + error: error == freezed + ? _value.error + : error // ignore: cast_nullable_to_non_nullable + as String, )); } } @@ -518,7 +501,7 @@ class _$FailedCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res> /// @nodoc class _$Failed implements Failed { - const _$Failed({@required this.error}) : assert(error != null); + const _$Failed({required this.error}); factory _$Failed.fromJson(Map json) => _$_$FailedFromJson(json); @@ -550,29 +533,24 @@ class _$Failed implements Failed { @override @optionalTypeArgs - TResult when({ - @required TResult preparing(), - @required TResult inProgress(int uploaded, int total), - @required TResult success(), - @required TResult failed(String error), + TResult when({ + required TResult Function() preparing, + required TResult Function(int uploaded, int total) inProgress, + required TResult Function() success, + required TResult Function(String error) failed, }) { - assert(preparing != null); - assert(inProgress != null); - assert(success != null); - assert(failed != null); return failed(error); } @override @optionalTypeArgs - TResult maybeWhen({ - TResult preparing(), - TResult inProgress(int uploaded, int total), - TResult success(), - TResult failed(String error), - @required TResult orElse(), + TResult maybeWhen({ + TResult Function()? preparing, + TResult Function(int uploaded, int total)? inProgress, + TResult Function()? success, + TResult Function(String error)? failed, + required TResult orElse(), }) { - assert(orElse != null); if (failed != null) { return failed(error); } @@ -581,29 +559,24 @@ class _$Failed implements Failed { @override @optionalTypeArgs - TResult map({ - @required TResult preparing(Preparing value), - @required TResult inProgress(InProgress value), - @required TResult success(Success value), - @required TResult failed(Failed value), + TResult map({ + required TResult Function(Preparing value) preparing, + required TResult Function(InProgress value) inProgress, + required TResult Function(Success value) success, + required TResult Function(Failed value) failed, }) { - assert(preparing != null); - assert(inProgress != null); - assert(success != null); - assert(failed != null); return failed(this); } @override @optionalTypeArgs - TResult maybeMap({ - TResult preparing(Preparing value), - TResult inProgress(InProgress value), - TResult success(Success value), - TResult failed(Failed value), - @required TResult orElse(), + TResult maybeMap({ + TResult Function(Preparing value)? preparing, + TResult Function(InProgress value)? inProgress, + TResult Function(Success value)? success, + TResult Function(Failed value)? failed, + required TResult orElse(), }) { - assert(orElse != null); if (failed != null) { return failed(this); } @@ -617,11 +590,11 @@ class _$Failed implements Failed { } abstract class Failed implements UploadState { - const factory Failed({@required String error}) = _$Failed; + const factory Failed({required String error}) = _$Failed; factory Failed.fromJson(Map json) = _$Failed.fromJson; - String get error; + String get error => throw _privateConstructorUsedError; @JsonKey(ignore: true) - $FailedCopyWith get copyWith; + $FailedCopyWith get copyWith => throw _privateConstructorUsedError; } diff --git a/packages/stream_chat/lib/src/models/attachment_file.g.dart b/packages/stream_chat/lib/src/models/attachment_file.g.dart index c6716fba..55fcea6c 100644 --- a/packages/stream_chat/lib/src/models/attachment_file.g.dart +++ b/packages/stream_chat/lib/src/models/attachment_file.g.dart @@ -6,12 +6,12 @@ part of 'attachment_file.dart'; // JsonSerializableGenerator // ************************************************************************** -AttachmentFile _$AttachmentFileFromJson(Map json) { +AttachmentFile _$AttachmentFileFromJson(Map json) { return AttachmentFile( - path: json['path'] as String, - name: json['name'] as String, - bytes: _fromString(json['bytes'] as String), - size: json['size'] as int, + path: json['path'] as String?, + name: json['name'] as String?, + bytes: _fromString(json['bytes'] as String?), + size: json['size'] as int?, ); } @@ -23,14 +23,14 @@ Map _$AttachmentFileToJson(AttachmentFile instance) => 'size': instance.size, }; -_$Preparing _$_$PreparingFromJson(Map json) { +_$Preparing _$_$PreparingFromJson(Map json) { return _$Preparing(); } Map _$_$PreparingToJson(_$Preparing instance) => {}; -_$InProgress _$_$InProgressFromJson(Map json) { +_$InProgress _$_$InProgressFromJson(Map json) { return _$InProgress( uploaded: json['uploaded'] as int, total: json['total'] as int, @@ -43,14 +43,14 @@ Map _$_$InProgressToJson(_$InProgress instance) => 'total': instance.total, }; -_$Success _$_$SuccessFromJson(Map json) { +_$Success _$_$SuccessFromJson(Map json) { return _$Success(); } Map _$_$SuccessToJson(_$Success instance) => {}; -_$Failed _$_$FailedFromJson(Map json) { +_$Failed _$_$FailedFromJson(Map json) { return _$Failed( error: json['error'] as String, ); diff --git a/packages/stream_chat/lib/src/models/channel_config.dart b/packages/stream_chat/lib/src/models/channel_config.dart index 3541573e..9ba36e4f 100644 --- a/packages/stream_chat/lib/src/models/channel_config.dart +++ b/packages/stream_chat/lib/src/models/channel_config.dart @@ -1,5 +1,6 @@ import 'package:json_annotation/json_annotation.dart'; import 'package:stream_chat/src/models/command.dart'; + part 'channel_config.g.dart'; /// The class that contains the information about the configuration of a channel @@ -7,35 +8,38 @@ part 'channel_config.g.dart'; class ChannelConfig { /// Constructor used for json serialization ChannelConfig({ - this.automod, - this.commands, - this.connectEvents, - this.createdAt, - this.updatedAt, - this.maxMessageLength, - this.messageRetention, - this.mutes, - this.name, - this.reactions, - this.readEvents, - this.replies, - this.search, - this.typingEvents, - this.uploads, - this.urlEnrichment, - }); + this.automod = 'flag', + this.commands = const [], + this.connectEvents = false, + DateTime? createdAt, + DateTime? updatedAt, + this.maxMessageLength = 0, + this.messageRetention = '', + this.mutes = false, + this.reactions = false, + this.readEvents = false, + this.replies = false, + this.search = false, + this.typingEvents = false, + this.uploads = false, + this.urlEnrichment = false, + }) : createdAt = createdAt ?? DateTime.now(), + updatedAt = updatedAt ?? DateTime.now(); /// Create a new instance from a json factory ChannelConfig.fromJson(Map json) => _$ChannelConfigFromJson(json); /// Moderation configuration + @JsonKey(defaultValue: 'flag') final String automod; /// List of available commands + @JsonKey(defaultValue: []) final List commands; /// True if the channel should send connect events + @JsonKey(defaultValue: false) final bool connectEvents; /// Date of channel creation @@ -45,36 +49,43 @@ class ChannelConfig { final DateTime updatedAt; /// Max channel message length + @JsonKey(defaultValue: 0) final int maxMessageLength; /// Duration of message retention + @JsonKey(defaultValue: '') final String messageRetention; /// True if users can be muted + @JsonKey(defaultValue: false) final bool mutes; - /// Name of the channel - final String name; - /// True if reaction are active for this channel + @JsonKey(defaultValue: false) final bool reactions; /// True if readEvents are active for this channel + @JsonKey(defaultValue: false) final bool readEvents; /// True if reply message are active for this channel + @JsonKey(defaultValue: false) final bool replies; /// True if it's possible to perform a search in this channel + @JsonKey(defaultValue: false) final bool search; /// True if typing events should be sent for this channel + @JsonKey(defaultValue: false) final bool typingEvents; /// True if it's possible to upload files to this channel + @JsonKey(defaultValue: false) final bool uploads; /// True if urls appears as attachments + @JsonKey(defaultValue: false) final bool urlEnrichment; /// Serialize to json diff --git a/packages/stream_chat/lib/src/models/channel_config.g.dart b/packages/stream_chat/lib/src/models/channel_config.g.dart index e8152c80..723281c0 100644 --- a/packages/stream_chat/lib/src/models/channel_config.g.dart +++ b/packages/stream_chat/lib/src/models/channel_config.g.dart @@ -6,48 +6,43 @@ part of 'channel_config.dart'; // JsonSerializableGenerator // ************************************************************************** -ChannelConfig _$ChannelConfigFromJson(Map json) { +ChannelConfig _$ChannelConfigFromJson(Map json) { return ChannelConfig( - automod: json['automod'] as String, - commands: (json['commands'] as List) - ?.map((e) => e == null - ? null - : Command.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(), - connectEvents: json['connect_events'] as bool, + automod: json['automod'] as String? ?? 'flag', + commands: (json['commands'] as List?) + ?.map((e) => Command.fromJson(e as Map)) + .toList() ?? + [], + connectEvents: json['connect_events'] as bool? ?? false, createdAt: json['created_at'] == null ? null : DateTime.parse(json['created_at'] as String), updatedAt: json['updated_at'] == null ? null : DateTime.parse(json['updated_at'] as String), - maxMessageLength: json['max_message_length'] as int, - messageRetention: json['message_retention'] as String, - mutes: json['mutes'] as bool, - name: json['name'] as String, - reactions: json['reactions'] as bool, - readEvents: json['read_events'] as bool, - replies: json['replies'] as bool, - search: json['search'] as bool, - typingEvents: json['typing_events'] as bool, - uploads: json['uploads'] as bool, - urlEnrichment: json['url_enrichment'] as bool, + maxMessageLength: json['max_message_length'] as int? ?? 0, + messageRetention: json['message_retention'] as String? ?? '', + mutes: json['mutes'] as bool? ?? false, + reactions: json['reactions'] as bool? ?? false, + readEvents: json['read_events'] as bool? ?? false, + replies: json['replies'] as bool? ?? false, + search: json['search'] as bool? ?? false, + typingEvents: json['typing_events'] as bool? ?? false, + uploads: json['uploads'] as bool? ?? false, + urlEnrichment: json['url_enrichment'] as bool? ?? false, ); } Map _$ChannelConfigToJson(ChannelConfig instance) => { 'automod': instance.automod, - 'commands': instance.commands?.map((e) => e?.toJson())?.toList(), + 'commands': instance.commands.map((e) => e.toJson()).toList(), 'connect_events': instance.connectEvents, - 'created_at': instance.createdAt?.toIso8601String(), - 'updated_at': instance.updatedAt?.toIso8601String(), + 'created_at': instance.createdAt.toIso8601String(), + 'updated_at': instance.updatedAt.toIso8601String(), 'max_message_length': instance.maxMessageLength, 'message_retention': instance.messageRetention, 'mutes': instance.mutes, - 'name': instance.name, 'reactions': instance.reactions, 'read_events': instance.readEvents, 'replies': instance.replies, diff --git a/packages/stream_chat/lib/src/models/channel_model.dart b/packages/stream_chat/lib/src/models/channel_model.dart index 62ea1853..d68edbde 100644 --- a/packages/stream_chat/lib/src/models/channel_model.dart +++ b/packages/stream_chat/lib/src/models/channel_model.dart @@ -10,20 +10,29 @@ part 'channel_model.g.dart'; class ChannelModel { /// Constructor used for json serialization ChannelModel({ - this.id, - this.type, - this.cid, - this.config, + String? id, + String? type, + String? cid, + ChannelConfig? config, this.createdBy, - this.frozen, + this.frozen = false, this.lastMessageAt, - this.createdAt, - this.updatedAt, + DateTime? createdAt, + DateTime? updatedAt, this.deletedAt, - this.memberCount, + this.memberCount = 0, this.extraData, this.team, - }); + }) : config = config ?? ChannelConfig(), + createdAt = createdAt ?? DateTime.now(), + updatedAt = updatedAt ?? DateTime.now(), + assert( + cid != null || (id != null && type != null), + 'provide either a cid or an id and type', + ), + id = id ?? cid!.split(':')[1], + type = type ?? cid!.split(':')[0], + cid = cid ?? '$type:$id'; /// Create a new instance from a json factory ChannelModel.fromJson(Map json) => @@ -46,15 +55,15 @@ class ChannelModel { /// 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) + @JsonKey(includeIfNull: false, defaultValue: false) 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) @@ -66,19 +75,20 @@ class ChannelModel { /// 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) + @JsonKey( + includeIfNull: false, toJson: Serialization.readOnly, defaultValue: 0) 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,30 +108,29 @@ 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( _$ChannelModelToJson(this), - topLevelFields, ); /// 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 +150,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_model.g.dart b/packages/stream_chat/lib/src/models/channel_model.g.dart index 4f535f46..e71112b6 100644 --- a/packages/stream_chat/lib/src/models/channel_model.g.dart +++ b/packages/stream_chat/lib/src/models/channel_model.g.dart @@ -6,22 +6,18 @@ part of 'channel_model.dart'; // JsonSerializableGenerator // ************************************************************************** -ChannelModel _$ChannelModelFromJson(Map json) { +ChannelModel _$ChannelModelFromJson(Map json) { return ChannelModel( - id: json['id'] as String, - type: json['type'] as String, - cid: json['cid'] as String, + id: json['id'] as String?, + type: json['type'] as String?, + cid: json['cid'] as String?, config: json['config'] == null ? null - : ChannelConfig.fromJson((json['config'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), + : ChannelConfig.fromJson(json['config'] as Map), createdBy: json['created_by'] == null ? null - : User.fromJson((json['created_by'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), - frozen: json['frozen'] as bool, + : User.fromJson(json['created_by'] as Map), + frozen: json['frozen'] as bool? ?? false, lastMessageAt: json['last_message_at'] == null ? null : DateTime.parse(json['last_message_at'] as String), @@ -34,11 +30,9 @@ ChannelModel _$ChannelModelFromJson(Map json) { deletedAt: json['deleted_at'] == null ? null : DateTime.parse(json['deleted_at'] as String), - memberCount: json['member_count'] as int, - extraData: (json['extra_data'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - ), - team: json['team'] as String, + memberCount: json['member_count'] as int? ?? 0, + extraData: json['extra_data'] as Map?, + team: json['team'] as String?, ); } @@ -57,7 +51,7 @@ Map _$ChannelModelToJson(ChannelModel instance) { writeNotNull('cid', readonly(instance.cid)); writeNotNull('config', readonly(instance.config)); writeNotNull('created_by', readonly(instance.createdBy)); - writeNotNull('frozen', instance.frozen); + val['frozen'] = instance.frozen; writeNotNull('last_message_at', readonly(instance.lastMessageAt)); writeNotNull('created_at', readonly(instance.createdAt)); writeNotNull('updated_at', readonly(instance.updatedAt)); diff --git a/packages/stream_chat/lib/src/models/channel_state.dart b/packages/stream_chat/lib/src/models/channel_state.dart index 3a1f3178..9d18f434 100644 --- a/packages/stream_chat/lib/src/models/channel_state.dart +++ b/packages/stream_chat/lib/src/models/channel_state.dart @@ -22,24 +22,29 @@ class ChannelState { }); /// The channel to which this state belongs - final ChannelModel channel; + final ChannelModel? channel; /// A paginated list of channel messages + @JsonKey(defaultValue: []) final List messages; /// A paginated list of channel members + @JsonKey(defaultValue: []) final List members; /// A paginated list of pinned messages + @JsonKey(defaultValue: []) final List pinnedMessages; /// The count of users watching the channel - final int watcherCount; + final int? watcherCount; /// A paginated list of users watching the channel + @JsonKey(defaultValue: []) final List watchers; /// The list of channel reads + @JsonKey(defaultValue: []) final List read; /// Create a new instance from a json @@ -51,13 +56,13 @@ class ChannelState { /// 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/channel_state.g.dart b/packages/stream_chat/lib/src/models/channel_state.g.dart index a66899a4..39c6373f 100644 --- a/packages/stream_chat/lib/src/models/channel_state.g.dart +++ b/packages/stream_chat/lib/src/models/channel_state.g.dart @@ -6,60 +6,43 @@ part of 'channel_state.dart'; // JsonSerializableGenerator // ************************************************************************** -ChannelState _$ChannelStateFromJson(Map json) { +ChannelState _$ChannelStateFromJson(Map json) { return ChannelState( channel: json['channel'] == null ? null - : ChannelModel.fromJson((json['channel'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), - messages: (json['messages'] as List) - ?.map((e) => e == null - ? null - : Message.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(), - members: (json['members'] as List) - ?.map((e) => e == null - ? null - : Member.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(), - pinnedMessages: (json['pinned_messages'] as List) - ?.map((e) => e == null - ? null - : Message.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(), - watcherCount: json['watcher_count'] as int, - watchers: (json['watchers'] as List) - ?.map((e) => e == null - ? null - : User.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(), - read: (json['read'] as List) - ?.map((e) => e == null - ? null - : Read.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(), + : ChannelModel.fromJson(json['channel'] as Map), + messages: (json['messages'] as List?) + ?.map((e) => Message.fromJson(e as Map)) + .toList() ?? + [], + members: (json['members'] as List?) + ?.map((e) => Member.fromJson(e as Map)) + .toList() ?? + [], + pinnedMessages: (json['pinned_messages'] as List?) + ?.map((e) => Message.fromJson(e as Map)) + .toList() ?? + [], + watcherCount: json['watcher_count'] as int?, + watchers: (json['watchers'] as List?) + ?.map((e) => User.fromJson(e as Map)) + .toList() ?? + [], + read: (json['read'] as List?) + ?.map((e) => Read.fromJson(e as Map)) + .toList() ?? + [], ); } Map _$ChannelStateToJson(ChannelState instance) => { 'channel': instance.channel?.toJson(), - 'messages': instance.messages?.map((e) => e?.toJson())?.toList(), - 'members': instance.members?.map((e) => e?.toJson())?.toList(), + 'messages': instance.messages.map((e) => e.toJson()).toList(), + 'members': instance.members.map((e) => e.toJson()).toList(), 'pinned_messages': - instance.pinnedMessages?.map((e) => e?.toJson())?.toList(), + instance.pinnedMessages.map((e) => e.toJson()).toList(), 'watcher_count': instance.watcherCount, - 'watchers': instance.watchers?.map((e) => e?.toJson())?.toList(), - 'read': instance.read?.map((e) => e?.toJson())?.toList(), + 'watchers': instance.watchers.map((e) => e.toJson()).toList(), + 'read': instance.read.map((e) => e.toJson()).toList(), }; diff --git a/packages/stream_chat/lib/src/models/command.dart b/packages/stream_chat/lib/src/models/command.dart index a5ababd2..5ba0043c 100644 --- a/packages/stream_chat/lib/src/models/command.dart +++ b/packages/stream_chat/lib/src/models/command.dart @@ -7,9 +7,9 @@ part 'command.g.dart'; class Command { /// Constructor used for json serialization Command({ - this.name, - this.description, - this.args, + required this.name, + required this.description, + required this.args, }); /// Create a new instance from a json diff --git a/packages/stream_chat/lib/src/models/command.g.dart b/packages/stream_chat/lib/src/models/command.g.dart index f32e8e8a..cf8be971 100644 --- a/packages/stream_chat/lib/src/models/command.g.dart +++ b/packages/stream_chat/lib/src/models/command.g.dart @@ -6,7 +6,7 @@ part of 'command.dart'; // JsonSerializableGenerator // ************************************************************************** -Command _$CommandFromJson(Map json) { +Command _$CommandFromJson(Map json) { return Command( name: json['name'] as String, description: json['description'] as String, diff --git a/packages/stream_chat/lib/src/models/device.dart b/packages/stream_chat/lib/src/models/device.dart index 150e6759..5dc98d25 100644 --- a/packages/stream_chat/lib/src/models/device.dart +++ b/packages/stream_chat/lib/src/models/device.dart @@ -7,8 +7,8 @@ part 'device.g.dart'; class Device { /// Constructor used for json serialization Device({ - this.id, - this.pushProvider, + required this.id, + required this.pushProvider, }); /// Create a new instance from a json diff --git a/packages/stream_chat/lib/src/models/device.g.dart b/packages/stream_chat/lib/src/models/device.g.dart index bac60856..5fcd9435 100644 --- a/packages/stream_chat/lib/src/models/device.g.dart +++ b/packages/stream_chat/lib/src/models/device.g.dart @@ -6,7 +6,7 @@ part of 'device.dart'; // JsonSerializableGenerator // ************************************************************************** -Device _$DeviceFromJson(Map json) { +Device _$DeviceFromJson(Map json) { return Device( id: json['id'] as String, pushProvider: json['push_provider'] as String, diff --git a/packages/stream_chat/lib/src/models/event.dart b/packages/stream_chat/lib/src/models/event.dart index 28cf1b38..7e57de60 100644 --- a/packages/stream_chat/lib/src/models/event.dart +++ b/packages/stream_chat/lib/src/models/event.dart @@ -10,7 +10,7 @@ part 'event.g.dart'; @JsonSerializable() class Event { /// Constructor used for json serialization - Event({ + const Event({ this.type, this.cid, this.connectionId, @@ -27,71 +27,72 @@ class Event { this.channelId, this.channelType, this.parentId, - this.extraData, - }) : isLocal = true; + this.extraData = const {}, + this.isLocal = true, + }); /// Create a new instance from a json factory Event.fromJson(Map json) => _$EventFromJson(Serialization.moveToExtraDataFromRoot( json, topLevelFields, - )) - ..isLocal = false; + )); /// The type of the event /// [EventType] contains some predefined constant types - final String type; + 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; + @JsonKey(defaultValue: false) + final bool isLocal; /// Map of custom channel extraData - @JsonKey(includeIfNull: false) + @JsonKey(defaultValue: {}) final Map extraData; /// Known top level fields. @@ -119,28 +120,27 @@ class Event { /// Serialize to json Map toJson() => Serialization.moveFromExtraDataToRoot( _$EventToJson(this), - topLevelFields, ); /// 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, + required String cid, + required ChannelConfig config, + User? createdBy, + bool frozen = false, + DateTime? lastMessageAt, + required DateTime createdAt, + required DateTime updatedAt, + DateTime? deletedAt, + required int memberCount, + Map? extraData, }) : super( id: id, type: type, @@ -204,7 +204,7 @@ class EventChannel extends ChannelModel { )); /// A paginated list of channel members - final List members; + final List? members; /// Known top level fields. /// Useful for [Serialization] methods. @@ -217,6 +217,5 @@ class EventChannel extends ChannelModel { @override Map toJson() => Serialization.moveFromExtraDataToRoot( _$EventChannelToJson(this), - topLevelFields, ); } diff --git a/packages/stream_chat/lib/src/models/event.g.dart b/packages/stream_chat/lib/src/models/event.g.dart index aaef8181..d0bb0d87 100644 --- a/packages/stream_chat/lib/src/models/event.g.dart +++ b/packages/stream_chat/lib/src/models/event.g.dart @@ -6,126 +6,87 @@ part of 'event.dart'; // JsonSerializableGenerator // ************************************************************************** -Event _$EventFromJson(Map json) { +Event _$EventFromJson(Map json) { return Event( - type: json['type'] as String, - cid: json['cid'] as String, - connectionId: json['connection_id'] as String, + type: json['type'] as String?, + cid: json['cid'] as String?, + connectionId: json['connection_id'] as String?, createdAt: json['created_at'] == null ? null : DateTime.parse(json['created_at'] as String), me: json['me'] == null ? null - : OwnUser.fromJson((json['me'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), + : OwnUser.fromJson(json['me'] as Map), user: json['user'] == null ? null - : User.fromJson((json['user'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), + : User.fromJson(json['user'] as Map), message: json['message'] == null ? null - : Message.fromJson((json['message'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), - totalUnreadCount: json['total_unread_count'] as int, - unreadChannels: json['unread_channels'] as int, + : Message.fromJson(json['message'] as Map), + totalUnreadCount: json['total_unread_count'] as int?, + unreadChannels: json['unread_channels'] as int?, reaction: json['reaction'] == null ? null - : Reaction.fromJson((json['reaction'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), - online: json['online'] as bool, + : Reaction.fromJson(json['reaction'] as Map), + online: json['online'] as bool?, channel: json['channel'] == null ? null - : EventChannel.fromJson((json['channel'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), + : EventChannel.fromJson(json['channel'] as Map), member: json['member'] == null ? null - : Member.fromJson((json['member'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), - channelId: json['channel_id'] as String, - channelType: json['channel_type'] as String, - parentId: json['parent_id'] as String, - extraData: (json['extra_data'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - ), - )..isLocal = json['is_local'] as bool; + : Member.fromJson(json['member'] as Map), + channelId: json['channel_id'] as String?, + channelType: json['channel_type'] as String?, + parentId: json['parent_id'] as String?, + extraData: json['extra_data'] as Map? ?? {}, + isLocal: json['is_local'] as bool? ?? false, + ); } -Map _$EventToJson(Event instance) { - final val = { - 'type': instance.type, - 'cid': instance.cid, - 'channel_id': instance.channelId, - 'channel_type': instance.channelType, - 'connection_id': instance.connectionId, - 'created_at': instance.createdAt?.toIso8601String(), - 'me': instance.me?.toJson(), - 'user': instance.user?.toJson(), - 'message': instance.message?.toJson(), - 'channel': instance.channel?.toJson(), - 'member': instance.member?.toJson(), - 'reaction': instance.reaction?.toJson(), - 'total_unread_count': instance.totalUnreadCount, - 'unread_channels': instance.unreadChannels, - 'online': instance.online, - 'parent_id': instance.parentId, - 'is_local': instance.isLocal, - }; +Map _$EventToJson(Event instance) => { + 'type': instance.type, + 'cid': instance.cid, + 'channel_id': instance.channelId, + 'channel_type': instance.channelType, + 'connection_id': instance.connectionId, + 'created_at': instance.createdAt?.toIso8601String(), + 'me': instance.me?.toJson(), + 'user': instance.user?.toJson(), + 'message': instance.message?.toJson(), + 'channel': instance.channel?.toJson(), + 'member': instance.member?.toJson(), + 'reaction': instance.reaction?.toJson(), + 'total_unread_count': instance.totalUnreadCount, + 'unread_channels': instance.unreadChannels, + 'online': instance.online, + 'parent_id': instance.parentId, + 'is_local': instance.isLocal, + 'extra_data': instance.extraData, + }; - void writeNotNull(String key, dynamic value) { - if (value != null) { - val[key] = value; - } - } - - writeNotNull('extra_data', instance.extraData); - return val; -} - -EventChannel _$EventChannelFromJson(Map json) { +EventChannel _$EventChannelFromJson(Map json) { return EventChannel( - members: (json['members'] as List) - ?.map((e) => e == null - ? null - : Member.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(), - id: json['id'] as String, - type: json['type'] as String, + members: (json['members'] as List?) + ?.map((e) => Member.fromJson(e as Map)) + .toList(), + id: json['id'] as String?, + type: json['type'] as String?, cid: json['cid'] as String, - config: json['config'] == null - ? null - : ChannelConfig.fromJson((json['config'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), + config: ChannelConfig.fromJson(json['config'] as Map), createdBy: json['created_by'] == null ? null - : User.fromJson((json['created_by'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), - frozen: json['frozen'] as bool, + : User.fromJson(json['created_by'] as Map), + frozen: json['frozen'] as bool? ?? false, lastMessageAt: json['last_message_at'] == null ? null : DateTime.parse(json['last_message_at'] as String), - createdAt: json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String), - updatedAt: json['updated_at'] == null - ? null - : DateTime.parse(json['updated_at'] as String), + createdAt: DateTime.parse(json['created_at'] as String), + updatedAt: DateTime.parse(json['updated_at'] as String), deletedAt: json['deleted_at'] == null ? null : DateTime.parse(json['deleted_at'] as String), - memberCount: json['member_count'] as int, - extraData: (json['extra_data'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - ), + memberCount: json['member_count'] as int? ?? 0, + extraData: json['extra_data'] as Map?, ); } @@ -144,13 +105,13 @@ Map _$EventChannelToJson(EventChannel instance) { writeNotNull('cid', readonly(instance.cid)); writeNotNull('config', readonly(instance.config)); writeNotNull('created_by', readonly(instance.createdBy)); - writeNotNull('frozen', instance.frozen); + val['frozen'] = instance.frozen; writeNotNull('last_message_at', readonly(instance.lastMessageAt)); writeNotNull('created_at', readonly(instance.createdAt)); writeNotNull('updated_at', readonly(instance.updatedAt)); writeNotNull('deleted_at', readonly(instance.deletedAt)); writeNotNull('member_count', readonly(instance.memberCount)); writeNotNull('extra_data', instance.extraData); - val['members'] = instance.members?.map((e) => e?.toJson())?.toList(); + val['members'] = instance.members?.map((e) => e.toJson()).toList(); return val; } diff --git a/packages/stream_chat/lib/src/models/member.dart b/packages/stream_chat/lib/src/models/member.dart index e99d8d34..49df170c 100644 --- a/packages/stream_chat/lib/src/models/member.dart +++ b/packages/stream_chat/lib/src/models/member.dart @@ -12,15 +12,16 @@ class Member { this.user, this.inviteAcceptedAt, this.inviteRejectedAt, - this.invited, + this.invited = false, this.role, this.userId, - this.isModerator, - this.createdAt, - this.updatedAt, - this.banned, - this.shadowBanned, - }); + this.isModerator = false, + DateTime? createdAt, + DateTime? updatedAt, + this.banned = false, + this.shadowBanned = false, + }) : createdAt = createdAt ?? DateTime.now(), + updatedAt = updatedAt ?? DateTime.now(); /// Create a new instance from a json factory Member.fromJson(Map json) { @@ -31,30 +32,34 @@ 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 + @JsonKey(defaultValue: false) 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 + @JsonKey(defaultValue: false) final bool isModerator; /// True if the member is banned from the channel + @JsonKey(defaultValue: false) final bool banned; /// True if the member is shadow banned from the channel + @JsonKey(defaultValue: false) final bool shadowBanned; /// The date of creation @@ -65,17 +70,17 @@ class Member { /// 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/member.g.dart b/packages/stream_chat/lib/src/models/member.g.dart index 3ac8778e..a75b458d 100644 --- a/packages/stream_chat/lib/src/models/member.g.dart +++ b/packages/stream_chat/lib/src/models/member.g.dart @@ -6,31 +6,29 @@ part of 'member.dart'; // JsonSerializableGenerator // ************************************************************************** -Member _$MemberFromJson(Map json) { +Member _$MemberFromJson(Map json) { return Member( user: json['user'] == null ? null - : User.fromJson((json['user'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), + : User.fromJson(json['user'] as Map), inviteAcceptedAt: json['invite_accepted_at'] == null ? null : DateTime.parse(json['invite_accepted_at'] as String), inviteRejectedAt: json['invite_rejected_at'] == null ? null : DateTime.parse(json['invite_rejected_at'] as String), - invited: json['invited'] as bool, - role: json['role'] as String, - userId: json['user_id'] as String, - isModerator: json['is_moderator'] as bool, + invited: json['invited'] as bool? ?? false, + role: json['role'] as String?, + userId: json['user_id'] as String?, + isModerator: json['is_moderator'] as bool? ?? false, createdAt: json['created_at'] == null ? null : DateTime.parse(json['created_at'] as String), updatedAt: json['updated_at'] == null ? null : DateTime.parse(json['updated_at'] as String), - banned: json['banned'] as bool, - shadowBanned: json['shadow_banned'] as bool, + banned: json['banned'] as bool? ?? false, + shadowBanned: json['shadow_banned'] as bool? ?? false, ); } @@ -44,6 +42,6 @@ Map _$MemberToJson(Member instance) => { 'is_moderator': instance.isModerator, 'banned': instance.banned, 'shadow_banned': instance.shadowBanned, - 'created_at': instance.createdAt?.toIso8601String(), - 'updated_at': instance.updatedAt?.toIso8601String(), + 'created_at': instance.createdAt.toIso8601String(), + 'updated_at': instance.updatedAt.toIso8601String(), }; diff --git a/packages/stream_chat/lib/src/models/message.dart b/packages/stream_chat/lib/src/models/message.dart index 68264f8d..03a162c5 100644 --- a/packages/stream_chat/lib/src/models/message.dart +++ b/packages/stream_chat/lib/src/models/message.dart @@ -45,13 +45,13 @@ enum MessageSendingStatus { class Message extends Equatable { /// Constructor used for json serialization Message({ - String id, + String? id, this.text, - this.type, - this.attachments, - this.mentionedUsers, - this.silent, - this.shadowed, + this.type = 'regular', + this.attachments = const [], + this.mentionedUsers = const [], + this.silent = false, + this.shadowed = false, this.reactionCounts, this.reactionScores, this.latestReactions, @@ -63,19 +63,21 @@ class Message extends Equatable { this.threadParticipants, this.showInChannel, this.command, - this.createdAt, - this.updatedAt, + DateTime? createdAt, + DateTime? updatedAt, this.user, this.pinned = false, this.pinnedAt, - DateTime pinExpires, + DateTime? pinExpires, this.pinnedBy, - this.extraData, + this.extraData = const {}, this.deletedAt, this.status = MessageSendingStatus.sent, - this.skipPush, + this.skipPush = false, }) : id = id ?? const Uuid().v4(), - pinExpires = pinExpires?.toUtc(); + pinExpires = pinExpires?.toUtc(), + createdAt = createdAt ?? DateTime.now(), + updatedAt = updatedAt ?? DateTime.now(); /// Create a new instance from a json factory Message.fromJson(Map json) => _$MessageFromJson( @@ -86,75 +88,91 @@ class Message extends Equatable { final String id; /// The text of this message - final String text; + final String? text; /// The status of a sending message @JsonKey(ignore: true) final MessageSendingStatus status; /// The message type - @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) + @JsonKey( + includeIfNull: false, + toJson: Serialization.readOnly, + defaultValue: 'regular', + ) 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) + @JsonKey( + includeIfNull: false, + defaultValue: [], + ) final List attachments; /// The list of user mentioned in the message - @JsonKey(toJson: Serialization.userIds) + @JsonKey( + toJson: Serialization.userIds, + defaultValue: [], + ) 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 + @JsonKey(defaultValue: false) final bool silent; /// If true the message will not send a push notification + @JsonKey(defaultValue: false) final bool skipPush; /// If true the message is shadowed - @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) + @JsonKey( + includeIfNull: false, + toJson: Serialization.readOnly, + defaultValue: false, + ) 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) @@ -166,26 +184,30 @@ class Message extends Equatable { /// User who sent the message @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) - final User user; + final User? user; /// If true the message is pinned + @JsonKey(defaultValue: false) 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) + @JsonKey( + includeIfNull: false, + defaultValue: {}, + ) final Map extraData; /// True if the message is a system info @@ -199,7 +221,7 @@ class Message extends Equatable { /// 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. @@ -236,39 +258,40 @@ class Message extends Equatable { /// Serialize to json Map toJson() => Serialization.moveFromExtraDataToRoot( - _$MessageToJson(this), topLevelFields); + _$MessageToJson(this), + ); /// 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 && @@ -306,49 +329,47 @@ class Message extends Equatable { 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, ); } /// Returns a new [Message] that is a combination of this message and the /// given [other] message. - Message merge(Message other) { - if (other == null) return this; - return copyWith( - id: other.id, - text: other.text, - type: other.type, - attachments: other.attachments, - mentionedUsers: other.mentionedUsers, - reactionCounts: other.reactionCounts, - reactionScores: other.reactionScores, - latestReactions: other.latestReactions, - ownReactions: other.ownReactions, - parentId: other.parentId, - quotedMessage: other.quotedMessage, - quotedMessageId: other.quotedMessageId, - replyCount: other.replyCount, - threadParticipants: other.threadParticipants, - showInChannel: other.showInChannel, - command: other.command, - createdAt: other.createdAt, - silent: other.silent, - extraData: other.extraData, - user: other.user, - shadowed: other.shadowed, - updatedAt: other.updatedAt, - deletedAt: other.deletedAt, - status: other.status, - pinned: other.pinned, - pinnedAt: other.pinnedAt, - pinExpires: other.pinExpires, - pinnedBy: other.pinnedBy, - ); - } + Message merge(Message other) => copyWith( + id: other.id, + text: other.text, + type: other.type, + attachments: other.attachments, + mentionedUsers: other.mentionedUsers, + reactionCounts: other.reactionCounts, + reactionScores: other.reactionScores, + latestReactions: other.latestReactions, + ownReactions: other.ownReactions, + parentId: other.parentId, + quotedMessage: other.quotedMessage, + quotedMessageId: other.quotedMessageId, + replyCount: other.replyCount, + threadParticipants: other.threadParticipants, + showInChannel: other.showInChannel, + command: other.command, + createdAt: other.createdAt, + silent: other.silent, + extraData: other.extraData, + user: other.user, + shadowed: other.shadowed, + updatedAt: other.updatedAt, + deletedAt: other.deletedAt, + status: other.status, + pinned: other.pinned, + pinnedAt: other.pinnedAt, + pinExpires: other.pinExpires, + pinnedBy: other.pinnedBy, + ); @override - List get props => [ + List get props => [ id, text, type, @@ -386,7 +407,7 @@ class Message extends Equatable { @JsonSerializable() class TranslatedMessage extends Message { /// Constructor used for json serialization - TranslatedMessage(this.i18n); + TranslatedMessage(this.i18n) : super(); /// Create a new instance from a json factory TranslatedMessage.fromJson(Map json) => @@ -395,7 +416,7 @@ class TranslatedMessage extends Message { ); /// A Map of - final Map i18n; + final Map? i18n; /// Known top level fields. /// Useful for [Serialization] methods. @@ -408,6 +429,5 @@ class TranslatedMessage extends Message { @override Map toJson() => Serialization.moveFromExtraDataToRoot( _$TranslatedMessageToJson(this), - topLevelFields, ); } diff --git a/packages/stream_chat/lib/src/models/message.g.dart b/packages/stream_chat/lib/src/models/message.g.dart index 80df1589..ca094a83 100644 --- a/packages/stream_chat/lib/src/models/message.g.dart +++ b/packages/stream_chat/lib/src/models/message.g.dart @@ -6,64 +6,44 @@ part of 'message.dart'; // JsonSerializableGenerator // ************************************************************************** -Message _$MessageFromJson(Map json) { +Message _$MessageFromJson(Map json) { return Message( - id: json['id'] as String, - text: json['text'] as String, - type: json['type'] as String, - attachments: (json['attachments'] as List) - ?.map((e) => e == null - ? null - : Attachment.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(), - mentionedUsers: (json['mentioned_users'] as List) - ?.map((e) => e == null - ? null - : User.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(), - silent: json['silent'] as bool, - shadowed: json['shadowed'] as bool, - reactionCounts: (json['reaction_counts'] as Map)?.map( - (k, e) => MapEntry(k as String, e as int), + id: json['id'] as String?, + text: json['text'] as String?, + type: json['type'] as String? ?? 'regular', + attachments: (json['attachments'] as List?) + ?.map((e) => Attachment.fromJson(e as Map)) + .toList() ?? + [], + mentionedUsers: (json['mentioned_users'] as List?) + ?.map((e) => User.fromJson(e as Map)) + .toList() ?? + [], + silent: json['silent'] as bool? ?? false, + shadowed: json['shadowed'] as bool? ?? false, + reactionCounts: (json['reaction_counts'] as Map?)?.map( + (k, e) => MapEntry(k, e as int), ), - reactionScores: (json['reaction_scores'] as Map)?.map( - (k, e) => MapEntry(k as String, e as int), + reactionScores: (json['reaction_scores'] as Map?)?.map( + (k, e) => MapEntry(k, e as int), ), - latestReactions: (json['latest_reactions'] as List) - ?.map((e) => e == null - ? null - : Reaction.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(), - ownReactions: (json['own_reactions'] as List) - ?.map((e) => e == null - ? null - : Reaction.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(), - parentId: json['parent_id'] as String, + latestReactions: (json['latest_reactions'] as List?) + ?.map((e) => Reaction.fromJson(e as Map)) + .toList(), + ownReactions: (json['own_reactions'] as List?) + ?.map((e) => Reaction.fromJson(e as Map)) + .toList(), + parentId: json['parent_id'] as String?, quotedMessage: json['quoted_message'] == null ? null - : Message.fromJson((json['quoted_message'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), - quotedMessageId: json['quoted_message_id'] as String, - replyCount: json['reply_count'] as int, - threadParticipants: (json['thread_participants'] as List) - ?.map((e) => e == null - ? null - : User.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(), - showInChannel: json['show_in_channel'] as bool, - command: json['command'] as String, + : Message.fromJson(json['quoted_message'] as Map), + quotedMessageId: json['quoted_message_id'] as String?, + replyCount: json['reply_count'] as int?, + threadParticipants: (json['thread_participants'] as List?) + ?.map((e) => User.fromJson(e as Map)) + .toList(), + showInChannel: json['show_in_channel'] as bool?, + command: json['command'] as String?, createdAt: json['created_at'] == null ? null : DateTime.parse(json['created_at'] as String), @@ -72,10 +52,8 @@ Message _$MessageFromJson(Map json) { : DateTime.parse(json['updated_at'] as String), user: json['user'] == null ? null - : User.fromJson((json['user'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), - pinned: json['pinned'] as bool, + : User.fromJson(json['user'] as Map), + pinned: json['pinned'] as bool? ?? false, pinnedAt: json['pinned_at'] == null ? null : DateTime.parse(json['pinned_at'] as String), @@ -84,16 +62,12 @@ Message _$MessageFromJson(Map json) { : DateTime.parse(json['pin_expires'] as String), pinnedBy: json['pinned_by'] == null ? null - : User.fromJson((json['pinned_by'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), - extraData: (json['extra_data'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - ), + : User.fromJson(json['pinned_by'] as Map), + extraData: json['extra_data'] as Map? ?? {}, deletedAt: json['deleted_at'] == null ? null : DateTime.parse(json['deleted_at'] as String), - skipPush: json['skip_push'] as bool, + skipPush: json['skip_push'] as bool? ?? false, ); } @@ -110,8 +84,7 @@ Map _$MessageToJson(Message instance) { } writeNotNull('type', readonly(instance.type)); - writeNotNull( - 'attachments', instance.attachments?.map((e) => e?.toJson())?.toList()); + val['attachments'] = instance.attachments.map((e) => e.toJson()).toList(); val['mentioned_users'] = Serialization.userIds(instance.mentionedUsers); writeNotNull('reaction_counts', readonly(instance.reactionCounts)); writeNotNull('reaction_scores', readonly(instance.reactionScores)); @@ -134,15 +107,15 @@ Map _$MessageToJson(Message instance) { val['pinned_at'] = readonly(instance.pinnedAt); val['pin_expires'] = instance.pinExpires?.toIso8601String(); val['pinned_by'] = readonly(instance.pinnedBy); - writeNotNull('extra_data', instance.extraData); + val['extra_data'] = instance.extraData; writeNotNull('deleted_at', readonly(instance.deletedAt)); return val; } -TranslatedMessage _$TranslatedMessageFromJson(Map json) { +TranslatedMessage _$TranslatedMessageFromJson(Map json) { return TranslatedMessage( - (json['i18n'] as Map)?.map( - (k, e) => MapEntry(k as String, e as String), + (json['i18n'] as Map?)?.map( + (k, e) => MapEntry(k, e as String), ), ); } diff --git a/packages/stream_chat/lib/src/models/mute.dart b/packages/stream_chat/lib/src/models/mute.dart index e3d5e1a0..3ba26230 100644 --- a/packages/stream_chat/lib/src/models/mute.dart +++ b/packages/stream_chat/lib/src/models/mute.dart @@ -9,7 +9,12 @@ part 'mute.g.dart'; @JsonSerializable() class Mute { /// Constructor used for json serialization - Mute({this.user, this.channel, this.createdAt, this.updatedAt}); + Mute({ + required this.user, + required this.channel, + required this.createdAt, + required this.updatedAt, + }); /// Create a new instance from a json factory Mute.fromJson(Map json) => _$MuteFromJson(json); diff --git a/packages/stream_chat/lib/src/models/mute.g.dart b/packages/stream_chat/lib/src/models/mute.g.dart index 9d0b9318..e77b8707 100644 --- a/packages/stream_chat/lib/src/models/mute.g.dart +++ b/packages/stream_chat/lib/src/models/mute.g.dart @@ -6,24 +6,12 @@ part of 'mute.dart'; // JsonSerializableGenerator // ************************************************************************** -Mute _$MuteFromJson(Map json) { +Mute _$MuteFromJson(Map json) { return Mute( - user: json['user'] == null - ? null - : User.fromJson((json['user'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), - channel: json['channel'] == null - ? null - : ChannelModel.fromJson((json['channel'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), - createdAt: json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String), - updatedAt: json['updated_at'] == null - ? null - : DateTime.parse(json['updated_at'] as String), + user: User.fromJson(json['user'] as Map), + channel: ChannelModel.fromJson(json['channel'] as Map), + createdAt: DateTime.parse(json['created_at'] as String), + updatedAt: DateTime.parse(json['updated_at'] as String), ); } diff --git a/packages/stream_chat/lib/src/models/own_user.dart b/packages/stream_chat/lib/src/models/own_user.dart index 18ee2abf..3ce951cf 100644 --- a/packages/stream_chat/lib/src/models/own_user.dart +++ b/packages/stream_chat/lib/src/models/own_user.dart @@ -12,19 +12,19 @@ part 'own_user.g.dart'; class OwnUser extends User { /// Constructor used for json serialization OwnUser({ - this.devices, - this.mutes, - this.totalUnreadCount, + this.devices = const [], + this.mutes = const [], + this.totalUnreadCount = 0, this.unreadChannels, - this.channelMutes, - String id, - String role, - DateTime createdAt, - DateTime updatedAt, - DateTime lastActive, - bool online, - Map extraData, - bool banned, + this.channelMutes = const [], + String id = '', + String role = '', + DateTime? createdAt, + DateTime? updatedAt, + DateTime? lastActive, + bool online = false, + Map extraData = const {}, + bool banned = false, }) : super( id: id, role: role, @@ -41,24 +41,34 @@ class OwnUser extends User { Serialization.moveToExtraDataFromRoot(json, topLevelFields)); /// List of user devices - @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) + @JsonKey( + includeIfNull: false, + toJson: Serialization.readOnly, + defaultValue: []) final List devices; /// List of users muted by the user - @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) + @JsonKey( + includeIfNull: false, + toJson: Serialization.readOnly, + defaultValue: []) final List mutes; /// List of users muted by the user - @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) + @JsonKey( + includeIfNull: false, + toJson: Serialization.readOnly, + defaultValue: []) final List channelMutes; /// Total unread messages by the user - @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) + @JsonKey( + includeIfNull: false, toJson: Serialization.readOnly, defaultValue: 0) 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. @@ -74,5 +84,6 @@ class OwnUser extends User { /// Serialize to json @override Map toJson() => Serialization.moveFromExtraDataToRoot( - _$OwnUserToJson(this), topLevelFields); + _$OwnUserToJson(this), + ); } diff --git a/packages/stream_chat/lib/src/models/own_user.g.dart b/packages/stream_chat/lib/src/models/own_user.g.dart index 887e6b28..5802060c 100644 --- a/packages/stream_chat/lib/src/models/own_user.g.dart +++ b/packages/stream_chat/lib/src/models/own_user.g.dart @@ -6,33 +6,24 @@ part of 'own_user.dart'; // JsonSerializableGenerator // ************************************************************************** -OwnUser _$OwnUserFromJson(Map json) { +OwnUser _$OwnUserFromJson(Map json) { return OwnUser( - devices: (json['devices'] as List) - ?.map((e) => e == null - ? null - : Device.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(), - mutes: (json['mutes'] as List) - ?.map((e) => e == null - ? null - : Mute.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(), - totalUnreadCount: json['total_unread_count'] as int, - unreadChannels: json['unread_channels'] as int, - channelMutes: (json['channel_mutes'] as List) - ?.map((e) => e == null - ? null - : Mute.fromJson((e as Map)?.map( - (k, e) => MapEntry(k as String, e), - ))) - ?.toList(), + devices: (json['devices'] as List?) + ?.map((e) => Device.fromJson(e as Map)) + .toList() ?? + [], + mutes: (json['mutes'] as List?) + ?.map((e) => Mute.fromJson(e as Map)) + .toList() ?? + [], + totalUnreadCount: json['total_unread_count'] as int? ?? 0, + unreadChannels: json['unread_channels'] as int?, + channelMutes: (json['channel_mutes'] as List?) + ?.map((e) => Mute.fromJson(e as Map)) + .toList() ?? + [], id: json['id'] as String, - role: json['role'] as String, + role: json['role'] as String? ?? '', createdAt: json['created_at'] == null ? null : DateTime.parse(json['created_at'] as String), @@ -42,11 +33,9 @@ OwnUser _$OwnUserFromJson(Map json) { lastActive: json['last_active'] == null ? null : DateTime.parse(json['last_active'] as String), - online: json['online'] as bool, - extraData: (json['extra_data'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - ), - banned: json['banned'] as bool, + online: json['online'] as bool? ?? false, + extraData: json['extra_data'] as Map, + banned: json['banned'] as bool? ?? false, ); } @@ -67,7 +56,7 @@ Map _$OwnUserToJson(OwnUser instance) { writeNotNull('last_active', readonly(instance.lastActive)); writeNotNull('online', readonly(instance.online)); writeNotNull('banned', readonly(instance.banned)); - writeNotNull('extra_data', instance.extraData); + val['extra_data'] = instance.extraData; writeNotNull('devices', readonly(instance.devices)); writeNotNull('mutes', readonly(instance.mutes)); writeNotNull('channel_mutes', readonly(instance.channelMutes)); diff --git a/packages/stream_chat/lib/src/models/reaction.dart b/packages/stream_chat/lib/src/models/reaction.dart index 6792bf3f..82913218 100644 --- a/packages/stream_chat/lib/src/models/reaction.dart +++ b/packages/stream_chat/lib/src/models/reaction.dart @@ -10,20 +10,24 @@ class Reaction { /// Constructor used for json serialization Reaction({ this.messageId, - this.createdAt, - this.type, + DateTime? createdAt, + required this.type, this.user, - String userId, - this.score, + String? userId, + this.score = 0, this.extraData, - }) : userId = userId ?? user?.id; + }) : userId = userId ?? user?.id, + createdAt = createdAt ?? DateTime.now(); /// 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; @@ -34,18 +38,19 @@ class Reaction { /// 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) + @JsonKey(defaultValue: 0) 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 = [ @@ -59,17 +64,18 @@ class Reaction { /// Serialize to json Map toJson() => Serialization.moveFromExtraDataToRoot( - _$ReactionToJson(this), topLevelFields); + _$ReactionToJson(this), + ); /// 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, @@ -83,16 +89,13 @@ class Reaction { /// Returns a new [Reaction] that is a combination of this reaction and the /// given [other] reaction. - Reaction merge(Reaction other) { - if (other == null) return this; - return copyWith( - messageId: other.messageId, - createdAt: other.createdAt, - type: other.type, - user: other.user, - userId: other.userId, - score: other.score, - extraData: other.extraData, - ); - } + Reaction merge(Reaction other) => copyWith( + messageId: other.messageId, + createdAt: other.createdAt, + type: other.type, + user: other.user, + userId: other.userId, + score: other.score, + extraData: other.extraData, + ); } diff --git a/packages/stream_chat/lib/src/models/reaction.g.dart b/packages/stream_chat/lib/src/models/reaction.g.dart index a270af01..963b4d21 100644 --- a/packages/stream_chat/lib/src/models/reaction.g.dart +++ b/packages/stream_chat/lib/src/models/reaction.g.dart @@ -6,23 +6,19 @@ part of 'reaction.dart'; // JsonSerializableGenerator // ************************************************************************** -Reaction _$ReactionFromJson(Map json) { +Reaction _$ReactionFromJson(Map json) { return Reaction( - messageId: json['message_id'] as String, + messageId: json['message_id'] as String?, createdAt: json['created_at'] == null ? null : DateTime.parse(json['created_at'] as String), type: json['type'] as String, user: json['user'] == null ? null - : User.fromJson((json['user'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), - userId: json['user_id'] as String, - score: json['score'] as int, - extraData: (json['extra_data'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - ), + : User.fromJson(json['user'] as Map), + userId: json['user_id'] as String?, + score: json['score'] as int? ?? 0, + extraData: json['extra_data'] as Map?, ); } diff --git a/packages/stream_chat/lib/src/models/read.dart b/packages/stream_chat/lib/src/models/read.dart index fc8ef1bc..cbd47dd1 100644 --- a/packages/stream_chat/lib/src/models/read.dart +++ b/packages/stream_chat/lib/src/models/read.dart @@ -8,9 +8,9 @@ part 'read.g.dart'; class Read { /// Constructor used for json serialization Read({ - this.lastRead, - this.user, - this.unreadMessages, + required this.lastRead, + required this.user, + this.unreadMessages = 0, }); /// Create a new instance from a json @@ -23,6 +23,7 @@ class Read { final User user; /// Number of unread messages + @JsonKey(defaultValue: 0) final int unreadMessages; /// Serialize to json @@ -30,9 +31,9 @@ class Read { /// 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/read.g.dart b/packages/stream_chat/lib/src/models/read.g.dart index d04ae146..93c832d8 100644 --- a/packages/stream_chat/lib/src/models/read.g.dart +++ b/packages/stream_chat/lib/src/models/read.g.dart @@ -6,22 +6,16 @@ part of 'read.dart'; // JsonSerializableGenerator // ************************************************************************** -Read _$ReadFromJson(Map json) { +Read _$ReadFromJson(Map json) { return Read( - lastRead: json['last_read'] == null - ? null - : DateTime.parse(json['last_read'] as String), - user: json['user'] == null - ? null - : User.fromJson((json['user'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - )), - unreadMessages: json['unread_messages'] as int, + lastRead: DateTime.parse(json['last_read'] as String), + user: User.fromJson(json['user'] as Map), + unreadMessages: json['unread_messages'] as int? ?? 0, ); } Map _$ReadToJson(Read instance) => { - 'last_read': instance.lastRead?.toIso8601String(), - 'user': instance.user?.toJson(), + 'last_read': instance.lastRead.toIso8601String(), + 'user': instance.user.toJson(), 'unread_messages': instance.unreadMessages, }; diff --git a/packages/stream_chat/lib/src/models/serialization.dart b/packages/stream_chat/lib/src/models/serialization.dart index 18cdd545..d584912e 100644 --- a/packages/stream_chat/lib/src/models/serialization.dart +++ b/packages/stream_chat/lib/src/models/serialization.dart @@ -10,16 +10,14 @@ class Serialization { static const Function readOnly = readonly; /// List of users to list of userIds - static List userIds(List users) => - users?.map((u) => u.id)?.toList(); + static List? userIds(List? users) => + users?.map((u) => u.id).toList(); /// Takes unknown json keys and puts them in the `extra_data` key static Map moveToExtraDataFromRoot( Map json, List topLevelFields, ) { - if (json == null) return null; - final jsonClone = Map.from(json); final extraDataMap = Map.from(json) @@ -38,7 +36,6 @@ class Serialization { /// the json map static Map moveFromExtraDataToRoot( Map json, - List topLevelFields, ) { final jsonClone = Map.from(json); return jsonClone diff --git a/packages/stream_chat/lib/src/models/user.dart b/packages/stream_chat/lib/src/models/user.dart index 3b9f9add..67fc99f0 100644 --- a/packages/stream_chat/lib/src/models/user.dart +++ b/packages/stream_chat/lib/src/models/user.dart @@ -8,16 +8,17 @@ part 'user.g.dart'; class User { /// Constructor used for json serialization User({ - this.id, - this.role, - this.createdAt, - this.updatedAt, + this.id = '', + this.role = '', + DateTime? createdAt, + DateTime? updatedAt, this.lastActive, - this.online, - this.extraData, - this.banned, - this.teams, - }); + this.online = false, + this.extraData = const {}, + this.banned = false, + this.teams = const [], + }) : createdAt = createdAt ?? DateTime.now(), + updatedAt = updatedAt ?? DateTime.now(); /// Create a new instance from a json factory User.fromJson(Map json) => _$UserFromJson( @@ -26,14 +27,14 @@ class User { /// Use this named constructor to create a new user instance User.init( this.id, { - this.online, - this.extraData, - }) : createdAt = null, - updatedAt = null, - lastActive = null, - banned = null, - teams = null, - role = null; + this.online = false, + this.extraData = const {}, + required this.createdAt, + required this.updatedAt, + this.teams = const [], + required this.role, + }) : lastActive = null, + banned = false; /// Known top level fields. /// Useful for [Serialization] methods. @@ -52,11 +53,15 @@ class User { final String id; /// User role - @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) + @JsonKey( + includeIfNull: false, toJson: Serialization.readOnly, defaultValue: '') final String role; /// User role - @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) + @JsonKey( + includeIfNull: false, + toJson: Serialization.readOnly, + defaultValue: []) final List teams; /// Date of user creation @@ -69,14 +74,16 @@ class User { /// 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) + @JsonKey( + includeIfNull: false, toJson: Serialization.readOnly, defaultValue: false) final bool online; /// True if user is banned from the chat - @JsonKey(includeIfNull: false, toJson: Serialization.readOnly) + @JsonKey( + includeIfNull: false, toJson: Serialization.readOnly, defaultValue: false) final bool banned; /// Map of custom user extraData @@ -87,8 +94,8 @@ class User { int get hashCode => id.hashCode; /// Shortcut for user name - String get name => - (extraData?.containsKey('name') == true && extraData['name'] != '') + String? get name => + (extraData.containsKey('name') == true && extraData['name'] != '') ? extraData['name'] : id; @@ -98,20 +105,21 @@ class User { other is User && runtimeType == other.runtimeType && id == other.id; /// Serialize to json - Map toJson() => - Serialization.moveFromExtraDataToRoot(_$UserToJson(this), topLevelFields); + Map toJson() => Serialization.moveFromExtraDataToRoot( + _$UserToJson(this), + ); /// 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/lib/src/models/user.g.dart b/packages/stream_chat/lib/src/models/user.g.dart index b27935a7..18408d76 100644 --- a/packages/stream_chat/lib/src/models/user.g.dart +++ b/packages/stream_chat/lib/src/models/user.g.dart @@ -6,10 +6,10 @@ part of 'user.dart'; // JsonSerializableGenerator // ************************************************************************** -User _$UserFromJson(Map json) { +User _$UserFromJson(Map json) { return User( id: json['id'] as String, - role: json['role'] as String, + role: json['role'] as String? ?? '', createdAt: json['created_at'] == null ? null : DateTime.parse(json['created_at'] as String), @@ -19,12 +19,12 @@ User _$UserFromJson(Map json) { lastActive: json['last_active'] == null ? null : DateTime.parse(json['last_active'] as String), - online: json['online'] as bool, - extraData: (json['extra_data'] as Map)?.map( - (k, e) => MapEntry(k as String, e), - ), - banned: json['banned'] as bool, - teams: (json['teams'] as List)?.map((e) => e as String)?.toList(), + online: json['online'] as bool? ?? false, + extraData: json['extra_data'] as Map, + banned: json['banned'] as bool? ?? false, + teams: + (json['teams'] as List?)?.map((e) => e as String).toList() ?? + [], ); } @@ -46,6 +46,6 @@ Map _$UserToJson(User instance) { writeNotNull('last_active', readonly(instance.lastActive)); writeNotNull('online', readonly(instance.online)); writeNotNull('banned', readonly(instance.banned)); - writeNotNull('extra_data', instance.extraData); + val['extra_data'] = instance.extraData; return val; } diff --git a/packages/stream_chat/pubspec.yaml b/packages/stream_chat/pubspec.yaml index f221fc39..8e0d5f12 100644 --- a/packages/stream_chat/pubspec.yaml +++ b/packages/stream_chat/pubspec.yaml @@ -6,26 +6,26 @@ 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" + dio: ^4.0.0 equatable: ^2.0.0 freezed_annotation: ^0.14.0 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 + build_runner: ^1.12.2 + 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..041a3e49 100644 --- a/packages/stream_chat/test/src/api/channel_test.dart +++ b/packages/stream_chat/test/src/api/channel_test.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:dio/dio.dart'; import 'package:dio/native_imp.dart'; import 'package:mocktail/mocktail.dart'; @@ -6,11 +8,10 @@ import 'package:stream_chat/src/client.dart'; import 'package:stream_chat/src/event_type.dart'; import 'package:stream_chat/src/models/event.dart'; import 'package:stream_chat/src/models/message.dart'; -import 'package:stream_chat/src/models/reaction.dart'; import 'package:stream_chat/src/models/own_user.dart'; -import 'package:test/test.dart'; - +import 'package:stream_chat/src/models/reaction.dart'; import 'package:stream_chat/stream_chat.dart'; +import 'package:test/test.dart'; class MockDio extends Mock implements DioForNative {} @@ -37,6 +38,17 @@ void main() { final channelClient = client.channel('messaging', id: 'testid'); final message = Message(text: 'hey', id: 'test'); + when(() => mockDio.post( + any(), + data: any(named: 'data'), + )).thenAnswer((_) async => Response( + data: jsonEncode(ChannelState()), + statusCode: 200, + requestOptions: FakeRequestOptions(), + )); + + await channelClient.watch(); + when( () => mockDio.post( '/channels/messaging/testid/message', @@ -44,7 +56,7 @@ void main() { ), ).thenAnswer( (_) async => Response( - data: '{}', + data: jsonEncode({'message': message}), statusCode: 200, requestOptions: FakeRequestOptions(), ), @@ -76,7 +88,7 @@ void main() { any(), data: any(named: 'data'), )).thenAnswer((_) async => Response( - data: '{}', + data: jsonEncode(ChannelState()), statusCode: 200, requestOptions: FakeRequestOptions(), )); @@ -226,6 +238,17 @@ void main() { ); final channelClient = client.channel(channelType, id: channelId); + when(() => mockDio.post( + any(), + data: any(named: 'data'), + )).thenAnswer((_) async => Response( + data: jsonEncode(ChannelState()), + statusCode: 200, + requestOptions: FakeRequestOptions(), + )); + + await channelClient.watch(); + when(() => mockUploader.sendFile(file, channelId, channelType)) .thenAnswer((_) async => SendFileResponse()); @@ -254,6 +277,17 @@ void main() { ); final channelClient = client.channel(channelType, id: channelId); + when(() => mockDio.post( + any(), + data: any(named: 'data'), + )).thenAnswer((_) async => Response( + data: jsonEncode(ChannelState()), + statusCode: 200, + requestOptions: FakeRequestOptions(), + )); + + await channelClient.watch(); + when(() => mockUploader.sendImage(image, channelId, channelType)) .thenAnswer((_) async => SendImageResponse()); @@ -277,6 +311,17 @@ void main() { final channelClient = client.channel('messaging', id: 'testid'); const url = 'url'; + when(() => mockDio.post( + any(), + data: any(named: 'data'), + )).thenAnswer((_) async => Response( + data: jsonEncode(ChannelState()), + statusCode: 200, + requestOptions: FakeRequestOptions(), + )); + + await channelClient.watch(); + when( () => mockDio.delete( '/channels/messaging/testid/file', @@ -310,6 +355,17 @@ void main() { final channelClient = client.channel('messaging', id: 'testid'); const url = 'url'; + when(() => mockDio.post( + any(), + data: any(named: 'data'), + )).thenAnswer((_) async => Response( + data: jsonEncode(ChannelState()), + statusCode: 200, + requestOptions: FakeRequestOptions(), + )); + + await channelClient.watch(); + when( () => mockDio.delete( '/channels/messaging/testid/image', @@ -356,6 +412,17 @@ void main() { final channelClient = client.channel('messaging', id: 'testid'); final message = Message(text: 'Hello', id: 'test'); + when(() => mockDio.post( + any(), + data: any(named: 'data'), + )).thenAnswer((_) async => Response( + data: jsonEncode(ChannelState()), + statusCode: 200, + requestOptions: FakeRequestOptions(), + )); + + await channelClient.watch(); + when( () => mockDio.post( '/messages/${message.id}', @@ -363,7 +430,7 @@ void main() { ), ).thenAnswer( (_) async => Response( - data: '{}', + data: jsonEncode({'message': message}), statusCode: 200, requestOptions: FakeRequestOptions(), ), @@ -390,6 +457,17 @@ void main() { final channelClient = client.channel('messaging', id: 'testid'); final message = Message(text: 'Hello', id: 'test'); + when(() => mockDio.post( + any(), + data: any(named: 'data'), + )).thenAnswer((_) async => Response( + data: jsonEncode(ChannelState()), + statusCode: 200, + requestOptions: FakeRequestOptions(), + )); + + await channelClient.watch(); + when( () => mockDio.post( '/messages/${message.id}', @@ -397,7 +475,7 @@ void main() { ), ).thenAnswer( (_) async => Response( - data: '{}', + data: jsonEncode({'message': message}), statusCode: 200, requestOptions: FakeRequestOptions(), ), @@ -564,14 +642,32 @@ void main() { 'api-key', httpClient: mockDio, tokenProvider: (_) async => '', - )..state.user = OwnUser(id: 'test-id'); + ); - final channelClient = client.channel('messaging', id: 'testid'); + final user = OwnUser(id: 'test-id'); + + client.state.user = user; + + final message = Message(id: 'messageid'); const reactionType = 'test'; + final reaction = Reaction(type: reactionType); + final channelClient = client.channel('messaging', id: 'testid'); + + when(() => mockDio.post( + any(), + data: any(named: 'data'), + )).thenAnswer( + (_) async => Response( + data: '{}', + statusCode: 200, + requestOptions: FakeRequestOptions(), + ), + ); + await channelClient.watch(); when( () => mockDio.post( - '/messages/messageid/reaction', + '/messages/${message.id}/reaction', data: { 'reaction': { 'type': reactionType, @@ -581,20 +677,17 @@ void main() { ), ).thenAnswer( (_) async => Response( - data: '{}', + data: jsonEncode({ + 'message': message, + 'reaction': reaction, + }), statusCode: 200, requestOptions: FakeRequestOptions(), ), ); await channelClient.sendReaction( - Message( - id: 'messageid', - reactionCounts: const {}, - reactionScores: const {}, - latestReactions: const [], - ownReactions: const [], - ), + message, reactionType, ); @@ -617,7 +710,9 @@ void main() { 'api-key', httpClient: mockDio, tokenProvider: (_) async => '', - )..state.user = OwnUser(id: 'test-id'); + ); + + client.state.user = OwnUser(id: 'test-id'); final channelClient = client.channel('messaging', id: 'testid'); @@ -634,12 +729,14 @@ void main() { await channelClient.deleteReaction( Message( id: 'messageid', - reactionCounts: const {}, - reactionScores: const {}, - latestReactions: const [], - ownReactions: const [], ), - Reaction(type: 'test'), + Reaction( + type: 'test', + createdAt: DateTime.now(), + user: User( + id: client.state.user?.id ?? '', + ), + ), ); verify(() => @@ -694,26 +791,44 @@ void main() { tokenProvider: (_) async => '', ); final channelClient = client.channel('messaging', id: 'testid'); - final members = ['vishal']; + final channelModel = ChannelModel(cid: 'messaging:testid'); + + when(() => mockDio.post( + any(), + data: any(named: 'data'), + )).thenAnswer((_) async => Response( + data: jsonEncode(ChannelState(channel: channelModel)), + statusCode: 200, + requestOptions: FakeRequestOptions(), + )); + + await channelClient.watch(); + + final members = [Member(userId: 'vishal')]; + final memberIds = members.map((e) => e.userId!).toList(); final message = Message(text: 'test'); when( () => mockDio.post( '/channels/messaging/testid', - data: {'add_members': members, 'message': message.toJson()}, + data: {'add_members': memberIds, 'message': message.toJson()}, ), ).thenAnswer( (_) async => Response( - data: '{}', + data: jsonEncode({ + 'members': members, + 'message': message, + 'channel': channelModel, + }), statusCode: 200, requestOptions: FakeRequestOptions(), ), ); - await channelClient.addMembers(members, message); + await channelClient.addMembers(memberIds, message); verify(() => mockDio.post('/channels/messaging/testid', - data: {'add_members': members, 'message': message.toJson()})) + data: {'add_members': memberIds, 'message': message.toJson()})) .called(1); }); @@ -729,6 +844,19 @@ void main() { tokenProvider: (_) async => '', ); final channelClient = client.channel('messaging', id: 'testid'); + final channelModel = ChannelModel(cid: 'messaging:testid'); + + when(() => mockDio.post( + any(), + data: any(named: 'data'), + )).thenAnswer((_) async => Response( + data: jsonEncode(ChannelState(channel: channelModel)), + statusCode: 200, + requestOptions: FakeRequestOptions(), + )); + + await channelClient.watch(); + final message = Message(text: 'test'); when( @@ -738,7 +866,10 @@ void main() { ), ).thenAnswer( (_) async => Response( - data: '{}', + data: jsonEncode({ + 'message': message, + 'channel': channelModel, + }), statusCode: 200, requestOptions: FakeRequestOptions(), ), @@ -1069,8 +1200,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 +1837,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 +2158,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 { @@ -2077,6 +2208,19 @@ void main() { tokenProvider: (_) async => '', ); final channelClient = client.channel('messaging', id: 'testid'); + final channelModel = ChannelModel(cid: 'messaging:testid'); + + when(() => mockDio.post( + any(), + data: any(named: 'data'), + )).thenAnswer((_) async => Response( + data: jsonEncode(ChannelState(channel: channelModel)), + statusCode: 200, + requestOptions: FakeRequestOptions(), + )); + + await channelClient.watch(); + final message = Message(text: 'test'); when( @@ -2089,7 +2233,10 @@ void main() { ), ).thenAnswer( (_) async => Response( - data: '{}', + data: jsonEncode({ + 'channel': channelModel, + 'message': message, + }), statusCode: 200, requestOptions: FakeRequestOptions(), ), @@ -2176,6 +2323,19 @@ void main() { tokenProvider: (_) async => '', ); final channelClient = client.channel('messaging', id: 'testid'); + final channelModel = ChannelModel(cid: 'messaging:testid'); + + when(() => mockDio.post( + any(), + data: any(named: 'data'), + )).thenAnswer((_) async => Response( + data: jsonEncode(ChannelState(channel: channelModel)), + statusCode: 200, + requestOptions: FakeRequestOptions(), + )); + + await channelClient.watch(); + final message = Message(text: 'test'); when( @@ -2185,7 +2345,10 @@ void main() { ), ).thenAnswer( (_) async => Response( - data: '{}', + data: jsonEncode({ + 'message': message, + 'channel': channelModel, + }), statusCode: 200, requestOptions: FakeRequestOptions(), ), @@ -2210,26 +2373,45 @@ void main() { tokenProvider: (_) async => '', ); final channelClient = client.channel('messaging', id: 'testid'); - final members = ['vishal']; + final channelModel = ChannelModel(cid: 'messaging:testid'); + + when(() => mockDio.post( + any(), + data: any(named: 'data'), + )).thenAnswer((_) async => Response( + data: jsonEncode(ChannelState(channel: channelModel)), + statusCode: 200, + requestOptions: FakeRequestOptions(), + )); + + await channelClient.watch(); + + final members = [Member(userId: 'vishal')]; + final memberIds = members.map((e) => e.userId!).toList(); final message = Message(text: 'test'); when( () => mockDio.post( '/channels/messaging/testid', - data: {'invites': members, 'message': message.toJson()}, + data: {'invites': memberIds, 'message': message.toJson()}, ), ).thenAnswer( (_) async => Response( - data: '{}', + data: jsonEncode({ + 'members': members, + 'message': message, + 'channel': channelModel, + }), statusCode: 200, requestOptions: FakeRequestOptions(), ), ); - await channelClient.inviteMembers(members, message); + await channelClient.inviteMembers(memberIds, message); verify(() => mockDio.post('/channels/messaging/testid', - data: {'invites': members, 'message': message.toJson()})).called(1); + data: {'invites': memberIds, 'message': message.toJson()})) + .called(1); }); test('removeMembers', () async { @@ -2244,27 +2426,46 @@ void main() { tokenProvider: (_) async => '', ); final channelClient = client.channel('messaging', id: 'testid'); - final members = ['vishal']; + final channelModel = ChannelModel(cid: 'messaging:testid'); + + when(() => mockDio.post( + any(), + data: any(named: 'data'), + )).thenAnswer((_) async => Response( + data: jsonEncode(ChannelState(channel: channelModel)), + statusCode: 200, + requestOptions: FakeRequestOptions(), + )); + + await channelClient.watch(); + + final members = [Member(userId: 'vishal')]; + final memberIds = members.map((e) => e.userId!).toList(); final message = Message(text: 'test'); when( () => mockDio.post( '/channels/messaging/testid', - data: {'remove_members': members, 'message': message.toJson()}, + data: {'remove_members': memberIds, 'message': message.toJson()}, ), ).thenAnswer( (_) async => Response( - data: '{}', + data: jsonEncode({ + 'members': members, + 'message': message, + 'channel': channelModel, + }), statusCode: 200, requestOptions: FakeRequestOptions(), ), ); - await channelClient.removeMembers(members, message); + await channelClient.removeMembers(memberIds, message); - verify(() => mockDio.post('/channels/messaging/testid', - data: {'remove_members': members, 'message': message.toJson()})) - .called(1); + verify(() => mockDio.post('/channels/messaging/testid', data: { + 'remove_members': memberIds, + 'message': message.toJson() + })).called(1); }); test('hide', () async { diff --git a/packages/stream_chat/test/src/api/requests_test.dart b/packages/stream_chat/test/src/api/requests_test.dart index 1e46fee0..76253403 100644 --- a/packages/stream_chat/test/src/api/requests_test.dart +++ b/packages/stream_chat/test/src/api/requests_test.dart @@ -1,5 +1,5 @@ -import 'package:test/test.dart'; import 'package:stream_chat/stream_chat.dart'; +import 'package:test/test.dart'; void main() { group('src/api/requests', () { @@ -12,7 +12,8 @@ void main() { test('PaginationParams', () { const option = PaginationParams(); final j = option.toJson(); - expect(j, {'limit': 10, 'offset': 0}); + expect(j, containsPair('limit', 10)); + expect(j, containsPair('offset', 0)); }); }); } diff --git a/packages/stream_chat/test/src/api/responses_test.dart b/packages/stream_chat/test/src/api/responses_test.dart index ad81aeff..f91248af 100644 --- a/packages/stream_chat/test/src/api/responses_test.dart +++ b/packages/stream_chat/test/src/api/responses_test.dart @@ -12,7 +12,8 @@ import 'package:stream_chat/stream_chat.dart'; void main() { group('src/api/responses', () { test('QueryChannelsResponse', () { - const jsonExample = r'''{ + const jsonExample = r''' + { "channels": [ { "channel": { @@ -3432,7 +3433,8 @@ void main() { }); test('SendReactionResponse', () { - const jsonExample = r'''{"message": { + const jsonExample = r''' + {"message": { "id": "c6076f11-7768-4a04-bdf2-c43dddd6d666", "text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.", "html": "\u003cp\u003eWhat we don’t know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n", @@ -3481,7 +3483,8 @@ void main() { }); test('UpdateUsersResponse', () { - const jsonExample = '''{"users": {"bbb19d9a-ee50-45bc-84e5-0584e79d0c9e":{ + const jsonExample = ''' + {"users": {"bbb19d9a-ee50-45bc-84e5-0584e79d0c9e":{ "id": "bbb19d9a-ee50-45bc-84e5-0584e79d0c9e", "role": "user", "created_at": "2020-01-28T22:17:30.826259Z", @@ -3505,7 +3508,8 @@ void main() { }); test('GetMessagesByIdResponse', () { - const jsonExample = r'''{"messages":[{ + const jsonExample = r''' + {"messages":[{ "id": "c6076f11-7768-4a04-bdf2-c43dddd6d666", "text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.", "html": "\u003cp\u003eWhat we don’t know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n", @@ -3536,7 +3540,8 @@ void main() { }); test('SendActionResponse', () { - const jsonExample = r'''{"message":{ + const jsonExample = r''' + {"message":{ "id": "c6076f11-7768-4a04-bdf2-c43dddd6d666", "text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.", "html": "\u003cp\u003eWhat we don’t know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n", @@ -3566,7 +3571,8 @@ void main() { }); test('UpdateMessageResponse', () { - const jsonExample = r'''{"message":{ + const jsonExample = r''' + {"message":{ "id": "c6076f11-7768-4a04-bdf2-c43dddd6d666", "text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.", "html": "\u003cp\u003eWhat we don’t know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n", @@ -3596,7 +3602,8 @@ void main() { }); test('SendMessageResponse', () { - const jsonExample = r'''{"message":{ + const jsonExample = r''' + {"message":{ "id": "c6076f11-7768-4a04-bdf2-c43dddd6d666", "text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.", "html": "\u003cp\u003eWhat we don’t know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n", @@ -3626,7 +3633,8 @@ void main() { }); test('GetMessageResponse', () { - const jsonExample = r'''{"message":{ + const jsonExample = r''' + {"message":{ "id": "c6076f11-7768-4a04-bdf2-c43dddd6d666", "text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.", "html": "\u003cp\u003eWhat we don’t know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n", @@ -3656,7 +3664,8 @@ void main() { }); test('UpdateChannelResponse', () { - const jsonExample = r'''{"message":{ + const jsonExample = r''' + {"message":{ "id": "c6076f11-7768-4a04-bdf2-c43dddd6d666", "text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.", "html": "\u003cp\u003eWhat we don’t know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n", @@ -3769,7 +3778,8 @@ void main() { }); test('InviteMembersResponse', () { - const jsonExample = r'''{"message":{ + const jsonExample = r''' + {"message":{ "id": "c6076f11-7768-4a04-bdf2-c43dddd6d666", "text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.", "html": "\u003cp\u003eWhat we don’t know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n", @@ -3882,7 +3892,8 @@ void main() { }); test('RemoveMembersResponse', () { - const jsonExample = r'''{"message":{ + const jsonExample = r''' + {"message":{ "id": "c6076f11-7768-4a04-bdf2-c43dddd6d666", "text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.", "html": "\u003cp\u003eWhat we don’t know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n", @@ -3995,7 +4006,8 @@ void main() { }); test('AddMembersResponse', () { - const jsonExample = r'''{"message":{ + const jsonExample = r''' + {"message":{ "id": "c6076f11-7768-4a04-bdf2-c43dddd6d666", "text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.", "html": "\u003cp\u003eWhat we don’t know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n", @@ -4108,7 +4120,8 @@ void main() { }); test('AcceptInviteResponse', () { - const jsonExample = r'''{"message":{ + const jsonExample = r''' + {"message":{ "id": "c6076f11-7768-4a04-bdf2-c43dddd6d666", "text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.", "html": "\u003cp\u003eWhat we don’t know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n", @@ -4221,7 +4234,8 @@ void main() { }); test('RejectInviteResponse', () { - const jsonExample = r'''{"message":{ + const jsonExample = r''' + {"message":{ "id": "c6076f11-7768-4a04-bdf2-c43dddd6d666", "text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.", "html": "\u003cp\u003eWhat we don’t know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n", diff --git a/packages/stream_chat/test/src/api/websocket_test.dart b/packages/stream_chat/test/src/api/websocket_test.dart index 5993f0bf..b9c897a6 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) {} } @@ -37,7 +35,7 @@ void main() { }); test('should connect with correct parameters', () async { - final ConnectWebSocket connectFunc = MockFunctions().connectFunc; + final connectFunc = MockFunctions().connectFunc; final ws = WebSocket( baseUrl: 'baseurl', user: User(id: 'testid'), @@ -75,7 +73,7 @@ void main() { test('should connect with correct parameters and handle events', () async { final handleFunc = MockFunctions().handleFunc; - final ConnectWebSocket connectFunc = MockFunctions().connectFunc; + final connectFunc = MockFunctions().connectFunc; final ws = WebSocket( baseUrl: 'baseurl', user: User(id: 'testid'), @@ -111,7 +109,7 @@ void main() { test('should close correctly the controller', () async { final handleFunc = MockFunctions().handleFunc; - final ConnectWebSocket connectFunc = MockFunctions().connectFunc; + final connectFunc = MockFunctions().connectFunc; final ws = WebSocket( baseUrl: 'baseurl', user: User(id: 'testid'), @@ -126,8 +124,10 @@ void main() { const computedUrl = 'wss://baseurl/connect?test=true&json=%7B%22payload%22%3A%22test%22%2C%22user_details%22%3A%7B%22id%22%3A%22testid%22%7D%7D'; + final mockWSSink = MockWSSink(); + when(() => mockWSChannel.sink).thenAnswer((_) => mockWSSink); + when(() => mockWSSink.close(any(), any())).thenAnswer((_) async => null); when(() => connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel); - when(() => mockWSChannel.sink).thenAnswer((_) => MockWSSink()); when(() => mockWSChannel.stream).thenAnswer((_) => streamController.stream); final connect = ws.connect().then((_) { @@ -148,7 +148,7 @@ void main() { test('should close correctly the controller while connecting', () async { final handleFunc = MockFunctions().handleFunc; - final ConnectWebSocket connectFunc = MockFunctions().connectFunc; + final connectFunc = MockFunctions().connectFunc; final ws = WebSocket( baseUrl: 'baseurl', @@ -167,8 +167,10 @@ void main() { const computedUrl = 'wss://baseurl/connect?test=true&json=%7B%22payload%22%3A%22test%22%2C%22user_details%22%3A%7B%22id%22%3A%22testid%22%7D%7D'; + final mockWSSink = MockWSSink(); + when(() => mockWSChannel.sink).thenAnswer((_) => mockWSSink); + when(() => mockWSSink.close(any(), any())).thenAnswer((_) async => null); when(() => connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel); - when(() => mockWSChannel.sink).thenAnswer((_) => MockWSSink()); when(() => mockWSChannel.stream).thenAnswer((_) => streamController.stream); ws.connect(); @@ -183,7 +185,7 @@ void main() { test('should run correctly health check', () async { final handleFunc = MockFunctions().handleFunc; - final ConnectWebSocket connectFunc = MockFunctions().connectFunc; + final connectFunc = MockFunctions().connectFunc; final ws = WebSocket( baseUrl: 'baseurl', user: User(id: 'testid'), @@ -201,7 +203,8 @@ void main() { when(() => connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel); when(() => mockWSChannel.stream).thenAnswer((_) => streamController.stream); - when(() => mockWSChannel.sink).thenReturn(mockWSSink); + when(() => mockWSChannel.sink).thenAnswer((_) => mockWSSink); + when(() => mockWSSink.close(any(), any())).thenAnswer((_) async => null); final timer = Timer.periodic( const Duration(milliseconds: 1000), @@ -227,7 +230,7 @@ void main() { test('should run correctly reconnection check', () async { final handleFunc = MockFunctions().handleFunc; - final ConnectWebSocket connectFunc = MockFunctions().connectFunc; + final connectFunc = MockFunctions().connectFunc; Logger.root.level = Level.ALL; final ws = WebSocket( baseUrl: 'baseurl', @@ -247,7 +250,8 @@ void main() { when(() => connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel); when(() => mockWSChannel.stream).thenAnswer((_) => streamController.stream); - when(() => mockWSChannel.sink).thenReturn(mockWSSink); + when(() => mockWSChannel.sink).thenAnswer((_) => mockWSSink); + when(() => mockWSSink.close(any(), any())).thenAnswer((_) async => null); final connect = ws.connect().then((_) { streamController.sink.add('{}'); @@ -272,7 +276,7 @@ void main() { test('should close correctly the controller', () async { final handleFunc = MockFunctions().handleFunc; - final ConnectWebSocket connectFunc = MockFunctions().connectFunc; + final connectFunc = MockFunctions().connectFunc; final ws = WebSocket( baseUrl: 'baseurl', user: User(id: 'testid'), @@ -290,7 +294,8 @@ void main() { when(() => connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel); when(() => mockWSChannel.stream).thenAnswer((_) => streamController.stream); - when(() => mockWSChannel.sink).thenReturn(mockWSSink); + when(() => mockWSChannel.sink).thenAnswer((_) => mockWSSink); + when(() => mockWSSink.close(any(), any())).thenAnswer((_) async => null); final connect = ws.connect().then((_) { streamController.sink.add('{}'); @@ -309,7 +314,7 @@ void main() { }); test('should throw an error', () async { - final ConnectWebSocket connectFunc = MockFunctions().connectFunc; + final connectFunc = MockFunctions().connectFunc; final ws = WebSocket( baseUrl: 'baseurl', user: User(id: 'testid'), diff --git a/packages/stream_chat/test/src/client_test.dart b/packages/stream_chat/test/src/client_test.dart index 6ae8ee3e..c93059d5 100644 --- a/packages/stream_chat/test/src/client_test.dart +++ b/packages/stream_chat/test/src/client_test.dart @@ -9,9 +9,9 @@ import 'package:mocktail/mocktail.dart'; import 'package:stream_chat/src/api/requests.dart'; import 'package:stream_chat/src/client.dart'; import 'package:stream_chat/src/exceptions.dart'; +import 'package:stream_chat/src/models/channel_model.dart'; import 'package:stream_chat/src/models/message.dart'; import 'package:stream_chat/src/models/user.dart'; -import 'package:stream_chat/src/models/channel_model.dart'; import 'package:test/test.dart'; class MockDio extends Mock implements DioForNative {} @@ -21,7 +21,7 @@ class FakeRequestOptions extends Fake implements RequestOptions {} class MockHttpClientAdapter extends Mock implements HttpClientAdapter {} class Functions { - Future tokenProvider(String userId) => null; + Future tokenProvider(String userId) async => ''; } class MockFunctions extends Mock implements Functions {} @@ -155,7 +155,9 @@ void main() { 'sort': sortOptions, } ..addAll(options) - ..addAll(paginationParams.toJson())), + ..addAll(paginationParams + .toJson() + .map((key, value) => MapEntry(key, value as Object)))), }; when( @@ -735,7 +737,9 @@ void main() { when(() => mockDio.interceptors).thenReturn(Interceptors()); final client = StreamChatClient('api-key', httpClient: mockDio); - final message = Message(id: 'test', updatedAt: DateTime.now()); + final message = Message( + id: 'test', + ); when( () => mockDio.post( @@ -744,7 +748,7 @@ void main() { ), ).thenAnswer( (_) async => Response( - data: '{}', + data: jsonEncode({'message': message}), statusCode: 200, requestOptions: FakeRequestOptions(), ), @@ -789,7 +793,7 @@ void main() { when(() => mockDio.get('/messages/$messageId')).thenAnswer( (_) async => Response( - data: '{}', + data: jsonEncode({'message': Message(id: messageId)}), statusCode: 200, requestOptions: FakeRequestOptions(), ), @@ -1111,7 +1115,7 @@ void main() { ), ).thenAnswer( (_) async => Response( - data: '{}', + data: jsonEncode({'message': message}), statusCode: 200, requestOptions: FakeRequestOptions(), ), @@ -1133,7 +1137,7 @@ void main() { ), ).thenAnswer( (_) async => Response( - data: '{}', + data: jsonEncode({'message': message}), statusCode: 200, requestOptions: FakeRequestOptions(), ), @@ -1173,7 +1177,7 @@ void main() { ), ).thenAnswer( (_) async => Response( - data: '{}', + data: jsonEncode({'channel': ChannelModel(cid: 'messaging:test')}), statusCode: 200, requestOptions: FakeRequestOptions(), ), diff --git a/packages/stream_chat/test/src/models/action_test.dart b/packages/stream_chat/test/src/models/action_test.dart index 5d142953..22e66078 100644 --- a/packages/stream_chat/test/src/models/action_test.dart +++ b/packages/stream_chat/test/src/models/action_test.dart @@ -5,7 +5,8 @@ import 'package:stream_chat/src/models/action.dart'; void main() { group('src/models/action', () { - const jsonExample = '''{ + const jsonExample = ''' + { "name": "name", "style": "style", "text": "text", diff --git a/packages/stream_chat/test/src/models/attachment_test.dart b/packages/stream_chat/test/src/models/attachment_test.dart index 3124ddd2..1342e72e 100644 --- a/packages/stream_chat/test/src/models/attachment_test.dart +++ b/packages/stream_chat/test/src/models/attachment_test.dart @@ -1,12 +1,13 @@ import 'dart:convert'; -import 'package:stream_chat/src/models/attachment.dart'; -import 'package:stream_chat/src/models/action.dart'; +import 'package:stream_chat/src/models/action.dart'; +import 'package:stream_chat/src/models/attachment.dart'; import 'package:test/test.dart'; void main() { group('src/models/attachment', () { - const jsonExample = '''{ + const jsonExample = ''' + { "type": "giphy", "title": "awesome", "title_link": "https://giphy.com/gifs/nrkp3-dance-happy-3o7TKnCdBx5cMg0qti", @@ -66,7 +67,8 @@ void main() { 'type': 'image', 'title': 'soo', 'title_link': - 'https://giphy.com/gifs/nrkp3-dance-happy-3o7TKnCdBx5cMg0qti' + 'https://giphy.com/gifs/nrkp3-dance-happy-3o7TKnCdBx5cMg0qti', + 'actions': [], }, ); }); 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..afe384f5 100644 --- a/packages/stream_chat/test/src/models/channel_state_test.dart +++ b/packages/stream_chat/test/src/models/channel_state_test.dart @@ -1,16 +1,17 @@ import 'dart:convert'; -import 'package:test/test.dart'; import 'package:stream_chat/src/models/channel_config.dart'; import 'package:stream_chat/src/models/channel_state.dart'; import 'package:stream_chat/src/models/command.dart'; import 'package:stream_chat/src/models/message.dart'; import 'package:stream_chat/src/models/user.dart'; import 'package:stream_chat/stream_chat.dart'; +import 'package:test/test.dart'; void main() { group('src/models/channel_state', () { - const jsonExample = '''{ + const jsonExample = ''' + { "channel": { "id": "dev", "type": "team", @@ -844,26 +845,26 @@ 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)); @@ -888,8 +889,8 @@ void main() { "image": "https://cdn.chrisshort.net/testing-certificate-chains-in-go/GOPHER_MIC_DROP.png", "example": 1 }, - "watchers": null, - "read": null, + "watchers": [], + "read": [], "messages": [ { "id": "dry-meadow-0-2b73cc8b-cd86-4a01-8d40-bd82ad07a030", @@ -901,7 +902,7 @@ void main() { "show_in_channel": null, "mentioned_users": [], "status": "SENT", - "skip_push": null, + "skip_push": false, "silent": false, "pinned": false, "pinned_at": null, @@ -918,7 +919,7 @@ void main() { "show_in_channel": null, "mentioned_users": [], "status": "SENT", - "skip_push": null, + "skip_push": false, "silent": false, "pinned": false, "pinned_at": null, @@ -928,7 +929,7 @@ void main() { { "id": "dry-meadow-0-53e6299f-9b97-4a9c-a27e-7e2dde49b7e0", "text": "test message", - "skip_push": null, + "skip_push": false, "attachments": [], "parent_id": null, "quoted_message": null, @@ -951,7 +952,7 @@ void main() { "quoted_message_id": null, "show_in_channel": null, "mentioned_users": [], - "skip_push": null, + "skip_push": false, "status": "SENT", "silent": false, "pinned": false, @@ -963,7 +964,7 @@ void main() { "id": "dry-meadow-0-64d7970f-ede8-4b31-9738-1bc1756d2bfe", "text": "test", "attachments": [], - "skip_push": null, + "skip_push": false, "parent_id": null, "quoted_message": null, "quoted_message_id": null, @@ -981,7 +982,7 @@ void main() { "text": "hi", "attachments": [], "parent_id": null, - "skip_push": null, + "skip_push": false, "quoted_message": null, "quoted_message_id": null, "show_in_channel": null, @@ -999,7 +1000,7 @@ void main() { "attachments": [], "parent_id": null, "quoted_message": null, - "skip_push": null, + "skip_push": false, "quoted_message_id": null, "show_in_channel": null, "mentioned_users": [], @@ -1022,7 +1023,7 @@ void main() { "status": "SENT", "silent": false, "pinned": false, - "skip_push": null, + "skip_push": false, "pinned_at": null, "pin_expires": null, "pinned_by": null @@ -1040,7 +1041,7 @@ void main() { "silent": false, "pinned": false, "pinned_at": null, - "skip_push": null, + "skip_push": false, "pin_expires": null, "pinned_by": null }, @@ -1052,7 +1053,7 @@ void main() { "quoted_message": null, "quoted_message_id": null, "show_in_channel": null, - "skip_push": null, + "skip_push": false, "mentioned_users": [], "status": "SENT", "silent": false, @@ -1070,7 +1071,7 @@ void main() { "quoted_message_id": null, "show_in_channel": null, "mentioned_users": [], - "skip_push": null, + "skip_push": false, "status": "SENT", "silent": false, "pinned": false, @@ -1089,7 +1090,7 @@ void main() { "mentioned_users": [], "status": "SENT", "silent": false, - "skip_push": null, + "skip_push": false, "pinned": false, "pinned_at": null, "pin_expires": null, @@ -1099,7 +1100,7 @@ void main() { "id": "icy-recipe-7-935c396e-ddf8-4a9a-951c-0a12fa5bf055", "text": "what are you doing?", "attachments": [], - "skip_push": null, + "skip_push": false, "parent_id": null, "quoted_message": null, "quoted_message_id": null, @@ -1117,7 +1118,7 @@ void main() { "text": "👍", "attachments": [], "parent_id": null, - "skip_push": null, + "skip_push": false, "quoted_message": null, "quoted_message_id": null, "show_in_channel": null, @@ -1133,7 +1134,7 @@ void main() { "id": "snowy-credit-3-3e0c1a0d-d22f-42ee-b2a1-f9f49477bf21", "text": "sdasas", "attachments": [], - "skip_push": null, + "skip_push": false, "parent_id": null, "quoted_message": null, "quoted_message_id": null, @@ -1154,7 +1155,7 @@ void main() { "quoted_message": null, "quoted_message_id": null, "show_in_channel": null, - "skip_push": null, + "skip_push": false, "mentioned_users": [], "status": "SENT", "silent": false, @@ -1167,7 +1168,7 @@ void main() { "id": "snowy-credit-3-cfaf0b46-1daa-49c5-947c-b16d6697487d", "text": "nhisagdhsadz", "attachments": [], - "skip_push": null, + "skip_push": false, "parent_id": null, "quoted_message": null, "quoted_message_id": null, @@ -1186,7 +1187,7 @@ void main() { "attachments": [], "parent_id": null, "quoted_message": null, - "skip_push": null, + "skip_push": false, "quoted_message_id": null, "show_in_channel": null, "mentioned_users": [], @@ -1203,7 +1204,7 @@ void main() { "attachments": [], "parent_id": null, "quoted_message": null, - "skip_push": null, + "skip_push": false, "quoted_message_id": null, "show_in_channel": null, "mentioned_users": [], @@ -1211,7 +1212,7 @@ void main() { "silent": false, "pinned": false, "pinned_at": null, - "skip_push": null, + "skip_push": false, "pin_expires": null, "pinned_by": null }, @@ -1228,7 +1229,7 @@ void main() { "silent": false, "pinned": false, "pinned_at": null, - "skip_push": null, + "skip_push": false, "pin_expires": null, "pinned_by": null }, @@ -1245,7 +1246,7 @@ void main() { "silent": false, "pinned": false, "pinned_at": null, - "skip_push": null, + "skip_push": false, "pin_expires": null, "pinned_by": null }, @@ -1262,7 +1263,7 @@ void main() { "silent": false, "pinned": false, "pinned_at": null, - "skip_push": null, + "skip_push": false, "pin_expires": null, "pinned_by": null }, @@ -1279,7 +1280,7 @@ void main() { "silent": false, "pinned": false, "pinned_at": null, - "skip_push": null, + "skip_push": false, "pin_expires": null, "pinned_by": null }, @@ -1296,7 +1297,7 @@ void main() { "silent": false, "pinned": false, "pinned_at": null, - "skip_push": null, + "skip_push": false, "pin_expires": null, "pinned_by": null }, @@ -1313,7 +1314,7 @@ void main() { "silent": false, "pinned": false, "pinned_at": null, - "skip_push": null, + "skip_push": false, "pin_expires": null, "pinned_by": null } @@ -1329,10 +1330,10 @@ void main() { members: [], messages: (j['messages'] as List).map((m) => Message.fromJson(m)).toList(), - read: null, + read: [], watcherCount: 5, pinnedMessages: [], - watchers: null, + watchers: [], ); expect( diff --git a/packages/stream_chat/test/src/models/channel_test.dart b/packages/stream_chat/test/src/models/channel_test.dart index 9f6aa5b7..01c06fee 100644 --- a/packages/stream_chat/test/src/models/channel_test.dart +++ b/packages/stream_chat/test/src/models/channel_test.dart @@ -1,7 +1,7 @@ import 'dart:convert'; -import 'package:test/test.dart'; import 'package:stream_chat/src/models/channel_model.dart'; +import 'package:test/test.dart'; void main() { group('src/models/channel', () { @@ -9,7 +9,7 @@ void main() { { "id": "test", "type": "livestream", - "cid": "test:livestream", + "cid": "livestream:test", "cats": true, "fruit": ["bananas", "apples"] } @@ -19,9 +19,9 @@ void main() { final channel = ChannelModel.fromJson(json.decode(jsonExample)); 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.cid, equals('livestream:test')); + expect(channel.extraData!['cats'], equals(true)); + expect(channel.extraData!['fruit'], equals(['bananas', 'apples'])); }); test('should serialize to json correctly', () { @@ -34,7 +34,7 @@ void main() { expect( channel.toJson(), - {'id': 'id', 'type': 'type', 'name': 'cool'}, + {'id': 'id', 'type': 'type', 'frozen': false, 'name': 'cool'}, ); }); @@ -44,7 +44,6 @@ void main() { id: 'id', cid: 'a:a', extraData: {'name': 'cool'}, - frozen: false, ); expect( diff --git a/packages/stream_chat/test/src/models/device_test.dart b/packages/stream_chat/test/src/models/device_test.dart index 27a94982..5cbf015d 100644 --- a/packages/stream_chat/test/src/models/device_test.dart +++ b/packages/stream_chat/test/src/models/device_test.dart @@ -5,7 +5,8 @@ import 'package:stream_chat/src/models/device.dart'; void main() { group('src/models/device', () { - const jsonExample = '''{ + const jsonExample = ''' + { "id": "device-id", "push_provider": "push-provider" }'''; diff --git a/packages/stream_chat/test/src/models/event_test.dart b/packages/stream_chat/test/src/models/event_test.dart index a7c669df..96efa844 100644 --- a/packages/stream_chat/test/src/models/event_test.dart +++ b/packages/stream_chat/test/src/models/event_test.dart @@ -1,9 +1,9 @@ import 'dart:convert'; -import 'package:test/test.dart'; import 'package:stream_chat/src/models/event.dart'; import 'package:stream_chat/src/models/own_user.dart'; import 'package:stream_chat/stream_chat.dart'; +import 'package:test/test.dart'; void main() { group('src/models/event', () { @@ -47,6 +47,7 @@ void main() { expect(event.createdAt, isA()); expect(event.me, isA()); expect(event.user, isA()); + expect(event.isLocal, false); }); test('should serialize to json correctly', () { @@ -77,11 +78,11 @@ void main() { 'total_unread_count': 1, 'unread_channels': 1, 'online': true, - 'is_local': true, 'member': null, 'channel_id': null, 'channel_type': null, 'parent_id': null, + 'is_local': true, }, ); }); diff --git a/packages/stream_chat/test/src/models/message_test.dart b/packages/stream_chat/test/src/models/message_test.dart index 0898e902..1219e137 100644 --- a/packages/stream_chat/test/src/models/message_test.dart +++ b/packages/stream_chat/test/src/models/message_test.dart @@ -1,14 +1,15 @@ import 'dart:convert'; -import 'package:test/test.dart'; import 'package:stream_chat/src/models/attachment.dart'; import 'package:stream_chat/src/models/message.dart'; import 'package:stream_chat/src/models/reaction.dart'; import 'package:stream_chat/src/models/user.dart'; +import 'package:test/test.dart'; void main() { group('src/models/message', () { - const jsonExample = r'''{ + const jsonExample = r''' + { "id": "4637f7e4-a06b-42db-ba5a-8d8270dd926f", "text": "https://giphy.com/gifs/the-lion-king-live-action-5zvN79uTGfLMOVfQaA", "type": "regular", @@ -101,9 +102,8 @@ void main() { id: '4637f7e4-a06b-42db-ba5a-8d8270dd926f', text: 'https://giphy.com/gifs/the-lion-king-live-action-5zvN79uTGfLMOVfQaA', - silent: false, attachments: [ - Attachment.fromJson({ + Attachment.fromJson(const { 'type': 'video', 'author_name': 'GIPHY', 'title': 'The Lion King Disney GIF - Find \u0026 Share on GIPHY', @@ -123,7 +123,7 @@ void main() { ], showInChannel: true, parentId: 'parentId', - extraData: {'hey': 'test'}, + extraData: const {'hey': 'test'}, ); expect( @@ -133,7 +133,7 @@ void main() { "id": "4637f7e4-a06b-42db-ba5a-8d8270dd926f", "text": "https://giphy.com/gifs/the-lion-king-live-action-5zvN79uTGfLMOVfQaA", "silent": false, - "skip_push": null, + "skip_push": false, "attachments": [ { "type": "video", @@ -144,10 +144,11 @@ void main() { "og_scrape_url": "https://giphy.com/gifs/the-lion-king-live-action-5zvN79uTGfLMOVfQaA", "image_url": "https://media.giphy.com/media/5zvN79uTGfLMOVfQaA/giphy.gif", "author_name": "GIPHY", - "asset_url": "https://media.giphy.com/media/5zvN79uTGfLMOVfQaA/giphy.mp4" + "asset_url": "https://media.giphy.com/media/5zvN79uTGfLMOVfQaA/giphy.mp4", + "actions": [] } ], - "mentioned_users": null, + "mentioned_users": [], "parent_id": "parentId", "quoted_message": null, "quoted_message_id": null, diff --git a/packages/stream_chat/test/src/models/reaction_test.dart b/packages/stream_chat/test/src/models/reaction_test.dart index 2a1d5431..795e2967 100644 --- a/packages/stream_chat/test/src/models/reaction_test.dart +++ b/packages/stream_chat/test/src/models/reaction_test.dart @@ -1,8 +1,8 @@ import 'dart:convert'; -import 'package:test/test.dart'; import 'package:stream_chat/src/models/reaction.dart'; import 'package:stream_chat/src/models/user.dart'; +import 'package:test/test.dart'; void main() { group('src/models/reaction', () { @@ -33,8 +33,8 @@ void main() { expect(reaction.createdAt, DateTime.parse('2020-01-28T22:17:31.108742Z')); expect(reaction.type, 'wow'); expect( - reaction.user.toJson(), - User.init('2de0297c-f3f2-489d-b930-ef77342edccf', extraData: { + reaction.user?.toJson(), + User(id: '2de0297c-f3f2-489d-b930-ef77342edccf', extraData: { 'image': 'https://randomuser.me/api/portraits/women/45.jpg', 'name': 'Daisy Morgan' }).toJson(), @@ -49,7 +49,7 @@ void main() { messageId: '76cd8c82-b557-4e48-9d12-87995d3a0e04', createdAt: DateTime.parse('2020-01-28T22:17:31.108742Z'), type: 'wow', - user: User.init('2de0297c-f3f2-489d-b930-ef77342edccf', extraData: { + user: User(id: '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..66efe809 100644 --- a/packages/stream_chat/test/src/models/read_test.dart +++ b/packages/stream_chat/test/src/models/read_test.dart @@ -26,7 +26,7 @@ void main() { test('should serialize to json correctly', () { final read = Read( lastRead: DateTime.parse('2020-01-28T22:17:30.966485504Z'), - user: User.init('bbb19d9a-ee50-45bc-84e5-0584e79d0c9e'), + user: User(id: 'bbb19d9a-ee50-45bc-84e5-0584e79d0c9e'), unreadMessages: 10, ); diff --git a/packages/stream_chat/test/src/models/serialization_test.dart b/packages/stream_chat/test/src/models/serialization_test.dart index e8a2de3f..9e64caad 100644 --- a/packages/stream_chat/test/src/models/serialization_test.dart +++ b/packages/stream_chat/test/src/models/serialization_test.dart @@ -49,12 +49,12 @@ void main() { }); test('should return null', () { - final result = Serialization.moveToExtraDataFromRoot(null, [ + final result = Serialization.moveToExtraDataFromRoot({}, [ 'prop1', 'prop2', ]); - expect(result, null); + expect(result, {'extra_data': {}}); }); }); } diff --git a/packages/stream_chat/test/src/models/user_test.dart b/packages/stream_chat/test/src/models/user_test.dart index 0a307126..8f56f7e1 100644 --- a/packages/stream_chat/test/src/models/user_test.dart +++ b/packages/stream_chat/test/src/models/user_test.dart @@ -1,13 +1,14 @@ import 'dart:convert'; -import 'package:test/test.dart'; import 'package:stream_chat/src/models/user.dart'; +import 'package:test/test.dart'; void main() { group('src/models/user', () { const jsonExample = ''' { - "id": "bbb19d9a-ee50-45bc-84e5-0584e79d0c9e" + "id": "bbb19d9a-ee50-45bc-84e5-0584e79d0c9e", + "role": "test-role" } '''; diff --git a/packages/stream_chat/test/version_test.dart b/packages/stream_chat/test/version_test.dart index 55b1818a..9bbd446f 100644 --- a/packages/stream_chat/test/version_test.dart +++ b/packages/stream_chat/test/version_test.dart @@ -17,8 +17,8 @@ void main() { final String pubspecPath = '${Directory.current.path}/pubspec.yaml'; final String pubspec = File(pubspecPath).readAsStringSync(); final RegExp regex = RegExp('version:\s*(.*)'); - final RegExpMatch match = regex.firstMatch(pubspec); + final RegExpMatch? match = regex.firstMatch(pubspec); expect(match, isNotNull); - expect(PACKAGE_VERSION, match.group(1).trim()); + expect(PACKAGE_VERSION, match?.group(1)?.trim()); }); } diff --git a/packages/stream_chat_flutter/pubspec.yaml b/packages/stream_chat_flutter/pubspec.yaml index 522a7203..ffee617b 100644 --- a/packages/stream_chat_flutter/pubspec.yaml +++ b/packages/stream_chat_flutter/pubspec.yaml @@ -43,7 +43,7 @@ dependencies: ezanimation: ^0.4.1 synchronized: ^3.0.0 characters: ^1.0.0 - dio: ">=4.0.0-prev3 <4.0.0" + dio: ^4.0.0 path_provider: ^2.0.0 video_thumbnail: ^0.2.5+1 diff --git a/packages/stream_chat_flutter/test/src/message_reactions_modal_test.dart b/packages/stream_chat_flutter/test/src/message_reactions_modal_test.dart index 044eb693..8745cb45 100644 --- a/packages/stream_chat_flutter/test/src/message_reactions_modal_test.dart +++ b/packages/stream_chat_flutter/test/src/message_reactions_modal_test.dart @@ -90,6 +90,7 @@ void main() { Reaction( messageId: 'test', user: User(id: 'testid'), + type: 'test', ), ], );