From 9a4677a7b852c34344722455a64a20d5af462241 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Stolin=CC=81ski?= Date: Tue, 8 Feb 2022 15:47:42 +0100 Subject: [PATCH 1/5] decrease parent message reply count only once --- .../stream_chat/lib/src/client/channel.dart | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/packages/stream_chat/lib/src/client/channel.dart b/packages/stream_chat/lib/src/client/channel.dart index d3900842..84f0321e 100644 --- a/packages/stream_chat/lib/src/client/channel.dart +++ b/packages/stream_chat/lib/src/client/channel.dart @@ -1083,7 +1083,7 @@ class Channel { } else { // remove the passed message if response does // not contain message - state!.removeMessage(message); + state!.removeMessage(message, decreaseReplyCount: true); await _client.chatPersistenceClient?.deleteMessageById(messageId); } return res; @@ -1752,6 +1752,7 @@ class ChannelClientState { _subscriptions.add(_channel.on(EventType.messageDeleted).listen((event) { final message = event.message!; if (event.hardDelete == true) { + //do not decrease reply count here - it is done in _listenMessageUpdated removeMessage(message, hardDelete: true); } else { addMessage(message); @@ -1810,24 +1811,30 @@ class ChannelClientState { } /// Remove a [message] from this [channelState]. - void removeMessage(Message message, {bool hardDelete = false}) { + void removeMessage( + Message message, { + bool hardDelete = false, + bool decreaseReplyCount = false, + }) { final parentId = message.parentId; // i.e. it's a thread message // 1. Remove the thread message // 2. Reduce total reply count of parent message if (parentId != null) { - final allMessages = [...messages]; - final parentMessage = allMessages.firstWhereOrNull( - (it) => it.id == parentId, - ); + if (decreaseReplyCount) { + final allMessages = [...messages]; + final parentMessage = allMessages.firstWhereOrNull( + (it) => it.id == parentId, + ); - // return if message not available in the memory - if (parentMessage == null) return; - final replyCount = parentMessage.replyCount; - // return if reply count is null or zero - if (replyCount == null || replyCount == 0) return; + // return if message not available in the memory + if (parentMessage == null) return; + final replyCount = parentMessage.replyCount; + // return if reply count is null or zero + if (replyCount == null || replyCount == 0) return; - addMessage(parentMessage.copyWith(replyCount: replyCount - 1)); + addMessage(parentMessage.copyWith(replyCount: replyCount - 1)); + } updateThreadInfo( parentId, threads[parentId]! From 8f6bfd7b472acfbe007c4501b87c3b3584680fb7 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Thu, 17 Feb 2022 17:17:52 +0530 Subject: [PATCH 2/5] fix(llc): improve removeMessage logic. --- .../stream_chat/lib/src/client/channel.dart | 116 ++++++++---------- .../lib/src/client/retry_queue.dart | 2 +- 2 files changed, 51 insertions(+), 67 deletions(-) diff --git a/packages/stream_chat/lib/src/client/channel.dart b/packages/stream_chat/lib/src/client/channel.dart index 84f0321e..b5bad6cb 100644 --- a/packages/stream_chat/lib/src/client/channel.dart +++ b/packages/stream_chat/lib/src/client/channel.dart @@ -407,7 +407,7 @@ class Channel { if (index != -1) { final newAttachments = [...message!.attachments]..[index] = attachment; final updatedMessage = message!.copyWith(attachments: newAttachments); - state?.addMessage(updatedMessage); + state?.updateMessage(updatedMessage); // updating original message for next iteration message = message!.merge(updatedMessage); } @@ -512,7 +512,7 @@ class Channel { ).toList(), ); - state!.addMessage(message); + state!.updateMessage(message); try { if (message.attachments.any((it) => !it.uploadState.isSuccess)) { @@ -535,7 +535,7 @@ class Channel { type, skipPush: skipPush, ); - state!.addMessage(response.message); + state!.updateMessage(response.message); if (cooldown > 0) cooldownStartedAt = DateTime.now(); return response; } catch (e) { @@ -571,7 +571,7 @@ class Channel { ).toList(), ); - state?.addMessage(message); + state?.updateMessage(message); try { if (message.attachments.any((it) => !it.uploadState.isSuccess)) { @@ -594,7 +594,7 @@ class Channel { ownReactions: message.ownReactions, ); - state?.addMessage(m); + state?.updateMessage(m); return response; } catch (e) { @@ -602,7 +602,7 @@ class Channel { if (e.isRetriable) { state!._retryQueue.add([message]); } else { - state?.addMessage(originalMessage); + state?.updateMessage(originalMessage); } } rethrow; @@ -630,7 +630,7 @@ class Channel { ownReactions: message.ownReactions, ); - state?.addMessage(updatedMessage); + state?.updateMessage(updatedMessage); return response; } catch (e) { @@ -646,7 +646,7 @@ class Channel { // Directly deleting the local messages which are not yet sent to server if (message.status == MessageSendingStatus.sending || message.status == MessageSendingStatus.failed) { - state!.addMessage(message.copyWith( + state!.updateMessage(message.copyWith( type: 'deleted', status: MessageSendingStatus.sent, )); @@ -667,11 +667,11 @@ class Channel { deletedAt: message.deletedAt ?? DateTime.now(), ); - state?.addMessage(message); + state?.updateMessage(message); final response = await _client.deleteMessage(message.id, hard: hard); - state?.addMessage(message.copyWith(status: MessageSendingStatus.sent)); + state?.updateMessage(message.copyWith(status: MessageSendingStatus.sent)); return response; } catch (e) { @@ -860,7 +860,7 @@ class Channel { ownReactions: ownReactions, ); - state?.addMessage(newMessage); + state?.updateMessage(newMessage); try { final reactionResp = await _client.sendReaction( @@ -872,7 +872,7 @@ class Channel { return reactionResp; } catch (_) { // Reset the message if the update fails - state?.addMessage(message); + state?.updateMessage(message); rethrow; } } @@ -912,7 +912,7 @@ class Channel { ownReactions: ownReactions, ); - state?.addMessage(newMessage); + state?.updateMessage(newMessage); try { final deleteResponse = await _client.deleteReaction( @@ -922,7 +922,7 @@ class Channel { return deleteResponse; } catch (_) { // Reset the message if the update fails - state?.addMessage(message); + state?.updateMessage(message); rethrow; } } @@ -1079,11 +1079,11 @@ class Channel { // update the passed message with response message if (res.message != null) { - state!.addMessage(res.message!); + state!.updateMessage(res.message!); } else { // remove the passed message if response does // not contain message - state!.removeMessage(message, decreaseReplyCount: true); + state!.removeMessage(message); await _client.chatPersistenceClient?.deleteMessageById(messageId); } return res; @@ -1322,7 +1322,8 @@ class Channel { /// Remove the ban for the user with given [userID] in the channel. @Deprecated( - "Use 'unbanMember' instead. This method will be removed in v4.0.0") + "Use 'unbanMember' instead. This method will be removed in v4.0.0", + ) Future unbanUser(String userID) => unbanMember(userID); /// Remove the ban for the member with given [userID] in the channel. @@ -1694,7 +1695,9 @@ class ChannelClientState { void _listenReactionDeleted() { _subscriptions.add(_channel.on(EventType.reactionDeleted).listen((event) { final oldMessage = - messages.firstWhereOrNull((it) => it.id == event.message?.id); + messages.firstWhereOrNull((it) => it.id == event.message?.id) ?? + threads[event.message?.parentId] + ?.firstWhereOrNull((e) => e.id == event.message?.id); final reaction = event.reaction; final ownReactions = oldMessage?.ownReactions ?.whereNot((it) => @@ -1707,18 +1710,20 @@ class ChannelClientState { final message = event.message!.copyWith( ownReactions: ownReactions, ); - addMessage(message); + updateMessage(message); })); } void _listenReactions() { _subscriptions.add(_channel.on(EventType.reactionNew).listen((event) { final oldMessage = - messages.firstWhereOrNull((it) => it.id == event.message?.id); + messages.firstWhereOrNull((it) => it.id == event.message?.id) ?? + threads[event.message?.parentId] + ?.firstWhereOrNull((e) => e.id == event.message?.id); final message = event.message!.copyWith( ownReactions: oldMessage?.ownReactions, ); - addMessage(message); + updateMessage(message); })); } @@ -1730,12 +1735,13 @@ class ChannelClientState { ) .listen((event) { final oldMessage = - messages.firstWhereOrNull((it) => it.id == event.message?.id); - + messages.firstWhereOrNull((it) => it.id == event.message?.id) ?? + threads[event.message?.parentId] + ?.firstWhereOrNull((e) => e.id == event.message?.id); final message = event.message!.copyWith( ownReactions: oldMessage?.ownReactions, ); - addMessage(message); + updateMessage(message); if (message.pinned) { _channelState = _channelState.copyWith( @@ -1752,10 +1758,9 @@ class ChannelClientState { _subscriptions.add(_channel.on(EventType.messageDeleted).listen((event) { final message = event.message!; if (event.hardDelete == true) { - //do not decrease reply count here - it is done in _listenMessageUpdated - removeMessage(message, hardDelete: true); + removeMessage(message); } else { - addMessage(message); + updateMessage(message); } })); } @@ -1770,7 +1775,7 @@ class ChannelClientState { final message = event.message!; if (isUpToDate || (message.parentId != null && message.showInChannel != true)) { - addMessage(message); + updateMessage(message); } if (_countMessageAsUnread(message)) { @@ -1780,9 +1785,13 @@ class ChannelClientState { } /// Add a [message] to this [channelState]. - void addMessage(Message message) { + @Deprecated('Use updateMessage instead') + void addMessage(Message message) => updateMessage(message); + + /// Updates the [message] in the state if it exists. Adds it otherwise. + void updateMessage(Message message) { if (message.parentId == null || message.showInChannel == true) { - final newMessages = List.from(_channelState.messages); + final newMessages = [...messages]; final oldIndex = newMessages.indexWhere((m) => m.id == message.id); if (oldIndex != -1) { Message? m; @@ -1811,47 +1820,22 @@ class ChannelClientState { } /// Remove a [message] from this [channelState]. - void removeMessage( - Message message, { - bool hardDelete = false, - bool decreaseReplyCount = false, - }) { + void removeMessage(Message message) { final parentId = message.parentId; - // i.e. it's a thread message - // 1. Remove the thread message - // 2. Reduce total reply count of parent message + // i.e. it's a thread message, Remove it if (parentId != null) { - if (decreaseReplyCount) { - final allMessages = [...messages]; - final parentMessage = allMessages.firstWhereOrNull( - (it) => it.id == parentId, - ); - - // return if message not available in the memory - if (parentMessage == null) return; - final replyCount = parentMessage.replyCount; - // return if reply count is null or zero - if (replyCount == null || replyCount == 0) return; - - addMessage(parentMessage.copyWith(replyCount: replyCount - 1)); - } - updateThreadInfo( + final threadMessages = [...threads[parentId]!]; + return updateThreadInfo( parentId, - threads[parentId]! - ..removeWhere( - (e) => e.id == message.id, - ), + threadMessages..removeWhere((e) => e.id == message.id), ); - } else { - // Remove regular message - final allMessages = [...messages]; - if (hardDelete) { - allMessages.removeWhere((e) => e.id == message.id); - _channelState = _channelState.copyWith(messages: allMessages); - } else if (allMessages.remove(message)) { - _channelState = _channelState.copyWith(messages: allMessages); - } } + + // Remove regular message + final allMessages = [...messages]; + _channelState = _channelState.copyWith( + messages: allMessages..removeWhere((e) => e.id == message.id), + ); } void _listenReadEvents() { diff --git a/packages/stream_chat/lib/src/client/retry_queue.dart b/packages/stream_chat/lib/src/client/retry_queue.dart index 17b019a5..5dfa9aa3 100644 --- a/packages/stream_chat/lib/src/client/retry_queue.dart +++ b/packages/stream_chat/lib/src/client/retry_queue.dart @@ -158,7 +158,7 @@ class RetryQueue { : message.status == MessageSendingStatus.updating ? MessageSendingStatus.failed_update : MessageSendingStatus.failed_delete; - channel.state?.addMessage(message.copyWith(status: newStatus)); + channel.state?.updateMessage(message.copyWith(status: newStatus)); } Future _retryMessage(Message message) async { From e9626dc348ae9ecefe8df29b3667bf95de69de57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Stolin=CC=81ski?= Date: Tue, 22 Feb 2022 09:42:21 +0100 Subject: [PATCH 3/5] fix removing message from thread --- packages/stream_chat/CHANGELOG.md | 2 ++ .../stream_chat/lib/src/client/channel.dart | 18 +++++++++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/packages/stream_chat/CHANGELOG.md b/packages/stream_chat/CHANGELOG.md index 82225b27..59feb777 100644 --- a/packages/stream_chat/CHANGELOG.md +++ b/packages/stream_chat/CHANGELOG.md @@ -10,6 +10,8 @@ - [[#890]](https://github.com/GetStream/stream-chat-flutter/pull/890) Fixed Reactions not updating on thread messages. Thanks [bstolinski](https://github.com/bstolinski). - [[#897]](https://github.com/GetStream/stream-chat-flutter/issues/897) Fixed error type mis-match in `AuthInterceptor`. +- [[#891]](https://github.com/GetStream/stream-chat-flutter/pull/891). Fixed reply counter for parent message not + updating correctly after deleting thread message. ## 3.4.0 diff --git a/packages/stream_chat/lib/src/client/channel.dart b/packages/stream_chat/lib/src/client/channel.dart index c27f3634..2b311061 100644 --- a/packages/stream_chat/lib/src/client/channel.dart +++ b/packages/stream_chat/lib/src/client/channel.dart @@ -1826,11 +1826,19 @@ class ChannelClientState { final parentId = message.parentId; // i.e. it's a thread message, Remove it if (parentId != null) { - final threadMessages = [...threads[parentId]!]; - return updateThreadInfo( - parentId, - threadMessages..removeWhere((e) => e.id == message.id), - ); + if (!threads.containsKey(parentId)) { + return; + } + + final newThreads = Map>.from(threads); + + newThreads[parentId] = [ + ...newThreads[parentId]! + ..removeWhere((e) => e.id == message.id), + ]; + + _threads = newThreads; + return; } // Remove regular message From 52b035cb5fc226c63771bac75b44760116e11b50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Stolin=CC=81ski?= Date: Tue, 22 Feb 2022 09:49:18 +0100 Subject: [PATCH 4/5] fix formatting --- packages/stream_chat/CHANGELOG.md | 2 +- packages/stream_chat/lib/src/client/channel.dart | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/stream_chat/CHANGELOG.md b/packages/stream_chat/CHANGELOG.md index 59feb777..6bbc3b16 100644 --- a/packages/stream_chat/CHANGELOG.md +++ b/packages/stream_chat/CHANGELOG.md @@ -10,7 +10,7 @@ - [[#890]](https://github.com/GetStream/stream-chat-flutter/pull/890) Fixed Reactions not updating on thread messages. Thanks [bstolinski](https://github.com/bstolinski). - [[#897]](https://github.com/GetStream/stream-chat-flutter/issues/897) Fixed error type mis-match in `AuthInterceptor`. -- [[#891]](https://github.com/GetStream/stream-chat-flutter/pull/891). Fixed reply counter for parent message not +- [[#891]](https://github.com/GetStream/stream-chat-flutter/pull/891) Fixed reply counter for parent message not updating correctly after deleting thread message. ## 3.4.0 diff --git a/packages/stream_chat/lib/src/client/channel.dart b/packages/stream_chat/lib/src/client/channel.dart index 2b311061..072bb150 100644 --- a/packages/stream_chat/lib/src/client/channel.dart +++ b/packages/stream_chat/lib/src/client/channel.dart @@ -1833,8 +1833,7 @@ class ChannelClientState { final newThreads = Map>.from(threads); newThreads[parentId] = [ - ...newThreads[parentId]! - ..removeWhere((e) => e.id == message.id), + ...newThreads[parentId]!..removeWhere((e) => e.id == message.id), ]; _threads = newThreads; From 655da0ba33855a667108391fdd89a8f6d2ccfd6e Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Tue, 1 Mar 2022 14:19:20 +0530 Subject: [PATCH 5/5] fix(llc): Remove regular message in case `showInChannel` is true. (#3) * fix(llc): also remove regular message in case `showInChannel` is true. * fix(llc): use `hard` param while removing message in channel state. --- .../stream_chat/lib/src/client/channel.dart | 48 ++++++++++++------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/packages/stream_chat/lib/src/client/channel.dart b/packages/stream_chat/lib/src/client/channel.dart index 072bb150..b0f1bdd0 100644 --- a/packages/stream_chat/lib/src/client/channel.dart +++ b/packages/stream_chat/lib/src/client/channel.dart @@ -643,13 +643,18 @@ class Channel { /// Deletes the [message] from the channel. Future deleteMessage(Message message, {bool? hard}) async { + final hardDelete = hard ?? false; + // Directly deleting the local messages which are not yet sent to server if (message.status == MessageSendingStatus.sending || message.status == MessageSendingStatus.failed) { - state!.updateMessage(message.copyWith( - type: 'deleted', - status: MessageSendingStatus.sent, - )); + state!.deleteMessage( + message.copyWith( + type: 'deleted', + status: MessageSendingStatus.sent, + ), + hardDelete: hardDelete, + ); // Removing the attachments upload completer to stop the `sendMessage` // waiting for attachments to complete. @@ -667,11 +672,14 @@ class Channel { deletedAt: message.deletedAt ?? DateTime.now(), ); - state?.updateMessage(message); + state?.deleteMessage(message, hardDelete: hardDelete); final response = await _client.deleteMessage(message.id, hard: hard); - state?.updateMessage(message.copyWith(status: MessageSendingStatus.sent)); + state?.deleteMessage( + message.copyWith(status: MessageSendingStatus.sent), + hardDelete: hardDelete, + ); return response; } catch (e) { @@ -1826,27 +1834,33 @@ class ChannelClientState { final parentId = message.parentId; // i.e. it's a thread message, Remove it if (parentId != null) { - if (!threads.containsKey(parentId)) { - return; - } + final newThreads = {...threads}; + // Early return in case the thread is not available + if (!newThreads.containsKey(parentId)) return; - final newThreads = Map>.from(threads); + _threads = newThreads + ..update( + parentId, + (messages) => messages..removeWhere((e) => e.id == message.id), + ); - newThreads[parentId] = [ - ...newThreads[parentId]!..removeWhere((e) => e.id == message.id), - ]; - - _threads = newThreads; - return; + // Early return if the thread message is not shown in channel. + if (message.showInChannel == false) return; } - // Remove regular message + // Remove regular message, thread message shown in channel final allMessages = [...messages]; _channelState = _channelState.copyWith( messages: allMessages..removeWhere((e) => e.id == message.id), ); } + /// Removes/Updates the [message] based on the [hardDelete] value. + void deleteMessage(Message message, {bool hardDelete = false}) { + if (hardDelete) return removeMessage(message); + return updateMessage(message); + } + void _listenReadEvents() { if (_channelState.channel?.config.readEvents == false) { return;