From 7e0997838e1bc06ee45ae83145fdea26779bd73a Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Mon, 6 Sep 2021 19:21:28 +0530 Subject: [PATCH] fix: `updateChannelStates` invocation sequence as per foreign keys relations. Signed-off-by: xsahil03x --- .../lib/src/db/chat_persistence_client.dart | 183 +++++++++--------- .../src/db/chat_persistence_client_test.dart | 28 +-- .../lib/src/dao/member_dao.dart | 20 +- .../lib/src/dao/message_dao.dart | 25 ++- .../lib/src/dao/pinned_message_dao.dart | 25 ++- .../lib/src/dao/read_dao.dart | 19 +- .../lib/src/db/moor_chat_database.dart | 2 +- .../lib/src/db/moor_chat_database.g.dart | 80 ++++---- .../lib/src/entity/members.dart | 5 +- .../lib/src/entity/messages.dart | 4 +- .../lib/src/mapper/message_mapper.dart | 2 +- .../lib/src/mapper/pinned_message_mapper.dart | 3 +- .../src/stream_chat_persistence_client.dart | 24 +-- .../stream_chat_persistence_client_test.dart | 24 ++- 14 files changed, 240 insertions(+), 204 deletions(-) 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 bf2ef3a8..aa0ac245 100644 --- a/packages/stream_chat/lib/src/db/chat_persistence_client.dart +++ b/packages/stream_chat/lib/src/db/chat_persistence_client.dart @@ -143,11 +143,19 @@ abstract class ChatPersistenceClient { /// Updates the message data of a particular channel [cid] with /// the new [messages] data - Future updateMessages(String cid, List messages); + Future updateMessages(String cid, List messages) => + bulkUpdateMessages({cid: messages}); + + /// Bulk updates the message data of multiple channels. + Future bulkUpdateMessages(Map> messages); /// Updates the pinned message data of a particular channel [cid] with /// the new [messages] data - Future updatePinnedMessages(String cid, List messages); + Future updatePinnedMessages(String cid, List messages) => + bulkUpdatePinnedMessages({cid: messages}); + + /// Bulk updates the message data of multiple channels. + Future bulkUpdatePinnedMessages(Map> messages); /// Returns all the threads by parent message of a particular channel by /// providing channel [cid] @@ -158,11 +166,19 @@ abstract class ChatPersistenceClient { /// Updates all the members of a particular channle [cid] /// with the new [members] data - Future updateMembers(String cid, List members); + Future updateMembers(String cid, List members) => + bulkUpdateMembers({cid: members}); + + /// Bulk updates the members data of multiple channels. + Future bulkUpdateMembers(Map> members); /// Updates the read data of a particular channel [cid] with /// the new [reads] data - Future updateReads(String cid, List reads); + Future updateReads(String cid, List reads) => + bulkUpdateReads({cid: reads}); + + /// Bulk updates the read data of multiple channels. + Future bulkUpdateReads(Map> reads); /// Updates the users data with the new [users] data Future updateUsers(List users); @@ -188,104 +204,91 @@ abstract class ChatPersistenceClient { /// Update list of channel states Future updateChannelStates(List channelStates) async { - final deleteReactions = deleteReactionsByMessageId(channelStates - .expand((it) => it.messages) - .map((m) => m.id) - .toList(growable: false)); + final reactionsToDelete = []; + final pinnedReactionsToDelete = []; + final membersToDelete = []; - final deletePinnedMessageReactions = - deletePinnedMessageReactionsByMessageId(channelStates - .expand((it) => it.pinnedMessages) - .map((m) => m.id) - .toList(growable: false)); + final channels = []; + final channelWithMessages = >{}; + final channelWithPinnedMessages = >{}; + final channelWithReads = >{}; + final channelWithMembers = >{}; - final cleanedChannelStates = - channelStates.where((it) => it.channel != null); + final users = []; + final reactions = []; + final pinnedReactions = []; - final deleteMembers = deleteMembersByCids( - cleanedChannelStates.map((it) => it.channel!.cid).toList(growable: false), - ); + for (final state in channelStates) { + final channel = state.channel; + if (channel != null) { + channels.add(channel); + final cid = channel.cid; + final reads = state.read; + final members = state.members; + final messages = state.messages; + final pinnedMessages = state.pinnedMessages; + + // Preparing deletion data + membersToDelete.add(cid); + reactionsToDelete.addAll(state.messages.map((it) => it.id)); + pinnedReactionsToDelete.addAll(state.pinnedMessages.map((it) => it.id)); + + // preparing addition data + channelWithReads[cid] = reads; + channelWithMembers[cid] = members; + channelWithMessages[cid] = messages; + channelWithPinnedMessages[cid] = pinnedMessages; + + List expandReactions(Message message) { + final own = message.ownReactions; + final latest = message.latestReactions; + return [ + if (own != null) ...own.where((r) => r.userId != null), + if (latest != null) ...latest.where((r) => r.userId != null), + ]; + } + + reactions.addAll(messages.expand(expandReactions)); + pinnedReactions.addAll(pinnedMessages.expand(expandReactions)); + + users.addAll([ + channel.createdBy, + ...reads.map((it) => it.user), + ...members.map((it) => it.user), + ...reactions.map((it) => it.user), + ...pinnedReactions.map((it) => it.user), + ].withNullifyer); + } + } + + // Removing old members and reactions data as they may have + // changes over the time. await Future.wait([ - deleteReactions, - deletePinnedMessageReactions, - deleteMembers, + deleteMembersByCids(membersToDelete), + deleteReactionsByMessageId(reactionsToDelete), + deletePinnedMessageReactionsByMessageId(pinnedReactionsToDelete), ]); - final channels = cleanedChannelStates.map((it) => it.channel).withNullifyer; - - final reactions = cleanedChannelStates - .expand((it) => it.messages) - .expand((it) => [ - if (it.ownReactions != null) - ...it.ownReactions!.where((r) => r.userId != null), - if (it.latestReactions != null) - ...it.latestReactions!.where((r) => r.userId != null), - ]) - .withNullifyer; - - final pinnedMessageReactions = cleanedChannelStates - .expand((it) => it.pinnedMessages) - .expand((it) => [ - if (it.ownReactions != null) - ...it.ownReactions!.where((r) => r.userId != null), - if (it.latestReactions != null) - ...it.latestReactions!.where((r) => r.userId != null), - ]) - .withNullifyer; - - final users = cleanedChannelStates - .map((cs) => [ - cs.channel?.createdBy, - ...cs.messages - .map((m) => [ - m.user, - if (m.latestReactions != null) - ...m.latestReactions!.map((r) => r.user), - if (m.ownReactions != null) - ...m.ownReactions!.map((r) => r.user), - ]) - .expand((v) => v), - ...cs.read.map((r) => r.user), - ...cs.members.map((m) => m.user), - ]) - .expand((it) => it) - .withNullifyer; - - 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 = cleanedChannelStates.map((it) { - final cid = it.channel!.cid; - final messages = it.pinnedMessages; - return updatePinnedMessages(cid, messages.toList(growable: false)); - }).toList(growable: false); - - 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 = cleanedChannelStates.map((it) { - final cid = it.channel!.cid; - final members = it.members; - return updateMembers(cid, members.toList(growable: false)); - }).toList(growable: false); - + // Updating first as does not depend on any other table. await Future.wait([ - ...updateMessagesFuture, - ...updatePinnedMessagesFuture, - ...updateReadsFuture, - ...updateMembersFuture, updateUsers(users.toList(growable: false)), updateChannels(channels.toList(growable: false)), + ]); + + // All has a foreign key relation with channels table. + await Future.wait([ + bulkUpdateReads(channelWithReads), + bulkUpdateMembers(channelWithMembers), + bulkUpdateMessages(channelWithMessages), + bulkUpdatePinnedMessages(channelWithPinnedMessages), + ]); + + // Both has a foreign key relation with messages, pinnedMessages table. + await Future.wait([ updateReactions(reactions.toList(growable: false)), updatePinnedMessageReactions( - pinnedMessageReactions.toList(growable: false), + pinnedReactions.toList(growable: false), ), ]); } 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 d7de4745..897bbcb0 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 @@ -106,18 +106,6 @@ class TestPersistenceClient extends ChatPersistenceClient { Future updateLastSyncAt(DateTime lastSyncAt) => throw UnimplementedError(); - @override - Future updateMembers(String cid, List members) => - Future.value(); - - @override - Future updateMessages(String cid, List messages) => - Future.value(); - - @override - Future updatePinnedMessages(String cid, List messages) => - Future.value(); - @override Future updateReactions(List reactions) => Future.value(); @@ -126,10 +114,22 @@ class TestPersistenceClient extends ChatPersistenceClient { Future.value(); @override - Future updateReads(String cid, List reads) => Future.value(); + Future updateUsers(List users) => Future.value(); @override - Future updateUsers(List users) => Future.value(); + Future bulkUpdateMembers(Map> members) => + Future.value(); + + @override + Future bulkUpdateMessages(Map> messages) => + Future.value(); + + @override + Future bulkUpdatePinnedMessages(Map> messages) => + Future.value(); + + @override + Future bulkUpdateReads(Map> reads) => Future.value(); } void main() { 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 1f39904c..e471b1b5 100644 --- a/packages/stream_chat_persistence/lib/src/dao/member_dao.dart +++ b/packages/stream_chat_persistence/lib/src/dao/member_dao.dart @@ -30,13 +30,19 @@ class MemberDao extends DatabaseAccessor }).get(); /// Updates all the members using the new [memberList] data - Future updateMembers(String cid, List memberList) async => - batch( - (it) => it.insertAllOnConflictUpdate( - members, - memberList.map((m) => m.toEntity(cid: cid)).toList(), - ), - ); + Future updateMembers(String cid, List memberList) => + bulkUpdateMembers({cid: memberList}); + + /// Bulk updates the members data of multiple channels + Future bulkUpdateMembers(Map> channelWithMembers) { + final entities = channelWithMembers.entries + .map((entry) => entry.value.map( + (member) => member.toEntity(cid: entry.key), + )) + .expand((it) => it) + .toList(growable: false); + return batch((batch) => batch.insertAllOnConflictUpdate(members, entities)); + } /// Deletes all the members whose [Members.channelCid] is present in [cids] Future deleteMemberByCids(List cids) async => batch((it) { 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 eb02e8aa..b7d9a5db 100644 --- a/packages/stream_chat_persistence/lib/src/dao/message_dao.dart +++ b/packages/stream_chat_persistence/lib/src/dao/message_dao.dart @@ -169,12 +169,21 @@ class MessageDao extends DatabaseAccessor /// Updates the message data of a particular channel with /// the new [messageList] data - Future updateMessages(String cid, List messageList) => batch( - (batch) { - batch.insertAllOnConflictUpdate( - messages, - messageList.map((it) => it.toEntity(cid: cid)).toList(), - ); - }, - ); + Future updateMessages(String cid, List messageList) => + bulkUpdateMessages({cid: messageList}); + + /// Bulk updates the message data of multiple channels + Future bulkUpdateMessages( + Map> channelWithMessages, + ) { + final entities = channelWithMessages.entries + .map((entry) => entry.value.map( + (message) => message.toEntity(cid: entry.key), + )) + .expand((it) => it) + .toList(growable: false); + return batch( + (batch) => batch.insertAllOnConflictUpdate(messages, entities), + ); + } } 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 065dc40d..9bc0fa64 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 @@ -170,12 +170,21 @@ class PinnedMessageDao extends DatabaseAccessor /// Updates the message data of a particular channel with /// the new [messageList] data - Future updateMessages(String cid, List messageList) => batch( - (batch) { - batch.insertAllOnConflictUpdate( - pinnedMessages, - messageList.map((it) => it.toPinnedEntity(cid: cid)).toList(), - ); - }, - ); + Future updateMessages(String cid, List messageList) => + bulkUpdateMessages({cid: messageList}); + + /// Bulk updates the message data of multiple channels + Future bulkUpdateMessages( + Map> channelWithMessages, + ) { + final entities = channelWithMessages.entries + .map((entry) => entry.value.map( + (message) => message.toPinnedEntity(cid: entry.key), + )) + .expand((it) => it) + .toList(growable: false); + return batch( + (batch) => batch.insertAllOnConflictUpdate(pinnedMessages, entities), + ); + } } 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 007cb3a7..87bfa19b 100644 --- a/packages/stream_chat_persistence/lib/src/dao/read_dao.dart +++ b/packages/stream_chat_persistence/lib/src/dao/read_dao.dart @@ -29,10 +29,17 @@ class ReadDao extends DatabaseAccessor with _$ReadDaoMixin { /// Updates the read data of a particular channel with /// the new [readList] data - Future updateReads(String cid, List readList) => batch( - (it) => it.insertAllOnConflictUpdate( - reads, - readList.map((r) => r.toEntity(cid: cid)).toList(), - ), - ); + Future updateReads(String cid, List readList) => + bulkUpdateReads({cid: readList}); + + /// Bulk updates the reads data of multiple channels + Future bulkUpdateReads(Map> channelWithReads) { + final entities = channelWithReads.entries + .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/moor_chat_database.dart b/packages/stream_chat_persistence/lib/src/db/moor_chat_database.dart index 07f86821..85f7b2de 100644 --- a/packages/stream_chat_persistence/lib/src/db/moor_chat_database.dart +++ b/packages/stream_chat_persistence/lib/src/db/moor_chat_database.dart @@ -58,7 +58,7 @@ class MoorChatDatabase extends _$MoorChatDatabase { @override MigrationStrategy get migration => MigrationStrategy( beforeOpen: (details) async { - await customStatement('PRAGMA foreign_keys = ON;'); + await customStatement('PRAGMA foreign_keys = ON'); }, onUpgrade: (openingDetails, before, after) async { if (before != after) { diff --git a/packages/stream_chat_persistence/lib/src/db/moor_chat_database.g.dart b/packages/stream_chat_persistence/lib/src/db/moor_chat_database.g.dart index c0991f3e..7360f9ab 100644 --- a/packages/stream_chat_persistence/lib/src/db/moor_chat_database.g.dart +++ b/packages/stream_chat_persistence/lib/src/db/moor_chat_database.g.dart @@ -645,7 +645,7 @@ class MessageEntity extends DataClass implements Insertable { final String? pinnedByUserId; /// The channel cid of which this message is part of - final String? channelCid; + final String channelCid; /// A Map of [messageText] translations. final Map? i18n; @@ -675,7 +675,7 @@ class MessageEntity extends DataClass implements Insertable { this.pinnedAt, this.pinExpires, this.pinnedByUserId, - this.channelCid, + required this.channelCid, this.i18n, this.extraData}); factory MessageEntity.fromData( @@ -728,7 +728,7 @@ class MessageEntity extends DataClass implements Insertable { pinnedByUserId: const StringType() .mapFromDatabaseResponse(data['${effectivePrefix}pinned_by_user_id']), channelCid: const StringType() - .mapFromDatabaseResponse(data['${effectivePrefix}channel_cid']), + .mapFromDatabaseResponse(data['${effectivePrefix}channel_cid'])!, i18n: $MessagesTable.$converter5.mapToDart(const StringType() .mapFromDatabaseResponse(data['${effectivePrefix}i18n'])), extraData: $MessagesTable.$converter6.mapToDart(const StringType() @@ -800,9 +800,7 @@ class MessageEntity extends DataClass implements Insertable { if (!nullToAbsent || pinnedByUserId != null) { map['pinned_by_user_id'] = Variable(pinnedByUserId); } - if (!nullToAbsent || channelCid != null) { - map['channel_cid'] = Variable(channelCid); - } + map['channel_cid'] = Variable(channelCid); if (!nullToAbsent || i18n != null) { final converter = $MessagesTable.$converter5; map['i18n'] = Variable(converter.mapToSql(i18n)); @@ -842,7 +840,7 @@ class MessageEntity extends DataClass implements Insertable { pinnedAt: serializer.fromJson(json['pinnedAt']), pinExpires: serializer.fromJson(json['pinExpires']), pinnedByUserId: serializer.fromJson(json['pinnedByUserId']), - channelCid: serializer.fromJson(json['channelCid']), + channelCid: serializer.fromJson(json['channelCid']), i18n: serializer.fromJson?>(json['i18n']), extraData: serializer.fromJson?>(json['extraData']), ); @@ -873,7 +871,7 @@ class MessageEntity extends DataClass implements Insertable { 'pinnedAt': serializer.toJson(pinnedAt), 'pinExpires': serializer.toJson(pinExpires), 'pinnedByUserId': serializer.toJson(pinnedByUserId), - 'channelCid': serializer.toJson(channelCid), + 'channelCid': serializer.toJson(channelCid), 'i18n': serializer.toJson?>(i18n), 'extraData': serializer.toJson?>(extraData), }; @@ -902,7 +900,7 @@ class MessageEntity extends DataClass implements Insertable { Value pinnedAt = const Value.absent(), Value pinExpires = const Value.absent(), Value pinnedByUserId = const Value.absent(), - Value channelCid = const Value.absent(), + String? channelCid, Value?> i18n = const Value.absent(), Value?> extraData = const Value.absent()}) => MessageEntity( @@ -934,7 +932,7 @@ class MessageEntity extends DataClass implements Insertable { pinExpires: pinExpires.present ? pinExpires.value : this.pinExpires, pinnedByUserId: pinnedByUserId.present ? pinnedByUserId.value : this.pinnedByUserId, - channelCid: channelCid.present ? channelCid.value : this.channelCid, + channelCid: channelCid ?? this.channelCid, i18n: i18n.present ? i18n.value : this.i18n, extraData: extraData.present ? extraData.value : this.extraData, ); @@ -1068,7 +1066,7 @@ class MessagesCompanion extends UpdateCompanion { final Value pinnedAt; final Value pinExpires; final Value pinnedByUserId; - final Value channelCid; + final Value channelCid; final Value?> i18n; final Value?> extraData; const MessagesCompanion({ @@ -1121,12 +1119,13 @@ class MessagesCompanion extends UpdateCompanion { this.pinnedAt = const Value.absent(), this.pinExpires = const Value.absent(), this.pinnedByUserId = const Value.absent(), - this.channelCid = const Value.absent(), + required String channelCid, this.i18n = const Value.absent(), this.extraData = const Value.absent(), }) : id = Value(id), attachments = Value(attachments), - mentionedUsers = Value(mentionedUsers); + mentionedUsers = Value(mentionedUsers), + channelCid = Value(channelCid); static Insertable custom({ Expression? id, Expression? messageText, @@ -1150,7 +1149,7 @@ class MessagesCompanion extends UpdateCompanion { Expression? pinnedAt, Expression? pinExpires, Expression? pinnedByUserId, - Expression? channelCid, + Expression? channelCid, Expression?>? i18n, Expression?>? extraData, }) { @@ -1206,7 +1205,7 @@ class MessagesCompanion extends UpdateCompanion { Value? pinnedAt, Value? pinExpires, Value? pinnedByUserId, - Value? channelCid, + Value? channelCid, Value?>? i18n, Value?>? extraData}) { return MessagesCompanion( @@ -1317,7 +1316,7 @@ class MessagesCompanion extends UpdateCompanion { map['pinned_by_user_id'] = Variable(pinnedByUserId.value); } if (channelCid.present) { - map['channel_cid'] = Variable(channelCid.value); + map['channel_cid'] = Variable(channelCid.value); } if (i18n.present) { final converter = $MessagesTable.$converter5; @@ -1491,11 +1490,10 @@ class $MessagesTable extends Messages typeName: 'TEXT', requiredDuringInsert: false); final VerificationMeta _channelCidMeta = const VerificationMeta('channelCid'); late final GeneratedColumn channelCid = GeneratedColumn( - 'channel_cid', aliasedName, true, + 'channel_cid', aliasedName, false, typeName: 'TEXT', - requiredDuringInsert: false, - $customConstraints: - 'NULLABLE REFERENCES channels(cid) ON DELETE CASCADE'); + requiredDuringInsert: true, + $customConstraints: 'REFERENCES channels(cid) ON DELETE CASCADE'); final VerificationMeta _i18nMeta = const VerificationMeta('i18n'); late final GeneratedColumnWithTypeConverter, String?> i18n = GeneratedColumn('i18n', aliasedName, true, @@ -1634,6 +1632,8 @@ class $MessagesTable extends Messages _channelCidMeta, channelCid.isAcceptableOrUnknown( data['channel_cid']!, _channelCidMeta)); + } else if (isInserting) { + context.missing(_channelCidMeta); } context.handle(_i18nMeta, const VerificationResult.success()); context.handle(_extraDataMeta, const VerificationResult.success()); @@ -1739,7 +1739,7 @@ class PinnedMessageEntity extends DataClass final String? pinnedByUserId; /// The channel cid of which this message is part of - final String? channelCid; + final String channelCid; /// A Map of [messageText] translations. final Map? i18n; @@ -1769,7 +1769,7 @@ class PinnedMessageEntity extends DataClass this.pinnedAt, this.pinExpires, this.pinnedByUserId, - this.channelCid, + required this.channelCid, this.i18n, this.extraData}); factory PinnedMessageEntity.fromData( @@ -1825,7 +1825,7 @@ class PinnedMessageEntity extends DataClass pinnedByUserId: const StringType() .mapFromDatabaseResponse(data['${effectivePrefix}pinned_by_user_id']), channelCid: const StringType() - .mapFromDatabaseResponse(data['${effectivePrefix}channel_cid']), + .mapFromDatabaseResponse(data['${effectivePrefix}channel_cid'])!, i18n: $PinnedMessagesTable.$converter5.mapToDart(const StringType() .mapFromDatabaseResponse(data['${effectivePrefix}i18n'])), extraData: $PinnedMessagesTable.$converter6.mapToDart(const StringType() @@ -1897,9 +1897,7 @@ class PinnedMessageEntity extends DataClass if (!nullToAbsent || pinnedByUserId != null) { map['pinned_by_user_id'] = Variable(pinnedByUserId); } - if (!nullToAbsent || channelCid != null) { - map['channel_cid'] = Variable(channelCid); - } + map['channel_cid'] = Variable(channelCid); if (!nullToAbsent || i18n != null) { final converter = $PinnedMessagesTable.$converter5; map['i18n'] = Variable(converter.mapToSql(i18n)); @@ -1939,7 +1937,7 @@ class PinnedMessageEntity extends DataClass pinnedAt: serializer.fromJson(json['pinnedAt']), pinExpires: serializer.fromJson(json['pinExpires']), pinnedByUserId: serializer.fromJson(json['pinnedByUserId']), - channelCid: serializer.fromJson(json['channelCid']), + channelCid: serializer.fromJson(json['channelCid']), i18n: serializer.fromJson?>(json['i18n']), extraData: serializer.fromJson?>(json['extraData']), ); @@ -1970,7 +1968,7 @@ class PinnedMessageEntity extends DataClass 'pinnedAt': serializer.toJson(pinnedAt), 'pinExpires': serializer.toJson(pinExpires), 'pinnedByUserId': serializer.toJson(pinnedByUserId), - 'channelCid': serializer.toJson(channelCid), + 'channelCid': serializer.toJson(channelCid), 'i18n': serializer.toJson?>(i18n), 'extraData': serializer.toJson?>(extraData), }; @@ -1999,7 +1997,7 @@ class PinnedMessageEntity extends DataClass Value pinnedAt = const Value.absent(), Value pinExpires = const Value.absent(), Value pinnedByUserId = const Value.absent(), - Value channelCid = const Value.absent(), + String? channelCid, Value?> i18n = const Value.absent(), Value?> extraData = const Value.absent()}) => PinnedMessageEntity( @@ -2031,7 +2029,7 @@ class PinnedMessageEntity extends DataClass pinExpires: pinExpires.present ? pinExpires.value : this.pinExpires, pinnedByUserId: pinnedByUserId.present ? pinnedByUserId.value : this.pinnedByUserId, - channelCid: channelCid.present ? channelCid.value : this.channelCid, + channelCid: channelCid ?? this.channelCid, i18n: i18n.present ? i18n.value : this.i18n, extraData: extraData.present ? extraData.value : this.extraData, ); @@ -2165,7 +2163,7 @@ class PinnedMessagesCompanion extends UpdateCompanion { final Value pinnedAt; final Value pinExpires; final Value pinnedByUserId; - final Value channelCid; + final Value channelCid; final Value?> i18n; final Value?> extraData; const PinnedMessagesCompanion({ @@ -2218,12 +2216,13 @@ class PinnedMessagesCompanion extends UpdateCompanion { this.pinnedAt = const Value.absent(), this.pinExpires = const Value.absent(), this.pinnedByUserId = const Value.absent(), - this.channelCid = const Value.absent(), + required String channelCid, this.i18n = const Value.absent(), this.extraData = const Value.absent(), }) : id = Value(id), attachments = Value(attachments), - mentionedUsers = Value(mentionedUsers); + mentionedUsers = Value(mentionedUsers), + channelCid = Value(channelCid); static Insertable custom({ Expression? id, Expression? messageText, @@ -2247,7 +2246,7 @@ class PinnedMessagesCompanion extends UpdateCompanion { Expression? pinnedAt, Expression? pinExpires, Expression? pinnedByUserId, - Expression? channelCid, + Expression? channelCid, Expression?>? i18n, Expression?>? extraData, }) { @@ -2303,7 +2302,7 @@ class PinnedMessagesCompanion extends UpdateCompanion { Value? pinnedAt, Value? pinExpires, Value? pinnedByUserId, - Value? channelCid, + Value? channelCid, Value?>? i18n, Value?>? extraData}) { return PinnedMessagesCompanion( @@ -2414,7 +2413,7 @@ class PinnedMessagesCompanion extends UpdateCompanion { map['pinned_by_user_id'] = Variable(pinnedByUserId.value); } if (channelCid.present) { - map['channel_cid'] = Variable(channelCid.value); + map['channel_cid'] = Variable(channelCid.value); } if (i18n.present) { final converter = $PinnedMessagesTable.$converter5; @@ -2589,11 +2588,10 @@ class $PinnedMessagesTable extends PinnedMessages typeName: 'TEXT', requiredDuringInsert: false); final VerificationMeta _channelCidMeta = const VerificationMeta('channelCid'); late final GeneratedColumn channelCid = GeneratedColumn( - 'channel_cid', aliasedName, true, + 'channel_cid', aliasedName, false, typeName: 'TEXT', - requiredDuringInsert: false, - $customConstraints: - 'NULLABLE REFERENCES channels(cid) ON DELETE CASCADE'); + requiredDuringInsert: true, + $customConstraints: 'REFERENCES channels(cid) ON DELETE CASCADE'); final VerificationMeta _i18nMeta = const VerificationMeta('i18n'); late final GeneratedColumnWithTypeConverter, String?> i18n = GeneratedColumn('i18n', aliasedName, true, @@ -2734,6 +2732,8 @@ class $PinnedMessagesTable extends PinnedMessages _channelCidMeta, channelCid.isAcceptableOrUnknown( data['channel_cid']!, _channelCidMeta)); + } else if (isInserting) { + context.missing(_channelCidMeta); } context.handle(_i18nMeta, const VerificationResult.success()); context.handle(_extraDataMeta, const VerificationResult.success()); diff --git a/packages/stream_chat_persistence/lib/src/entity/members.dart b/packages/stream_chat_persistence/lib/src/entity/members.dart index 8d3d4a57..2773ddaa 100644 --- a/packages/stream_chat_persistence/lib/src/entity/members.dart +++ b/packages/stream_chat_persistence/lib/src/entity/members.dart @@ -39,8 +39,5 @@ class Members extends Table { DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)(); @override - Set get primaryKey => { - userId, - channelCid, - }; + Set get primaryKey => {userId, channelCid}; } diff --git a/packages/stream_chat_persistence/lib/src/entity/messages.dart b/packages/stream_chat_persistence/lib/src/entity/messages.dart index 0aef2b84..108b903f 100644 --- a/packages/stream_chat_persistence/lib/src/entity/messages.dart +++ b/packages/stream_chat_persistence/lib/src/entity/messages.dart @@ -77,8 +77,8 @@ class Messages extends Table { TextColumn get pinnedByUserId => text().nullable()(); /// The channel cid of which this message is part of - TextColumn get channelCid => text().nullable().customConstraint( - 'NULLABLE REFERENCES channels(cid) ON DELETE CASCADE')(); + TextColumn get channelCid => + text().customConstraint('REFERENCES channels(cid) ON DELETE CASCADE')(); /// A Map of [messageText] translations. TextColumn get i18n => text().nullable().map(MapConverter())(); diff --git a/packages/stream_chat_persistence/lib/src/mapper/message_mapper.dart b/packages/stream_chat_persistence/lib/src/mapper/message_mapper.dart index 85692296..f49631a9 100644 --- a/packages/stream_chat_persistence/lib/src/mapper/message_mapper.dart +++ b/packages/stream_chat_persistence/lib/src/mapper/message_mapper.dart @@ -51,7 +51,7 @@ extension MessageEntityX on MessageEntity { /// Useful mapping functions for [Message] extension MessageX on Message { /// Maps a [Message] into [MessageEntity] - MessageEntity toEntity({String? cid}) => MessageEntity( + MessageEntity toEntity({required String cid}) => MessageEntity( id: id, attachments: attachments.map((it) => jsonEncode(it.toData())).toList(), channelCid: cid, diff --git a/packages/stream_chat_persistence/lib/src/mapper/pinned_message_mapper.dart b/packages/stream_chat_persistence/lib/src/mapper/pinned_message_mapper.dart index b1a7849a..d47aa109 100644 --- a/packages/stream_chat_persistence/lib/src/mapper/pinned_message_mapper.dart +++ b/packages/stream_chat_persistence/lib/src/mapper/pinned_message_mapper.dart @@ -51,7 +51,8 @@ extension PinnedMessageEntityX on PinnedMessageEntity { /// Useful mapping functions for [Message] extension PMessageX on Message { /// Maps a [Message] into [PinnedMessageEntity] - PinnedMessageEntity toPinnedEntity({String? cid}) => PinnedMessageEntity( + PinnedMessageEntity toPinnedEntity({required String cid}) => + PinnedMessageEntity( id: id, attachments: attachments.map((it) => jsonEncode(it.toData())).toList(), channelCid: cid, 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 5df7a124..d7db7924 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 @@ -296,25 +296,25 @@ class StreamChatPersistenceClient extends ChatPersistenceClient { } @override - Future updateMembers(String cid, List members) { + Future bulkUpdateMembers(Map> members) { assert(_debugIsConnected, ''); - _logger.info('updateMembers'); - return _readProtected(() => db!.memberDao.updateMembers(cid, members)); + _logger.info('bulkUpdateMembers'); + return _readProtected(() => db!.memberDao.bulkUpdateMembers(members)); } @override - Future updateMessages(String cid, List messages) { + Future bulkUpdateMessages(Map> messages) { assert(_debugIsConnected, ''); - _logger.info('updateMessages'); - return _readProtected(() => db!.messageDao.updateMessages(cid, messages)); + _logger.info('bulkUpdateMessages'); + return _readProtected(() => db!.messageDao.bulkUpdateMessages(messages)); } @override - Future updatePinnedMessages(String cid, List messages) { + Future bulkUpdatePinnedMessages(Map> messages) { assert(_debugIsConnected, ''); - _logger.info('updatePinnedMessages'); + _logger.info('bulkUpdatePinnedMessages'); return _readProtected( - () => db!.pinnedMessageDao.updateMessages(cid, messages), + () => db!.pinnedMessageDao.bulkUpdateMessages(messages), ); } @@ -335,10 +335,10 @@ class StreamChatPersistenceClient extends ChatPersistenceClient { } @override - Future updateReads(String cid, List reads) { + Future bulkUpdateReads(Map> reads) { assert(_debugIsConnected, ''); - _logger.info('updateReads'); - return _readProtected(() => db!.readDao.updateReads(cid, reads)); + _logger.info('bulkUpdateReads'); + return _readProtected(() => db!.readDao.bulkUpdateReads(reads)); } @override 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 37e82e93..25e926b1 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 @@ -397,23 +397,26 @@ void main() { test('updateMessages', () async { const cid = 'testCid'; final messages = List.generate(3, (index) => Message()); - when(() => mockDatabase.messageDao.updateMessages(cid, messages)) + + when(() => mockDatabase.messageDao.bulkUpdateMessages({cid: messages})) .thenAnswer((_) => Future.value()); await client.updateMessages(cid, messages); - verify(() => mockDatabase.messageDao.updateMessages(cid, messages)) + verify(() => mockDatabase.messageDao.bulkUpdateMessages({cid: messages})) .called(1); }); test('updatePinnedMessages', () async { const cid = 'testCid'; final messages = List.generate(3, (index) => Message()); - when(() => mockDatabase.pinnedMessageDao.updateMessages(cid, messages)) - .thenAnswer((_) => Future.value()); + when( + () => mockDatabase.pinnedMessageDao.bulkUpdateMessages({cid: messages}), + ).thenAnswer((_) => Future.value()); await client.updatePinnedMessages(cid, messages); - verify(() => mockDatabase.pinnedMessageDao.updateMessages(cid, messages)) - .called(1); + verify( + () => mockDatabase.pinnedMessageDao.bulkUpdateMessages({cid: messages}), + ).called(1); }); test('getChannelThreads', () async { @@ -456,11 +459,11 @@ void main() { test('updateMembers', () async { const cid = 'testCid'; final members = List.generate(3, (index) => Member()); - when(() => mockDatabase.memberDao.updateMembers(cid, members)) + when(() => mockDatabase.memberDao.bulkUpdateMembers({cid: members})) .thenAnswer((_) => Future.value()); await client.updateMembers(cid, members); - verify(() => mockDatabase.memberDao.updateMembers(cid, members)) + verify(() => mockDatabase.memberDao.bulkUpdateMembers({cid: members})) .called(1); }); @@ -473,11 +476,12 @@ void main() { lastRead: DateTime.now(), ), ); - when(() => mockDatabase.readDao.updateReads(cid, reads)) + when(() => mockDatabase.readDao.bulkUpdateReads({cid: reads})) .thenAnswer((_) => Future.value()); await client.updateReads(cid, reads); - verify(() => mockDatabase.readDao.updateReads(cid, reads)).called(1); + verify(() => mockDatabase.readDao.bulkUpdateReads({cid: reads})) + .called(1); }); test('updateUsers', () async {