diff --git a/packages/stream_chat/CHANGELOG.md b/packages/stream_chat/CHANGELOG.md index 298468ba2..5e37a3984 100644 --- a/packages/stream_chat/CHANGELOG.md +++ b/packages/stream_chat/CHANGELOG.md @@ -3,6 +3,8 @@ 🐞 Fixed - Fixed `WebSocket` race condition where reconnection could access null user during disconnect. +- Fixed draft message persistence issues where removed drafts were not properly deleted from the + database. ## 9.14.0 diff --git a/packages/stream_chat/lib/src/db/chat_persistence_client.dart b/packages/stream_chat/lib/src/db/chat_persistence_client.dart index ade11ffab..95439d574 100644 --- a/packages/stream_chat/lib/src/db/chat_persistence_client.dart +++ b/packages/stream_chat/lib/src/db/chat_persistence_client.dart @@ -240,6 +240,9 @@ abstract class ChatPersistenceClient { /// Deletes all the members by channel [cids] Future deleteMembersByCids(List cids); + /// Deletes all the draft messages by channel [cids] + Future deleteDraftMessagesByCids(List cids); + /// Updates the channel [cid] threads data along with reactions and users. Future updateChannelThreads( String cid, @@ -277,7 +280,6 @@ abstract class ChatPersistenceClient { final channelWithPinnedMessages = ?>{}; final channelWithReads = ?>{}; final channelWithMembers = ?>{}; - final drafts = []; final users = []; final reactions = []; @@ -287,6 +289,9 @@ abstract class ChatPersistenceClient { final pollVotes = []; final pollVotesToDelete = []; + final drafts = []; + final draftsToDeleteCids = []; + for (final state in channelStates) { final channel = state.channel; // Continue if channel is not available. @@ -311,6 +316,7 @@ abstract class ChatPersistenceClient { membersToDelete.add(cid); reactionsToDelete.addAll(messages?.map((it) => it.id) ?? []); pinnedReactionsToDelete.addAll(pinnedMessages?.map((it) => it.id) ?? []); + draftsToDeleteCids.add(cid); // preparing addition data channelWithReads[cid] = reads; @@ -356,6 +362,7 @@ abstract class ChatPersistenceClient { deleteReactionsByMessageId(reactionsToDelete), deletePinnedMessageReactionsByMessageId(pinnedReactionsToDelete), deletePollVotesByPollIds(pollVotesToDelete), + deleteDraftMessagesByCids(draftsToDeleteCids), ]); // Updating first as does not depend on any other table. 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 00adbe668..a3036a2fd 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 @@ -32,6 +32,9 @@ class TestPersistenceClient extends ChatPersistenceClient { @override Future deleteMembersByCids(List cids) => Future.value(); + @override + Future deleteDraftMessagesByCids(List cids) => Future.value(); + @override Future deleteMessageByCids(List cids) => Future.value(); diff --git a/packages/stream_chat_persistence/CHANGELOG.md b/packages/stream_chat_persistence/CHANGELOG.md index a8259e2f5..bd5a53354 100644 --- a/packages/stream_chat_persistence/CHANGELOG.md +++ b/packages/stream_chat_persistence/CHANGELOG.md @@ -1,3 +1,10 @@ +## Upcoming + +🐞 Fixed + +- Fixed draft message retrieval logic where channel drafts were incorrectly attached to all messages + instead of only thread drafts being attached to their respective parent messages. + ## 9.14.0 - Updated `stream_chat` dependency to [`9.14.0`](https://pub.dev/packages/stream_chat/changelog). diff --git a/packages/stream_chat_persistence/lib/src/dao/draft_message_dao.dart b/packages/stream_chat_persistence/lib/src/dao/draft_message_dao.dart index 1c69c3165..b5a5cb6fc 100644 --- a/packages/stream_chat_persistence/lib/src/dao/draft_message_dao.dart +++ b/packages/stream_chat_persistence/lib/src/dao/draft_message_dao.dart @@ -102,4 +102,9 @@ class DraftMessageDao extends DatabaseAccessor return query.go(); } + + /// Deletes all the draft messages by matching [DraftMessages.channelCid] + /// with the given list of [cids]. + Future deleteDraftMessagesByCids(List cids) => + (delete(draftMessages)..where((tbl) => tbl.channelCid.isIn(cids))).go(); } 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 3d11267a6..8a947a742 100644 --- a/packages/stream_chat_persistence/lib/src/dao/message_dao.dart +++ b/packages/stream_chat_persistence/lib/src/dao/message_dao.dart @@ -38,7 +38,7 @@ class MessageDao extends DatabaseAccessor Future _messageFromJoinRow( TypedResult rows, { - bool fetchDraft = true, + bool fetchDraft = false, }) async { final userEntity = rows.readTableOrNull(_users); final pinnedByEntity = rows.readTableOrNull(_pinnedByUsers); @@ -62,7 +62,7 @@ class MessageDao extends DatabaseAccessor final draft = await switch (fetchDraft) { true => _db.draftMessageDao.getDraftMessageByCid( msgEntity.channelCid, - parentId: msgEntity.parentId, + parentId: msgEntity.id, ), _ => null, }; @@ -85,19 +85,24 @@ class MessageDao extends DatabaseAccessor Future getMessageById( String id, { bool fetchDraft = true, - }) async => - await (select(messages).join( - [ - leftOuterJoin(_users, messages.userId.equalsExp(_users.id)), - leftOuterJoin( - _pinnedByUsers, - messages.pinnedByUserId.equalsExp(_pinnedByUsers.id), - ), - ], - )..where(messages.id.equals(id))) - .map((row) { - return _messageFromJoinRow(row, fetchDraft: fetchDraft); - }).getSingleOrNull(); + }) async { + final query = select(messages).join([ + leftOuterJoin(_users, messages.userId.equalsExp(_users.id)), + leftOuterJoin( + _pinnedByUsers, + messages.pinnedByUserId.equalsExp(_pinnedByUsers.id), + ), + ]) + ..where(messages.id.equals(id)); + + final result = await query.getSingleOrNull(); + if (result == null) return null; + + return _messageFromJoinRow( + result, + fetchDraft: fetchDraft, + ); + } /// Returns all the messages of a particular thread by matching /// [Messages.channelCid] with [cid] @@ -163,22 +168,31 @@ class MessageDao extends DatabaseAccessor /// [Messages.channelCid] with [parentId] Future> getMessagesByCid( String cid, { + bool fetchDraft = true, PaginationParams? messagePagination, }) async { - final msgList = await Future.wait(await (select(messages).join([ + final query = select(messages).join([ leftOuterJoin(_users, messages.userId.equalsExp(_users.id)), leftOuterJoin( _pinnedByUsers, messages.pinnedByUserId.equalsExp(_pinnedByUsers.id), ), ]) - ..where(messages.channelCid.equals(cid)) - ..where( - messages.parentId.isNull() | messages.showInChannel.equals(true), - ) - ..orderBy([OrderingTerm.asc(messages.createdAt)])) - .map(_messageFromJoinRow) - .get()); + ..where(messages.channelCid.equals(cid)) + ..where(messages.parentId.isNull() | messages.showInChannel.equals(true)) + ..orderBy([OrderingTerm.asc(messages.createdAt)]); + + final result = await query.get(); + if (result.isEmpty) return []; + + final msgList = await Future.wait( + result.map( + (row) => _messageFromJoinRow( + row, + fetchDraft: fetchDraft, + ), + ), + ); if (msgList.isNotEmpty) { if (messagePagination?.lessThan != null) { 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 bf7b45b12..ac8576b33 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 @@ -35,8 +35,11 @@ class PinnedMessageDao extends DatabaseAccessor Future deleteMessageByCids(List cids) async => (delete(pinnedMessages)..where((tbl) => tbl.channelCid.isIn(cids))).go(); - Future _messageFromJoinRow(TypedResult rows) async { - final userEntity = rows.readTableOrNull(users); + Future _messageFromJoinRow( + TypedResult rows, { + bool fetchDraft = false, + }) async { + final userEntity = rows.readTableOrNull(_users); final pinnedByEntity = rows.readTableOrNull(_pinnedByUsers); final msgEntity = rows.readTable(pinnedMessages); final latestReactions = @@ -46,16 +49,25 @@ class PinnedMessageDao extends DatabaseAccessor msgEntity.id, _db.userId, ); - Message? quotedMessage; - final quotedMessageId = msgEntity.quotedMessageId; - if (quotedMessageId != null) { - quotedMessage = await getMessageById(quotedMessageId); - } - Poll? poll; - final pollId = msgEntity.pollId; - if (pollId != null) { - poll = await _db.pollDao.getPollById(pollId); - } + + final quotedMessage = await switch (msgEntity.quotedMessageId) { + final id? => getMessageById(id), + _ => null, + }; + + final poll = await switch (msgEntity.pollId) { + final id? => _db.pollDao.getPollById(id), + _ => null, + }; + + final draft = await switch (fetchDraft) { + true => _db.draftMessageDao.getDraftMessageByCid( + msgEntity.channelCid, + parentId: msgEntity.id, + ), + _ => null, + }; + return msgEntity.toMessage( user: userEntity?.toUser(), pinnedBy: pinnedByEntity?.toUser(), @@ -63,21 +75,32 @@ class PinnedMessageDao extends DatabaseAccessor ownReactions: ownReactions, quotedMessage: quotedMessage, poll: poll, + draft: draft, ); } /// Returns a single message by matching the [PinnedMessages.id] with [id] - Future getMessageById(String id) async => - await (select(pinnedMessages).join([ - leftOuterJoin(_users, pinnedMessages.userId.equalsExp(_users.id)), - leftOuterJoin( - _pinnedByUsers, - pinnedMessages.pinnedByUserId.equalsExp(_pinnedByUsers.id), - ), - ]) - ..where(pinnedMessages.id.equals(id))) - .map(_messageFromJoinRow) - .getSingleOrNull(); + Future getMessageById( + String id, { + bool fetchDraft = true, + }) async { + final query = select(pinnedMessages).join([ + leftOuterJoin(_users, pinnedMessages.userId.equalsExp(_users.id)), + leftOuterJoin( + _pinnedByUsers, + pinnedMessages.pinnedByUserId.equalsExp(_pinnedByUsers.id), + ), + ]) + ..where(pinnedMessages.id.equals(id)); + + final result = await query.getSingleOrNull(); + if (result == null) return null; + + return _messageFromJoinRow( + result, + fetchDraft: fetchDraft, + ); + } /// Returns all the messages of a particular thread by matching /// [PinnedMessages.channelCid] with [cid] @@ -142,21 +165,32 @@ class PinnedMessageDao extends DatabaseAccessor /// [PinnedMessages.channelCid] with [parentId] Future> getMessagesByCid( String cid, { + bool fetchDraft = true, PaginationParams? messagePagination, }) async { - final msgList = await Future.wait(await (select(pinnedMessages).join([ + final query = select(pinnedMessages).join([ leftOuterJoin(_users, pinnedMessages.userId.equalsExp(_users.id)), leftOuterJoin( _pinnedByUsers, pinnedMessages.pinnedByUserId.equalsExp(_pinnedByUsers.id), ), ]) - ..where(pinnedMessages.channelCid.equals(cid)) - ..where(pinnedMessages.parentId.isNull() | - pinnedMessages.showInChannel.equals(true)) - ..orderBy([OrderingTerm.asc(pinnedMessages.createdAt)])) - .map(_messageFromJoinRow) - .get()); + ..where(pinnedMessages.channelCid.equals(cid)) + ..where(pinnedMessages.parentId.isNull() | + pinnedMessages.showInChannel.equals(true)) + ..orderBy([OrderingTerm.asc(pinnedMessages.createdAt)]); + + final result = await query.get(); + if (result.isEmpty) return []; + + final msgList = await Future.wait( + result.map( + (row) => _messageFromJoinRow( + row, + fetchDraft: fetchDraft, + ), + ), + ); if (msgList.isNotEmpty) { if (messagePagination?.lessThan != null) { 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 c1aa21797..7aaeee4b2 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 @@ -55,7 +55,7 @@ class DriftChatDatabase extends _$DriftChatDatabase { // you should bump this number whenever you change or add a table definition. @override - int get schemaVersion => 21; + int get schemaVersion => 22; @override MigrationStrategy get migration => MigrationStrategy( diff --git a/packages/stream_chat_persistence/lib/src/db/drift_chat_database.g.dart b/packages/stream_chat_persistence/lib/src/db/drift_chat_database.g.dart index 62b81b61e..fdf044d67 100644 --- a/packages/stream_chat_persistence/lib/src/db/drift_chat_database.g.dart +++ b/packages/stream_chat_persistence/lib/src/db/drift_chat_database.g.dart @@ -846,12 +846,6 @@ class $MessagesTable extends Messages type: DriftSqlType.string, requiredDuringInsert: false) .withConverter?>( $MessagesTable.$converterrestrictedVisibilityn); - static const VerificationMeta _draftMessageIdMeta = - const VerificationMeta('draftMessageId'); - @override - late final GeneratedColumn draftMessageId = GeneratedColumn( - 'draft_message_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); @override late final GeneratedColumnWithTypeConverter?, String> extraData = GeneratedColumn('extra_data', aliasedName, true, @@ -889,7 +883,6 @@ class $MessagesTable extends Messages channelCid, i18n, restrictedVisibility, - draftMessageId, extraData ]; @override @@ -1031,12 +1024,6 @@ class $MessagesTable extends Messages } else if (isInserting) { context.missing(_channelCidMeta); } - if (data.containsKey('draft_message_id')) { - context.handle( - _draftMessageIdMeta, - draftMessageId.isAcceptableOrUnknown( - data['draft_message_id']!, _draftMessageIdMeta)); - } return context; } @@ -1109,8 +1096,6 @@ class $MessagesTable extends Messages restrictedVisibility: $MessagesTable.$converterrestrictedVisibilityn .fromSql(attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}restricted_visibility'])), - draftMessageId: attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}draft_message_id']), extraData: $MessagesTable.$converterextraDatan.fromSql(attachedDatabase .typeMapping .read(DriftSqlType.string, data['${effectivePrefix}extra_data'])), @@ -1232,9 +1217,6 @@ class MessageEntity extends DataClass implements Insertable { /// The list of user ids that should be able to see the message. final List? restrictedVisibility; - /// Id of the draft message if this message is a parent message. - final String? draftMessageId; - /// Message custom extraData final Map? extraData; const MessageEntity( @@ -1267,7 +1249,6 @@ class MessageEntity extends DataClass implements Insertable { required this.channelCid, this.i18n, this.restrictedVisibility, - this.draftMessageId, this.extraData}); @override Map toColumns(bool nullToAbsent) { @@ -1352,9 +1333,6 @@ class MessageEntity extends DataClass implements Insertable { .$converterrestrictedVisibilityn .toSql(restrictedVisibility)); } - if (!nullToAbsent || draftMessageId != null) { - map['draft_message_id'] = Variable(draftMessageId); - } if (!nullToAbsent || extraData != null) { map['extra_data'] = Variable( $MessagesTable.$converterextraDatan.toSql(extraData)); @@ -1398,7 +1376,6 @@ class MessageEntity extends DataClass implements Insertable { i18n: serializer.fromJson?>(json['i18n']), restrictedVisibility: serializer.fromJson?>(json['restrictedVisibility']), - draftMessageId: serializer.fromJson(json['draftMessageId']), extraData: serializer.fromJson?>(json['extraData']), ); } @@ -1438,7 +1415,6 @@ class MessageEntity extends DataClass implements Insertable { 'i18n': serializer.toJson?>(i18n), 'restrictedVisibility': serializer.toJson?>(restrictedVisibility), - 'draftMessageId': serializer.toJson(draftMessageId), 'extraData': serializer.toJson?>(extraData), }; } @@ -1474,7 +1450,6 @@ class MessageEntity extends DataClass implements Insertable { String? channelCid, Value?> i18n = const Value.absent(), Value?> restrictedVisibility = const Value.absent(), - Value draftMessageId = const Value.absent(), Value?> extraData = const Value.absent()}) => MessageEntity( id: id ?? this.id, @@ -1524,8 +1499,6 @@ class MessageEntity extends DataClass implements Insertable { restrictedVisibility: restrictedVisibility.present ? restrictedVisibility.value : this.restrictedVisibility, - draftMessageId: - draftMessageId.present ? draftMessageId.value : this.draftMessageId, extraData: extraData.present ? extraData.value : this.extraData, ); MessageEntity copyWithCompanion(MessagesCompanion data) { @@ -1590,9 +1563,6 @@ class MessageEntity extends DataClass implements Insertable { restrictedVisibility: data.restrictedVisibility.present ? data.restrictedVisibility.value : this.restrictedVisibility, - draftMessageId: data.draftMessageId.present - ? data.draftMessageId.value - : this.draftMessageId, extraData: data.extraData.present ? data.extraData.value : this.extraData, ); } @@ -1629,7 +1599,6 @@ class MessageEntity extends DataClass implements Insertable { ..write('channelCid: $channelCid, ') ..write('i18n: $i18n, ') ..write('restrictedVisibility: $restrictedVisibility, ') - ..write('draftMessageId: $draftMessageId, ') ..write('extraData: $extraData') ..write(')')) .toString(); @@ -1666,7 +1635,6 @@ class MessageEntity extends DataClass implements Insertable { channelCid, i18n, restrictedVisibility, - draftMessageId, extraData ]); @override @@ -1702,7 +1670,6 @@ class MessageEntity extends DataClass implements Insertable { other.channelCid == this.channelCid && other.i18n == this.i18n && other.restrictedVisibility == this.restrictedVisibility && - other.draftMessageId == this.draftMessageId && other.extraData == this.extraData); } @@ -1736,7 +1703,6 @@ class MessagesCompanion extends UpdateCompanion { final Value channelCid; final Value?> i18n; final Value?> restrictedVisibility; - final Value draftMessageId; final Value?> extraData; final Value rowid; const MessagesCompanion({ @@ -1769,7 +1735,6 @@ class MessagesCompanion extends UpdateCompanion { this.channelCid = const Value.absent(), this.i18n = const Value.absent(), this.restrictedVisibility = const Value.absent(), - this.draftMessageId = const Value.absent(), this.extraData = const Value.absent(), this.rowid = const Value.absent(), }); @@ -1803,7 +1768,6 @@ class MessagesCompanion extends UpdateCompanion { required String channelCid, this.i18n = const Value.absent(), this.restrictedVisibility = const Value.absent(), - this.draftMessageId = const Value.absent(), this.extraData = const Value.absent(), this.rowid = const Value.absent(), }) : id = Value(id), @@ -1841,7 +1805,6 @@ class MessagesCompanion extends UpdateCompanion { Expression? channelCid, Expression? i18n, Expression? restrictedVisibility, - Expression? draftMessageId, Expression? extraData, Expression? rowid, }) { @@ -1877,7 +1840,6 @@ class MessagesCompanion extends UpdateCompanion { if (i18n != null) 'i18n': i18n, if (restrictedVisibility != null) 'restricted_visibility': restrictedVisibility, - if (draftMessageId != null) 'draft_message_id': draftMessageId, if (extraData != null) 'extra_data': extraData, if (rowid != null) 'rowid': rowid, }); @@ -1913,7 +1875,6 @@ class MessagesCompanion extends UpdateCompanion { Value? channelCid, Value?>? i18n, Value?>? restrictedVisibility, - Value? draftMessageId, Value?>? extraData, Value? rowid}) { return MessagesCompanion( @@ -1946,7 +1907,6 @@ class MessagesCompanion extends UpdateCompanion { channelCid: channelCid ?? this.channelCid, i18n: i18n ?? this.i18n, restrictedVisibility: restrictedVisibility ?? this.restrictedVisibility, - draftMessageId: draftMessageId ?? this.draftMessageId, extraData: extraData ?? this.extraData, rowid: rowid ?? this.rowid, ); @@ -2049,9 +2009,6 @@ class MessagesCompanion extends UpdateCompanion { .$converterrestrictedVisibilityn .toSql(restrictedVisibility.value)); } - if (draftMessageId.present) { - map['draft_message_id'] = Variable(draftMessageId.value); - } if (extraData.present) { map['extra_data'] = Variable( $MessagesTable.$converterextraDatan.toSql(extraData.value)); @@ -2094,7 +2051,6 @@ class MessagesCompanion extends UpdateCompanion { ..write('channelCid: $channelCid, ') ..write('i18n: $i18n, ') ..write('restrictedVisibility: $restrictedVisibility, ') - ..write('draftMessageId: $draftMessageId, ') ..write('extraData: $extraData, ') ..write('rowid: $rowid') ..write(')')) @@ -2998,12 +2954,6 @@ class $PinnedMessagesTable extends PinnedMessages type: DriftSqlType.string, requiredDuringInsert: false) .withConverter?>( $PinnedMessagesTable.$converterrestrictedVisibilityn); - static const VerificationMeta _draftMessageIdMeta = - const VerificationMeta('draftMessageId'); - @override - late final GeneratedColumn draftMessageId = GeneratedColumn( - 'draft_message_id', aliasedName, true, - type: DriftSqlType.string, requiredDuringInsert: false); @override late final GeneratedColumnWithTypeConverter?, String> extraData = GeneratedColumn('extra_data', aliasedName, true, @@ -3041,7 +2991,6 @@ class $PinnedMessagesTable extends PinnedMessages channelCid, i18n, restrictedVisibility, - draftMessageId, extraData ]; @override @@ -3184,12 +3133,6 @@ class $PinnedMessagesTable extends PinnedMessages } else if (isInserting) { context.missing(_channelCidMeta); } - if (data.containsKey('draft_message_id')) { - context.handle( - _draftMessageIdMeta, - draftMessageId.isAcceptableOrUnknown( - data['draft_message_id']!, _draftMessageIdMeta)); - } return context; } @@ -3263,8 +3206,6 @@ class $PinnedMessagesTable extends PinnedMessages restrictedVisibility: $PinnedMessagesTable.$converterrestrictedVisibilityn .fromSql(attachedDatabase.typeMapping.read(DriftSqlType.string, data['${effectivePrefix}restricted_visibility'])), - draftMessageId: attachedDatabase.typeMapping.read( - DriftSqlType.string, data['${effectivePrefix}draft_message_id']), extraData: $PinnedMessagesTable.$converterextraDatan.fromSql( attachedDatabase.typeMapping .read(DriftSqlType.string, data['${effectivePrefix}extra_data'])), @@ -3387,9 +3328,6 @@ class PinnedMessageEntity extends DataClass /// The list of user ids that should be able to see the message. final List? restrictedVisibility; - /// Id of the draft message if this message is a parent message. - final String? draftMessageId; - /// Message custom extraData final Map? extraData; const PinnedMessageEntity( @@ -3422,7 +3360,6 @@ class PinnedMessageEntity extends DataClass required this.channelCid, this.i18n, this.restrictedVisibility, - this.draftMessageId, this.extraData}); @override Map toColumns(bool nullToAbsent) { @@ -3508,9 +3445,6 @@ class PinnedMessageEntity extends DataClass .$converterrestrictedVisibilityn .toSql(restrictedVisibility)); } - if (!nullToAbsent || draftMessageId != null) { - map['draft_message_id'] = Variable(draftMessageId); - } if (!nullToAbsent || extraData != null) { map['extra_data'] = Variable( $PinnedMessagesTable.$converterextraDatan.toSql(extraData)); @@ -3554,7 +3488,6 @@ class PinnedMessageEntity extends DataClass i18n: serializer.fromJson?>(json['i18n']), restrictedVisibility: serializer.fromJson?>(json['restrictedVisibility']), - draftMessageId: serializer.fromJson(json['draftMessageId']), extraData: serializer.fromJson?>(json['extraData']), ); } @@ -3594,7 +3527,6 @@ class PinnedMessageEntity extends DataClass 'i18n': serializer.toJson?>(i18n), 'restrictedVisibility': serializer.toJson?>(restrictedVisibility), - 'draftMessageId': serializer.toJson(draftMessageId), 'extraData': serializer.toJson?>(extraData), }; } @@ -3630,7 +3562,6 @@ class PinnedMessageEntity extends DataClass String? channelCid, Value?> i18n = const Value.absent(), Value?> restrictedVisibility = const Value.absent(), - Value draftMessageId = const Value.absent(), Value?> extraData = const Value.absent()}) => PinnedMessageEntity( id: id ?? this.id, @@ -3680,8 +3611,6 @@ class PinnedMessageEntity extends DataClass restrictedVisibility: restrictedVisibility.present ? restrictedVisibility.value : this.restrictedVisibility, - draftMessageId: - draftMessageId.present ? draftMessageId.value : this.draftMessageId, extraData: extraData.present ? extraData.value : this.extraData, ); PinnedMessageEntity copyWithCompanion(PinnedMessagesCompanion data) { @@ -3746,9 +3675,6 @@ class PinnedMessageEntity extends DataClass restrictedVisibility: data.restrictedVisibility.present ? data.restrictedVisibility.value : this.restrictedVisibility, - draftMessageId: data.draftMessageId.present - ? data.draftMessageId.value - : this.draftMessageId, extraData: data.extraData.present ? data.extraData.value : this.extraData, ); } @@ -3785,7 +3711,6 @@ class PinnedMessageEntity extends DataClass ..write('channelCid: $channelCid, ') ..write('i18n: $i18n, ') ..write('restrictedVisibility: $restrictedVisibility, ') - ..write('draftMessageId: $draftMessageId, ') ..write('extraData: $extraData') ..write(')')) .toString(); @@ -3822,7 +3747,6 @@ class PinnedMessageEntity extends DataClass channelCid, i18n, restrictedVisibility, - draftMessageId, extraData ]); @override @@ -3858,7 +3782,6 @@ class PinnedMessageEntity extends DataClass other.channelCid == this.channelCid && other.i18n == this.i18n && other.restrictedVisibility == this.restrictedVisibility && - other.draftMessageId == this.draftMessageId && other.extraData == this.extraData); } @@ -3892,7 +3815,6 @@ class PinnedMessagesCompanion extends UpdateCompanion { final Value channelCid; final Value?> i18n; final Value?> restrictedVisibility; - final Value draftMessageId; final Value?> extraData; final Value rowid; const PinnedMessagesCompanion({ @@ -3925,7 +3847,6 @@ class PinnedMessagesCompanion extends UpdateCompanion { this.channelCid = const Value.absent(), this.i18n = const Value.absent(), this.restrictedVisibility = const Value.absent(), - this.draftMessageId = const Value.absent(), this.extraData = const Value.absent(), this.rowid = const Value.absent(), }); @@ -3959,7 +3880,6 @@ class PinnedMessagesCompanion extends UpdateCompanion { required String channelCid, this.i18n = const Value.absent(), this.restrictedVisibility = const Value.absent(), - this.draftMessageId = const Value.absent(), this.extraData = const Value.absent(), this.rowid = const Value.absent(), }) : id = Value(id), @@ -3997,7 +3917,6 @@ class PinnedMessagesCompanion extends UpdateCompanion { Expression? channelCid, Expression? i18n, Expression? restrictedVisibility, - Expression? draftMessageId, Expression? extraData, Expression? rowid, }) { @@ -4033,7 +3952,6 @@ class PinnedMessagesCompanion extends UpdateCompanion { if (i18n != null) 'i18n': i18n, if (restrictedVisibility != null) 'restricted_visibility': restrictedVisibility, - if (draftMessageId != null) 'draft_message_id': draftMessageId, if (extraData != null) 'extra_data': extraData, if (rowid != null) 'rowid': rowid, }); @@ -4069,7 +3987,6 @@ class PinnedMessagesCompanion extends UpdateCompanion { Value? channelCid, Value?>? i18n, Value?>? restrictedVisibility, - Value? draftMessageId, Value?>? extraData, Value? rowid}) { return PinnedMessagesCompanion( @@ -4102,7 +4019,6 @@ class PinnedMessagesCompanion extends UpdateCompanion { channelCid: channelCid ?? this.channelCid, i18n: i18n ?? this.i18n, restrictedVisibility: restrictedVisibility ?? this.restrictedVisibility, - draftMessageId: draftMessageId ?? this.draftMessageId, extraData: extraData ?? this.extraData, rowid: rowid ?? this.rowid, ); @@ -4207,9 +4123,6 @@ class PinnedMessagesCompanion extends UpdateCompanion { .$converterrestrictedVisibilityn .toSql(restrictedVisibility.value)); } - if (draftMessageId.present) { - map['draft_message_id'] = Variable(draftMessageId.value); - } if (extraData.present) { map['extra_data'] = Variable( $PinnedMessagesTable.$converterextraDatan.toSql(extraData.value)); @@ -4252,7 +4165,6 @@ class PinnedMessagesCompanion extends UpdateCompanion { ..write('channelCid: $channelCid, ') ..write('i18n: $i18n, ') ..write('restrictedVisibility: $restrictedVisibility, ') - ..write('draftMessageId: $draftMessageId, ') ..write('extraData: $extraData, ') ..write('rowid: $rowid') ..write(')')) @@ -9107,7 +9019,6 @@ typedef $$MessagesTableCreateCompanionBuilder = MessagesCompanion Function({ required String channelCid, Value?> i18n, Value?> restrictedVisibility, - Value draftMessageId, Value?> extraData, Value rowid, }); @@ -9141,7 +9052,6 @@ typedef $$MessagesTableUpdateCompanionBuilder = MessagesCompanion Function({ Value channelCid, Value?> i18n, Value?> restrictedVisibility, - Value draftMessageId, Value?> extraData, Value rowid, }); @@ -9310,10 +9220,6 @@ class $$MessagesTableFilterComposer column: $table.restrictedVisibility, builder: (column) => ColumnWithTypeConverterFilters(column)); - ColumnFilters get draftMessageId => $composableBuilder( - column: $table.draftMessageId, - builder: (column) => ColumnFilters(column)); - ColumnWithTypeConverterFilters?, Map, String> get extraData => $composableBuilder( @@ -9489,10 +9395,6 @@ class $$MessagesTableOrderingComposer column: $table.restrictedVisibility, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get draftMessageId => $composableBuilder( - column: $table.draftMessageId, - builder: (column) => ColumnOrderings(column)); - ColumnOrderings get extraData => $composableBuilder( column: $table.extraData, builder: (column) => ColumnOrderings(column)); @@ -9614,9 +9516,6 @@ class $$MessagesTableAnnotationComposer get restrictedVisibility => $composableBuilder( column: $table.restrictedVisibility, builder: (column) => column); - GeneratedColumn get draftMessageId => $composableBuilder( - column: $table.draftMessageId, builder: (column) => column); - GeneratedColumnWithTypeConverter?, String> get extraData => $composableBuilder( column: $table.extraData, builder: (column) => column); @@ -9738,7 +9637,6 @@ class $$MessagesTableTableManager extends RootTableManager< Value channelCid = const Value.absent(), Value?> i18n = const Value.absent(), Value?> restrictedVisibility = const Value.absent(), - Value draftMessageId = const Value.absent(), Value?> extraData = const Value.absent(), Value rowid = const Value.absent(), }) => @@ -9772,7 +9670,6 @@ class $$MessagesTableTableManager extends RootTableManager< channelCid: channelCid, i18n: i18n, restrictedVisibility: restrictedVisibility, - draftMessageId: draftMessageId, extraData: extraData, rowid: rowid, ), @@ -9807,7 +9704,6 @@ class $$MessagesTableTableManager extends RootTableManager< required String channelCid, Value?> i18n = const Value.absent(), Value?> restrictedVisibility = const Value.absent(), - Value draftMessageId = const Value.absent(), Value?> extraData = const Value.absent(), Value rowid = const Value.absent(), }) => @@ -9841,7 +9737,6 @@ class $$MessagesTableTableManager extends RootTableManager< channelCid: channelCid, i18n: i18n, restrictedVisibility: restrictedVisibility, - draftMessageId: draftMessageId, extraData: extraData, rowid: rowid, ), @@ -10466,7 +10361,6 @@ typedef $$PinnedMessagesTableCreateCompanionBuilder = PinnedMessagesCompanion required String channelCid, Value?> i18n, Value?> restrictedVisibility, - Value draftMessageId, Value?> extraData, Value rowid, }); @@ -10501,7 +10395,6 @@ typedef $$PinnedMessagesTableUpdateCompanionBuilder = PinnedMessagesCompanion Value channelCid, Value?> i18n, Value?> restrictedVisibility, - Value draftMessageId, Value?> extraData, Value rowid, }); @@ -10648,10 +10541,6 @@ class $$PinnedMessagesTableFilterComposer column: $table.restrictedVisibility, builder: (column) => ColumnWithTypeConverterFilters(column)); - ColumnFilters get draftMessageId => $composableBuilder( - column: $table.draftMessageId, - builder: (column) => ColumnFilters(column)); - ColumnWithTypeConverterFilters?, Map, String> get extraData => $composableBuilder( @@ -10791,10 +10680,6 @@ class $$PinnedMessagesTableOrderingComposer column: $table.restrictedVisibility, builder: (column) => ColumnOrderings(column)); - ColumnOrderings get draftMessageId => $composableBuilder( - column: $table.draftMessageId, - builder: (column) => ColumnOrderings(column)); - ColumnOrderings get extraData => $composableBuilder( column: $table.extraData, builder: (column) => ColumnOrderings(column)); } @@ -10899,9 +10784,6 @@ class $$PinnedMessagesTableAnnotationComposer get restrictedVisibility => $composableBuilder( column: $table.restrictedVisibility, builder: (column) => column); - GeneratedColumn get draftMessageId => $composableBuilder( - column: $table.draftMessageId, builder: (column) => column); - GeneratedColumnWithTypeConverter?, String> get extraData => $composableBuilder( column: $table.extraData, builder: (column) => column); @@ -10984,7 +10866,6 @@ class $$PinnedMessagesTableTableManager extends RootTableManager< Value channelCid = const Value.absent(), Value?> i18n = const Value.absent(), Value?> restrictedVisibility = const Value.absent(), - Value draftMessageId = const Value.absent(), Value?> extraData = const Value.absent(), Value rowid = const Value.absent(), }) => @@ -11018,7 +10899,6 @@ class $$PinnedMessagesTableTableManager extends RootTableManager< channelCid: channelCid, i18n: i18n, restrictedVisibility: restrictedVisibility, - draftMessageId: draftMessageId, extraData: extraData, rowid: rowid, ), @@ -11053,7 +10933,6 @@ class $$PinnedMessagesTableTableManager extends RootTableManager< required String channelCid, Value?> i18n = const Value.absent(), Value?> restrictedVisibility = const Value.absent(), - Value draftMessageId = const Value.absent(), Value?> extraData = const Value.absent(), Value rowid = const Value.absent(), }) => @@ -11087,7 +10966,6 @@ class $$PinnedMessagesTableTableManager extends RootTableManager< channelCid: channelCid, i18n: i18n, restrictedVisibility: restrictedVisibility, - draftMessageId: draftMessageId, extraData: extraData, rowid: rowid, ), diff --git a/packages/stream_chat_persistence/lib/src/entity/messages.dart b/packages/stream_chat_persistence/lib/src/entity/messages.dart index 766bbe599..caf11a1a1 100644 --- a/packages/stream_chat_persistence/lib/src/entity/messages.dart +++ b/packages/stream_chat_persistence/lib/src/entity/messages.dart @@ -129,9 +129,6 @@ class Messages extends Table { TextColumn get restrictedVisibility => text().nullable().map(ListConverter())(); - /// Id of the draft message if this message is a parent message. - TextColumn get draftMessageId => text().nullable()(); - /// Message custom extraData TextColumn get extraData => 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 8e4d9d544..323592516 100644 --- a/packages/stream_chat_persistence/lib/src/mapper/message_mapper.dart +++ b/packages/stream_chat_persistence/lib/src/mapper/message_mapper.dart @@ -91,6 +91,5 @@ extension MessageX on Message { pinnedByUserId: pinnedBy?.id, i18n: i18n, restrictedVisibility: restrictedVisibility, - draftMessageId: draft?.message.id, ); } 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 a87b716ec..c6cbf1941 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 @@ -13,6 +13,7 @@ extension PinnedMessageEntityX on PinnedMessageEntity { List? ownReactions, Message? quotedMessage, Poll? poll, + Draft? draft, }) => Message( shadowed: shadowed, @@ -52,6 +53,7 @@ extension PinnedMessageEntityX on PinnedMessageEntity { mentionedUsers.map((e) => User.fromJson(jsonDecode(e))).toList(), i18n: i18n, restrictedVisibility: restrictedVisibility, + draft: draft, ); } 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 c76991948..f136a9fe5 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 @@ -424,6 +424,13 @@ class StreamChatPersistenceClient extends ChatPersistenceClient { return db!.memberDao.deleteMemberByCids(cids); } + @override + Future deleteDraftMessagesByCids(List cids) { + assert(_debugIsConnected, ''); + _logger.info('deleteDraftMessagesByCids'); + return db!.draftMessageDao.deleteDraftMessagesByCids(cids); + } + @override Future deleteDraftMessageByCid( String cid, { diff --git a/packages/stream_chat_persistence/test/src/dao/draft_message_dao_test.dart b/packages/stream_chat_persistence/test/src/dao/draft_message_dao_test.dart index 5ac947b87..15c8e48b2 100644 --- a/packages/stream_chat_persistence/test/src/dao/draft_message_dao_test.dart +++ b/packages/stream_chat_persistence/test/src/dao/draft_message_dao_test.dart @@ -343,6 +343,41 @@ void main() { ); }); + group('deleteDraftMessagesByCids', () { + test('should delete drafts for specified channel cids', () async { + const cid1 = 'test:deleteByCids1'; + const cid2 = 'test:deleteByCids2'; + const cid3 = 'test:deleteByCids3'; + + // Create drafts for multiple channels + await _prepareTestData(cid1, count: 1); + await _prepareTestData(cid2, count: 1); + await _prepareTestData(cid3, count: 1); + + // Verify all drafts exist + final draft1Before = await draftMessageDao.getDraftMessageByCid(cid1); + final draft2Before = await draftMessageDao.getDraftMessageByCid(cid2); + final draft3Before = await draftMessageDao.getDraftMessageByCid(cid3); + expect(draft1Before, isNotNull); + expect(draft2Before, isNotNull); + expect(draft3Before, isNotNull); + + // Delete drafts for cid1 and cid2 + await draftMessageDao.deleteDraftMessagesByCids([cid1, cid2]); + + // Verify drafts for cid1 and cid2 are deleted + final draft1After = await draftMessageDao.getDraftMessageByCid(cid1); + final draft2After = await draftMessageDao.getDraftMessageByCid(cid2); + expect(draft1After, isNull); + expect(draft2After, isNull); + + // Verify draft for cid3 still exists + final draft3After = await draftMessageDao.getDraftMessageByCid(cid3); + expect(draft3After, isNotNull); + expect(draft3After!.channelCid, cid3); + }); + }); + group('DraftMessages entity references', () { test( 'should delete draft messages when referenced channel is deleted', 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 0a8cfc678..3b0cb7314 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 @@ -631,6 +631,16 @@ void main() { verify(() => mockDatabase.memberDao.deleteMemberByCids(cids)).called(1); }); + test('deleteDraftMessagesByCids', () async { + final cids = []; + when(() => mockDatabase.draftMessageDao.deleteDraftMessagesByCids(cids)) + .thenAnswer((_) => Future.value()); + + await client.deleteDraftMessagesByCids(cids); + verify(() => mockDatabase.draftMessageDao.deleteDraftMessagesByCids(cids)) + .called(1); + }); + test('getDraftMessageByCid', () async { const cid = 'testCid'; const parentId = 'testParentId';