diff --git a/packages/stream_chat/lib/src/client/channel.dart b/packages/stream_chat/lib/src/client/channel.dart index 995de15c..a1945883 100644 --- a/packages/stream_chat/lib/src/client/channel.dart +++ b/packages/stream_chat/lib/src/client/channel.dart @@ -1259,7 +1259,9 @@ class Channel { if (preferOffline && cid != null) { final updatedState = await _client.chatPersistenceClient ?.getChannelStateByCid(cid!, messagePagination: messagesPagination); - if (updatedState != null && updatedState.messages.isNotEmpty) { + if (updatedState != null && + updatedState.messages != null && + updatedState.messages!.isNotEmpty) { if (this.state == null) { _initState(updatedState); } else { @@ -1570,7 +1572,7 @@ class ChannelClientState { void _checkExpiredAttachmentMessages(ChannelState channelState) async { final expiredAttachmentMessagesId = channelState.messages - .where((m) => + ?.where((m) => !_updatedMessagesIds.contains(m.id) && m.attachments.isNotEmpty && m.attachments.any((e) { @@ -1597,7 +1599,8 @@ class ChannelClientState { .map((e) => e.id) .toList(); - if (expiredAttachmentMessagesId.isNotEmpty) { + if (expiredAttachmentMessagesId != null && + expiredAttachmentMessagesId.isNotEmpty) { await _channel._initializedCompleter.future; _updatedMessagesIds.addAll(expiredAttachmentMessagesId); _channel.getMessagesById(expiredAttachmentMessagesId); @@ -1607,9 +1610,10 @@ class ChannelClientState { void _listenMemberAdded() { _subscriptions.add(_channel.on(EventType.memberAdded).listen((Event e) { final member = e.member; + final existingMembers = channelState.members ?? []; updateChannelState(channelState.copyWith( members: [ - ...channelState.members, + ...existingMembers, member!, ], )); @@ -1619,11 +1623,13 @@ class ChannelClientState { void _listenMemberRemoved() { _subscriptions.add(_channel.on(EventType.memberRemoved).listen((Event e) { final user = e.user; + final existingMembers = channelState.members ?? []; + final existingRead = channelState.read ?? []; updateChannelState(channelState.copyWith( - members: channelState.members + members: existingMembers .where((m) => m.userId != user!.id) .toList(growable: false), - read: channelState.read + read: existingRead .where((r) => r.user.id != user!.id) .toList(growable: false), )); @@ -1793,9 +1799,10 @@ class ChannelClientState { updateMessage(message); if (message.pinned) { + final _existingPinnedMessages = _channelState.pinnedMessages ?? []; _channelState = _channelState.copyWith( pinnedMessages: [ - ..._channelState.pinnedMessages, + ..._existingPinnedMessages, message, ], ); @@ -1931,7 +1938,7 @@ 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); @@ -1952,31 +1959,34 @@ class ChannelClientState { } /// Channel message list. - List get messages => _channelState.messages; + List get messages => _channelState.messages ?? []; /// Channel message list as a stream. Stream> get messagesStream => channelStateStream - .map((cs) => cs.messages) + .map((cs) => cs.messages ?? []) .distinct(const ListEquality().equals); /// Channel pinned message list. - List get pinnedMessages => _channelState.pinnedMessages; + List get pinnedMessages => + _channelState.pinnedMessages ?? []; /// Channel pinned message list as a stream. Stream> get pinnedMessagesStream => channelStateStream - .map((cs) => cs.pinnedMessages) + .map((cs) => cs.pinnedMessages ?? []) .distinct(const ListEquality().equals); /// Get channel last message. Message? get lastMessage => - _channelState.messages.isNotEmpty ? _channelState.messages.last : null; + _channelState.messages != null && _channelState.messages!.isNotEmpty + ? _channelState.messages!.last + : null; /// Get channel last message. Stream get lastMessageStream => messagesStream.map((event) => event.isNotEmpty ? event.last : null); /// Channel members list. - List get members => _channelState.members + List get members => (_channelState.members ?? []) .map((e) => e.copyWith(user: _channel.client.state.users[e.user!.id])) .toList(); @@ -1997,7 +2007,7 @@ class ChannelClientState { channelStateStream.map((cs) => cs.watcherCount); /// Channel watchers list. - List get watchers => _channelState.watchers + List get watchers => (_channelState.watchers ?? []) .map((e) => _channel.client.state.users[e.id] ?? e) .toList(); @@ -2018,10 +2028,11 @@ class ChannelClientState { String? get currentUserRole => currentUserMember?.role; /// 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 ?? []); bool _isCurrentUserRead(Read read) => read.user.id == _channel._client.state.currentUser!.id; @@ -2042,7 +2053,7 @@ class ChannelClientState { /// Setter for unread count. set unreadCount(int count) { - final reads = [..._channelState.read]; + final reads = [...read]; final currentUserReadIndex = reads.indexWhere(_isCurrentUserRead); if (currentUserReadIndex < 0) return; @@ -2097,31 +2108,37 @@ class ChannelClientState { /// Update channelState with updated information. void updateChannelState(ChannelState updatedState) { + final _existingStateMessages = _channelState.messages ?? []; + final _updatedStateMessages = updatedState.messages ?? []; final newMessages = [ - ...updatedState.messages, - ..._channelState.messages + ..._updatedStateMessages, + ..._existingStateMessages .where((m) => - !updatedState.messages.any((newMessage) => newMessage.id == m.id)) + !_updatedStateMessages.any((newMessage) => newMessage.id == m.id)) .toList(), ]..sort(_sortByCreatedAt); + final _existingStateWatchers = _channelState.watchers ?? []; + final _updatedStateWatchers = updatedState.watchers ?? []; final newWatchers = [ - ...updatedState.watchers, - ..._channelState.watchers + ..._updatedStateWatchers, + ..._existingStateWatchers .where((w) => - !updatedState.watchers.any((newWatcher) => newWatcher.id == w.id)) + !_updatedStateWatchers.any((newWatcher) => newWatcher.id == w.id)) .toList(), ]; final newMembers = [ - ...updatedState.members, + ...updatedState.members ?? [], ]; + final _existingStateRead = _channelState.read ?? []; + final _updatedStateRead = updatedState.read ?? []; final newReads = [ - ...updatedState.read, - ..._channelState.read + ..._updatedStateRead, + ..._existingStateRead .where((r) => - !updatedState.read.any((newRead) => newRead.user.id == r.user.id)) + !_updatedStateRead.any((newRead) => newRead.user.id == r.user.id)) .toList(), ]; @@ -2273,9 +2290,9 @@ class ChannelClientState { _pinnedMessagesTimer = Timer.periodic(const Duration(seconds: 30), (_) { final now = DateTime.now(); var expiredMessages = channelState.pinnedMessages - .where((m) => m.pinExpires?.isBefore(now) == true) + ?.where((m) => m.pinExpires?.isBefore(now) == true) .toList(); - if (expiredMessages.isNotEmpty) { + if (expiredMessages != null && expiredMessages.isNotEmpty) { expiredMessages = expiredMessages .map((m) => m.copyWith( pinExpires: null, diff --git a/packages/stream_chat/lib/src/client/client.dart b/packages/stream_chat/lib/src/client/client.dart index 0fca0d2c..6e9ecc33 100644 --- a/packages/stream_chat/lib/src/client/client.dart +++ b/packages/stream_chat/lib/src/client/client.dart @@ -623,7 +623,7 @@ class StreamChatClient { final channels = res.channels; final users = channels - .expand((it) => it.members) + .expand((it) => it.members ?? []) .map((it) => it.user) .toList(growable: false); diff --git a/packages/stream_chat/lib/src/core/models/attachment.dart b/packages/stream_chat/lib/src/core/models/attachment.dart index 325dbe11..72ff5525 100644 --- a/packages/stream_chat/lib/src/core/models/attachment.dart +++ b/packages/stream_chat/lib/src/core/models/attachment.dart @@ -124,8 +124,7 @@ class Attachment extends Equatable { final String? assetUrl; /// Actions from a command - @JsonKey(defaultValue: []) - final List actions; + final List? actions; final Uri? localUri; diff --git a/packages/stream_chat/lib/src/core/models/attachment.g.dart b/packages/stream_chat/lib/src/core/models/attachment.g.dart index 381eb99c..2ebe8c53 100644 --- a/packages/stream_chat/lib/src/core/models/attachment.g.dart +++ b/packages/stream_chat/lib/src/core/models/attachment.g.dart @@ -26,9 +26,8 @@ Attachment _$AttachmentFromJson(Map json) => Attachment( 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() ?? - [], + ?.map((e) => Action.fromJson(e as Map)) + .toList(), extraData: json['extra_data'] as Map? ?? const {}, file: json['file'] == null ? null @@ -64,7 +63,7 @@ Map _$AttachmentToJson(Attachment instance) { writeNotNull('author_link', instance.authorLink); writeNotNull('author_icon', instance.authorIcon); writeNotNull('asset_url', instance.assetUrl); - val['actions'] = instance.actions.map((e) => e.toJson()).toList(); + writeNotNull('actions', instance.actions?.map((e) => e.toJson()).toList()); writeNotNull('file', instance.file?.toJson()); val['upload_state'] = instance.uploadState.toJson(); val['extra_data'] = instance.extraData; diff --git a/packages/stream_chat/lib/src/core/models/channel_state.dart b/packages/stream_chat/lib/src/core/models/channel_state.dart index 17439dc0..3ba3c5f8 100644 --- a/packages/stream_chat/lib/src/core/models/channel_state.dart +++ b/packages/stream_chat/lib/src/core/models/channel_state.dart @@ -7,42 +7,40 @@ import 'package:stream_chat/src/core/models/user.dart'; part 'channel_state.g.dart'; -const _emptyPinnedMessages = []; - /// The class that contains the information about a channel @JsonSerializable() class ChannelState { /// Constructor used for json serialization ChannelState({ this.channel, - this.messages = const [], - this.members = const [], - this.pinnedMessages = _emptyPinnedMessages, + this.messages, + this.members, + this.pinnedMessages, this.watcherCount, - this.watchers = const [], - this.read = const [], + this.watchers, + this.read, }); /// The channel to which this state belongs final ChannelModel? channel; /// A paginated list of channel messages - final List messages; + final List? messages; /// A paginated list of channel members - final List members; + final List? members; /// A paginated list of pinned messages - final List pinnedMessages; + final List? pinnedMessages; /// The count of users watching the channel final int? watcherCount; /// A paginated list of users watching the channel - final List watchers; + final List? watchers; /// The list of channel reads - final List read; + final List? read; /// Create a new instance from a json static ChannelState fromJson(Map json) => @@ -56,7 +54,7 @@ class ChannelState { ChannelModel? channel, List? messages, List? members, - List pinnedMessages = _emptyPinnedMessages, + List? pinnedMessages, int? watcherCount, List? watchers, List? read, @@ -65,11 +63,7 @@ class ChannelState { channel: channel ?? this.channel, messages: messages ?? this.messages, members: members ?? this.members, - // Hack to avoid using the default value in case nothing is provided. - // FIXME: Use non-nullable by default instead of empty list. - pinnedMessages: pinnedMessages == _emptyPinnedMessages - ? this.pinnedMessages - : pinnedMessages, + pinnedMessages: pinnedMessages ?? this.pinnedMessages, watcherCount: watcherCount ?? this.watcherCount, watchers: watchers ?? this.watchers, read: read ?? this.read, diff --git a/packages/stream_chat/lib/src/core/models/channel_state.g.dart b/packages/stream_chat/lib/src/core/models/channel_state.g.dart index 5100932c..afec76c4 100644 --- a/packages/stream_chat/lib/src/core/models/channel_state.g.dart +++ b/packages/stream_chat/lib/src/core/models/channel_state.g.dart @@ -11,36 +11,31 @@ ChannelState _$ChannelStateFromJson(Map json) => ChannelState( ? null : ChannelModel.fromJson(json['channel'] as Map), messages: (json['messages'] as List?) - ?.map((e) => Message.fromJson(e as Map)) - .toList() ?? - const [], + ?.map((e) => Message.fromJson(e as Map)) + .toList(), members: (json['members'] as List?) - ?.map((e) => Member.fromJson(e as Map)) - .toList() ?? - const [], + ?.map((e) => Member.fromJson(e as Map)) + .toList(), pinnedMessages: (json['pinned_messages'] as List?) - ?.map((e) => Message.fromJson(e as Map)) - .toList() ?? - _emptyPinnedMessages, + ?.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() ?? - const [], + ?.map((e) => User.fromJson(e as Map)) + .toList(), read: (json['read'] as List?) - ?.map((e) => Read.fromJson(e as Map)) - .toList() ?? - const [], + ?.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/core/models/message.g.dart b/packages/stream_chat/lib/src/core/models/message.g.dart index b3b58c58..e2ff89b4 100644 --- a/packages/stream_chat/lib/src/core/models/message.g.dart +++ b/packages/stream_chat/lib/src/core/models/message.g.dart @@ -49,6 +49,9 @@ Message _$MessageFromJson(Map json) => Message( updatedAt: json['updated_at'] == null ? null : DateTime.parse(json['updated_at'] as String), + deletedAt: json['deleted_at'] == null + ? null + : DateTime.parse(json['deleted_at'] as String), user: json['user'] == null ? null : User.fromJson(json['user'] as Map), @@ -63,9 +66,6 @@ Message _$MessageFromJson(Map json) => Message( ? null : User.fromJson(json['pinned_by'] as Map), extraData: json['extra_data'] as Map? ?? const {}, - deletedAt: json['deleted_at'] == null - ? null - : DateTime.parse(json['deleted_at'] as String), i18n: (json['i18n'] as Map?)?.map( (k, e) => MapEntry(k, e as String), ), @@ -99,6 +99,7 @@ Map _$MessageToJson(Message instance) { val['silent'] = instance.silent; writeNotNull('shadowed', readonly(instance.shadowed)); writeNotNull('command', readonly(instance.command)); + writeNotNull('deleted_at', readonly(instance.deletedAt)); writeNotNull('created_at', readonly(instance.createdAt)); writeNotNull('updated_at', readonly(instance.updatedAt)); writeNotNull('user', readonly(instance.user)); @@ -107,7 +108,6 @@ Map _$MessageToJson(Message instance) { val['pin_expires'] = instance.pinExpires?.toIso8601String(); val['pinned_by'] = readonly(instance.pinnedBy); val['extra_data'] = instance.extraData; - writeNotNull('deleted_at', readonly(instance.deletedAt)); writeNotNull('i18n', instance.i18n); return val; } 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 2dfb7cd7..b1769807 100644 --- a/packages/stream_chat/lib/src/db/chat_persistence_client.dart +++ b/packages/stream_chat/lib/src/db/chat_persistence_client.dart @@ -44,10 +44,10 @@ abstract class ChatPersistenceClient { Future getChannelByCid(String cid); /// Get stored channel [Member]s by providing channel [cid] - Future> getMembersByCid(String cid); + Future?> getMembersByCid(String cid); /// Get stored channel [Read]s by providing channel [cid] - Future> getReadsByCid(String cid); + Future?> getReadsByCid(String cid); /// Get stored [Message]s by providing channel [cid] /// @@ -78,15 +78,11 @@ abstract class ChatPersistenceClient { getPinnedMessagesByCid(cid, messagePagination: pinnedMessagePagination), ]); return ChannelState( - // ignore: cast_nullable_to_non_nullable - members: data[0] as List, - // ignore: cast_nullable_to_non_nullable - read: data[1] as List, + members: data[0] as List?, + read: data[1] as List?, channel: data[2] as ChannelModel?, - // ignore: cast_nullable_to_non_nullable - messages: data[3] as List, - // ignore: cast_nullable_to_non_nullable - pinnedMessages: data[4] as List, + messages: data[3] as List?, + pinnedMessages: data[4] as List?, ); } @@ -146,7 +142,7 @@ abstract class ChatPersistenceClient { bulkUpdateMessages({cid: messages}); /// Bulk updates the message data of multiple channels. - Future bulkUpdateMessages(Map> messages); + Future bulkUpdateMessages(Map?> messages); /// Updates the pinned message data of a particular channel [cid] with /// the new [messages] data @@ -154,7 +150,7 @@ abstract class ChatPersistenceClient { bulkUpdatePinnedMessages({cid: messages}); /// Bulk updates the message data of multiple channels. - Future bulkUpdatePinnedMessages(Map> messages); + Future bulkUpdatePinnedMessages(Map?> messages); /// Returns all the threads by parent message of a particular channel by /// providing channel [cid] @@ -169,7 +165,7 @@ abstract class ChatPersistenceClient { bulkUpdateMembers({cid: members}); /// Bulk updates the members data of multiple channels. - Future bulkUpdateMembers(Map> members); + Future bulkUpdateMembers(Map?> members); /// Updates the read data of a particular channel [cid] with /// the new [reads] data @@ -177,7 +173,7 @@ abstract class ChatPersistenceClient { bulkUpdateReads({cid: reads}); /// Bulk updates the read data of multiple channels. - Future bulkUpdateReads(Map> reads); + Future bulkUpdateReads(Map?> reads); /// Updates the users data with the new [users] data Future updateUsers(List users); @@ -230,10 +226,10 @@ abstract class ChatPersistenceClient { final membersToDelete = []; final channels = []; - final channelWithMessages = >{}; - final channelWithPinnedMessages = >{}; - final channelWithReads = >{}; - final channelWithMembers = >{}; + final channelWithMessages = ?>{}; + final channelWithPinnedMessages = ?>{}; + final channelWithReads = ?>{}; + final channelWithMembers = ?>{}; final users = []; final reactions = []; @@ -252,8 +248,9 @@ abstract class ChatPersistenceClient { // Preparing deletion data membersToDelete.add(cid); - reactionsToDelete.addAll(state.messages.map((it) => it.id)); - pinnedReactionsToDelete.addAll(state.pinnedMessages.map((it) => it.id)); + reactionsToDelete.addAll(state.messages?.map((it) => it.id) ?? []); + pinnedReactionsToDelete + .addAll(state.pinnedMessages?.map((it) => it.id) ?? []); // preparing addition data channelWithReads[cid] = reads; @@ -261,14 +258,14 @@ abstract class ChatPersistenceClient { channelWithMessages[cid] = messages; channelWithPinnedMessages[cid] = pinnedMessages; - reactions.addAll(messages.expand(_expandReactions)); - pinnedReactions.addAll(pinnedMessages.expand(_expandReactions)); + reactions.addAll(messages?.expand(_expandReactions) ?? []); + pinnedReactions.addAll(pinnedMessages?.expand(_expandReactions) ?? []); users.addAll([ channel.createdBy, - ...messages.map((it) => it.user), - ...reads.map((it) => it.user), - ...members.map((it) => it.user), + ...messages?.map((it) => it.user) ?? [], + ...reads?.map((it) => it.user) ?? [], + ...members?.map((it) => it.user) ?? [], ...reactions.map((it) => it.user), ...pinnedReactions.map((it) => it.user), ].withNullifyer); diff --git a/packages/stream_chat/test/src/core/api/channel_api_test.dart b/packages/stream_chat/test/src/core/api/channel_api_test.dart index a7856c92..bd0403de 100644 --- a/packages/stream_chat/test/src/core/api/channel_api_test.dart +++ b/packages/stream_chat/test/src/core/api/channel_api_test.dart @@ -105,11 +105,11 @@ void main() { ); expect(res, isNotNull); - expect(res.messages.length, channelState.messages.length); - expect(res.pinnedMessages.length, channelState.pinnedMessages.length); - expect(res.members.length, channelState.members.length); - expect(res.read.length, channelState.read.length); - expect(res.watchers.length, channelState.watchers.length); + expect(res.messages?.length, channelState.messages?.length); + expect(res.pinnedMessages?.length, channelState.pinnedMessages?.length); + expect(res.members?.length, channelState.members?.length); + expect(res.read?.length, channelState.read?.length); + expect(res.watchers?.length, channelState.watchers?.length); expect(res.watcherCount, channelState.watcherCount); verify(() => client.post(path, data: any(named: 'data'))).called(1); diff --git a/packages/stream_chat/test/src/core/models/attachment_test.dart b/packages/stream_chat/test/src/core/models/attachment_test.dart index 4e08f7f2..edcc0da6 100644 --- a/packages/stream_chat/test/src/core/models/attachment_test.dart +++ b/packages/stream_chat/test/src/core/models/attachment_test.dart @@ -19,8 +19,10 @@ void main() { attachment.thumbUrl, 'https://media0.giphy.com/media/3o7TKnCdBx5cMg0qti/giphy.gif', ); + expect(attachment.actions, isNotNull); + expect(attachment.actions, isNotEmpty); expect(attachment.actions, hasLength(3)); - expect(attachment.actions[0], isA()); + expect(attachment.actions![0], isA()); }); test('should serialize to json correctly', () { diff --git a/packages/stream_chat/test/src/core/models/channel_state_test.dart b/packages/stream_chat/test/src/core/models/channel_state_test.dart index 58f810a4..65a589b8 100644 --- a/packages/stream_chat/test/src/core/models/channel_state_test.dart +++ b/packages/stream_chat/test/src/core/models/channel_state_test.dart @@ -30,14 +30,16 @@ void main() { channelState.channel?.extraData['image'], 'https://cdn.chrisshort.net/testing-certificate-chains-in-go/GOPHER_MIC_DROP.png', ); + expect(channelState.messages, isNotNull); + expect(channelState.messages, isNotEmpty); expect(channelState.messages, hasLength(25)); - expect(channelState.messages[0], isA()); - expect(channelState.messages[0], isNotNull); + expect(channelState.messages![0], isA()); + expect(channelState.messages![0], isNotNull); expect( - channelState.messages[0].createdAt, + channelState.messages![0].createdAt, DateTime.parse('2020-01-29T03:23:02.843948Z'), ); - expect(channelState.messages[0].user, isA()); + expect(channelState.messages![0].user, isA()); expect(channelState.watcherCount, 5); }); diff --git a/packages/stream_chat/test/src/db/chat_persistence_client_test.dart b/packages/stream_chat/test/src/db/chat_persistence_client_test.dart index 7c49cc7d..121d279d 100644 --- a/packages/stream_chat/test/src/db/chat_persistence_client_test.dart +++ b/packages/stream_chat/test/src/db/chat_persistence_client_test.dart @@ -117,19 +117,20 @@ class TestPersistenceClient extends ChatPersistenceClient { Future updateUsers(List users) => Future.value(); @override - Future bulkUpdateMembers(Map> members) => + Future bulkUpdateMembers(Map?> members) => Future.value(); @override - Future bulkUpdateMessages(Map> messages) => + Future bulkUpdateMessages(Map?> messages) => Future.value(); @override - Future bulkUpdatePinnedMessages(Map> messages) => + Future bulkUpdatePinnedMessages(Map?> messages) => Future.value(); @override - Future bulkUpdateReads(Map> reads) => Future.value(); + Future bulkUpdateReads(Map?> reads) => + Future.value(); } void main() { diff --git a/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart b/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart index 6381c345..dc354432 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart @@ -45,7 +45,7 @@ class StreamGiphyAttachment extends StreamAttachmentWidget { if (imageUrl == null) { return const AttachmentError(); } - if (attachment.actions.isNotEmpty) { + if (attachment.actions != null && attachment.actions!.isNotEmpty) { return _buildSendingAttachment(context, imageUrl); } return _buildSentAttachment(context, imageUrl); diff --git a/packages/stream_chat_flutter_core/lib/src/stream_channel.dart b/packages/stream_chat_flutter_core/lib/src/stream_channel.dart index bcfb613e..47822791 100644 --- a/packages/stream_chat_flutter_core/lib/src/stream_channel.dart +++ b/packages/stream_chat_flutter_core/lib/src/stream_channel.dart @@ -106,7 +106,9 @@ class StreamChannelState extends State { limit: limit, preferOffline: preferOffline, ); - if (state.messages.isEmpty || state.messages.length < limit) { + if (state.messages == null || + state.messages!.isEmpty || + state.messages!.length < limit) { _topPaginationEnded = true; } _queryTopMessagesController.safeAdd(false); @@ -137,7 +139,9 @@ class StreamChannelState extends State { limit: limit, preferOffline: preferOffline, ); - if (state.messages.isEmpty || state.messages.length < limit) { + if (state.messages == null || + state.messages!.isEmpty || + state.messages!.length < limit) { _bottomPaginationEnded = true; } _queryBottomMessagesController.safeAdd(false); @@ -299,7 +303,9 @@ class StreamChannelState extends State { ), preferOffline: preferOffline, ); - if (state.messages.isEmpty || state.messages.length < limit) { + if (state.messages == null || + state.messages!.isEmpty || + state.messages!.length < limit) { channel.state?.isUpToDate = true; } return state; diff --git a/packages/stream_chat_persistence/example/lib/main.dart b/packages/stream_chat_persistence/example/lib/main.dart index d420b7f5..049868fa 100644 --- a/packages/stream_chat_persistence/example/lib/main.dart +++ b/packages/stream_chat_persistence/example/lib/main.dart @@ -98,8 +98,9 @@ class HomeScreen extends StatelessWidget { AsyncSnapshot snapshot, ) { if (snapshot.hasData && snapshot.data != null) { + final _messages = snapshot.data!.messages ?? []; return MessageView( - messages: snapshot.data!.messages.reversed.toList(), + messages: _messages.reversed.toList(), channel: channel, ); } else if (snapshot.hasError) { diff --git a/packages/stream_chat_persistence/lib/src/dao/member_dao.dart b/packages/stream_chat_persistence/lib/src/dao/member_dao.dart index 3a604ca1..9f81879c 100644 --- a/packages/stream_chat_persistence/lib/src/dao/member_dao.dart +++ b/packages/stream_chat_persistence/lib/src/dao/member_dao.dart @@ -34,14 +34,20 @@ class MemberDao extends DatabaseAccessor bulkUpdateMembers({cid: memberList}); /// Bulk updates the members data of multiple channels - Future bulkUpdateMembers(Map> channelWithMembers) { + Future bulkUpdateMembers( + Map?> channelWithMembers, + ) { final entities = channelWithMembers.entries - .map((entry) => entry.value.map( + .map((entry) => + (entry.value?.map( (member) => member.toEntity(cid: entry.key), - )) + )) ?? + []) .expand((it) => it) .toList(growable: false); - return batch((batch) => batch.insertAllOnConflictUpdate(members, entities)); + return batch( + (batch) => batch.insertAllOnConflictUpdate(members, entities), + ); } /// Deletes all the members whose [Members.channelCid] is present in [cids] diff --git a/packages/stream_chat_persistence/lib/src/dao/message_dao.dart b/packages/stream_chat_persistence/lib/src/dao/message_dao.dart index 31ca74ee..ccaefced 100644 --- a/packages/stream_chat_persistence/lib/src/dao/message_dao.dart +++ b/packages/stream_chat_persistence/lib/src/dao/message_dao.dart @@ -183,12 +183,14 @@ class MessageDao extends DatabaseAccessor /// Bulk updates the message data of multiple channels Future bulkUpdateMessages( - Map> channelWithMessages, + Map?> channelWithMessages, ) { final entities = channelWithMessages.entries - .map((entry) => entry.value.map( + .map((entry) => + entry.value?.map( (message) => message.toEntity(cid: entry.key), - )) + ) ?? + []) .expand((it) => it) .toList(growable: false); return batch( diff --git a/packages/stream_chat_persistence/lib/src/dao/pinned_message_dao.dart b/packages/stream_chat_persistence/lib/src/dao/pinned_message_dao.dart index 8712dea5..8be2399b 100644 --- a/packages/stream_chat_persistence/lib/src/dao/pinned_message_dao.dart +++ b/packages/stream_chat_persistence/lib/src/dao/pinned_message_dao.dart @@ -183,12 +183,14 @@ class PinnedMessageDao extends DatabaseAccessor /// Bulk updates the message data of multiple channels Future bulkUpdateMessages( - Map> channelWithMessages, + Map?> channelWithMessages, ) { final entities = channelWithMessages.entries - .map((entry) => entry.value.map( + .map((entry) => + entry.value?.map( (message) => message.toPinnedEntity(cid: entry.key), - )) + ) ?? + []) .expand((it) => it) .toList(growable: false); return batch( diff --git a/packages/stream_chat_persistence/lib/src/dao/read_dao.dart b/packages/stream_chat_persistence/lib/src/dao/read_dao.dart index 0a578d69..9e46f818 100644 --- a/packages/stream_chat_persistence/lib/src/dao/read_dao.dart +++ b/packages/stream_chat_persistence/lib/src/dao/read_dao.dart @@ -33,11 +33,13 @@ class ReadDao extends DatabaseAccessor with _$ReadDaoMixin { bulkUpdateReads({cid: readList}); /// Bulk updates the reads data of multiple channels - Future bulkUpdateReads(Map> channelWithReads) { + Future bulkUpdateReads(Map?> channelWithReads) { final entities = channelWithReads.entries - .map((entry) => entry.value.map( + .map((entry) => + entry.value?.map( (read) => read.toEntity(cid: entry.key), - )) + ) ?? + []) .expand((it) => it) .toList(growable: false); return batch((batch) => batch.insertAllOnConflictUpdate(reads, entities)); diff --git a/packages/stream_chat_persistence/lib/src/db/drift_chat_database.dart b/packages/stream_chat_persistence/lib/src/db/drift_chat_database.dart index 8561eed4..9b73312d 100644 --- a/packages/stream_chat_persistence/lib/src/db/drift_chat_database.dart +++ b/packages/stream_chat_persistence/lib/src/db/drift_chat_database.dart @@ -56,7 +56,7 @@ class DriftChatDatabase extends _$DriftChatDatabase { // you should bump this number whenever you change or add a table definition. @override - int get schemaVersion => 6; + int get schemaVersion => 7; @override MigrationStrategy get migration => MigrationStrategy( diff --git a/packages/stream_chat_persistence/lib/src/stream_chat_persistence_client.dart b/packages/stream_chat_persistence/lib/src/stream_chat_persistence_client.dart index 2dc61f18..5b180e30 100644 --- a/packages/stream_chat_persistence/lib/src/stream_chat_persistence_client.dart +++ b/packages/stream_chat_persistence/lib/src/stream_chat_persistence_client.dart @@ -295,21 +295,21 @@ class StreamChatPersistenceClient extends ChatPersistenceClient { } @override - Future bulkUpdateMembers(Map> members) { + Future bulkUpdateMembers(Map?> members) { assert(_debugIsConnected, ''); _logger.info('bulkUpdateMembers'); return _readProtected(() => db!.memberDao.bulkUpdateMembers(members)); } @override - Future bulkUpdateMessages(Map> messages) { + Future bulkUpdateMessages(Map?> messages) { assert(_debugIsConnected, ''); _logger.info('bulkUpdateMessages'); return _readProtected(() => db!.messageDao.bulkUpdateMessages(messages)); } @override - Future bulkUpdatePinnedMessages(Map> messages) { + Future bulkUpdatePinnedMessages(Map?> messages) { assert(_debugIsConnected, ''); _logger.info('bulkUpdatePinnedMessages'); return _readProtected( @@ -334,7 +334,7 @@ class StreamChatPersistenceClient extends ChatPersistenceClient { } @override - Future bulkUpdateReads(Map> reads) { + Future bulkUpdateReads(Map?> reads) { assert(_debugIsConnected, ''); _logger.info('bulkUpdateReads'); return _readProtected(() => db!.readDao.bulkUpdateReads(reads)); diff --git a/packages/stream_chat_persistence/test/src/mapper/channel_mapper_test.dart b/packages/stream_chat_persistence/test/src/mapper/channel_mapper_test.dart index e9718af9..d89687d3 100644 --- a/packages/stream_chat_persistence/test/src/mapper/channel_mapper_test.dart +++ b/packages/stream_chat_persistence/test/src/mapper/channel_mapper_test.dart @@ -61,10 +61,10 @@ void main() { ); expect(channelState, isA()); - expect(channelState.members.length, members.length); - expect(channelState.read.length, reads.length); - expect(channelState.messages.length, messages.length); - expect(channelState.pinnedMessages.length, messages.length); + expect(channelState.members?.length, members.length); + expect(channelState.read?.length, reads.length); + expect(channelState.messages?.length, messages.length); + expect(channelState.pinnedMessages?.length, messages.length); final channelModel = channelState.channel!; expect(channelModel.id, entity.id); diff --git a/packages/stream_chat_persistence/test/stream_chat_persistence_client_test.dart b/packages/stream_chat_persistence/test/stream_chat_persistence_client_test.dart index 4900aab2..8d7ab8be 100644 --- a/packages/stream_chat_persistence/test/stream_chat_persistence_client_test.dart +++ b/packages/stream_chat_persistence/test/stream_chat_persistence_client_test.dart @@ -219,10 +219,10 @@ void main() { .thenAnswer((_) async => messages); final fetchedChannelState = await client.getChannelStateByCid(cid); - expect(fetchedChannelState.messages.length, messages.length); - expect(fetchedChannelState.pinnedMessages.length, messages.length); - expect(fetchedChannelState.members.length, members.length); - expect(fetchedChannelState.read.length, reads.length); + expect(fetchedChannelState.messages?.length, messages.length); + expect(fetchedChannelState.pinnedMessages?.length, messages.length); + expect(fetchedChannelState.members?.length, members.length); + expect(fetchedChannelState.read?.length, reads.length); expect(fetchedChannelState.channel!.cid, channel.cid); verify(() => mockDatabase.memberDao.getMembersByCid(cid)).called(1); @@ -277,10 +277,10 @@ void main() { for (var i = 0; i < fetchedChannelStates.length; i++) { final original = channelStates[i]; final fetched = fetchedChannelStates[i]; - expect(fetched.members.length, original.members.length); - expect(fetched.messages.length, original.messages.length); - expect(fetched.pinnedMessages.length, original.pinnedMessages.length); - expect(fetched.read.length, original.read.length); + expect(fetched.members?.length, original.members?.length); + expect(fetched.messages?.length, original.messages?.length); + expect(fetched.pinnedMessages?.length, original.pinnedMessages?.length); + expect(fetched.read?.length, original.read?.length); expect(fetched.channel!.cid, original.channel!.cid); }