diff --git a/packages/stream_chat/CHANGELOG.md b/packages/stream_chat/CHANGELOG.md index ee657b1d3..5773faa45 100644 --- a/packages/stream_chat/CHANGELOG.md +++ b/packages/stream_chat/CHANGELOG.md @@ -1,5 +1,9 @@ ## Upcoming +🐞 Fixed + +- [[#2013]](https://github.com/GetStream/stream-chat-flutter/issues/2013) Fix pinned message get duplicated + 🔄 Changed - Updated `freezed_annotation` dependency to `">=2.4.1 <4.0.0"`. diff --git a/packages/stream_chat/lib/src/client/channel.dart b/packages/stream_chat/lib/src/client/channel.dart index f51a8a632..8eb81b5ff 100644 --- a/packages/stream_chat/lib/src/client/channel.dart +++ b/packages/stream_chat/lib/src/client/channel.dart @@ -2676,16 +2676,6 @@ class ChannelClientState { ownReactions: oldMessage?.ownReactions, ); updateMessage(message); - - if (message.pinned) { - final _existingPinnedMessages = _channelState.pinnedMessages ?? []; - _channelState = _channelState.copyWith( - pinnedMessages: [ - ..._existingPinnedMessages, - message, - ], - ); - } })); } diff --git a/packages/stream_chat/test/src/client/channel_test.dart b/packages/stream_chat/test/src/client/channel_test.dart index 0a991a2b7..84de97ed7 100644 --- a/packages/stream_chat/test/src/client/channel_test.dart +++ b/packages/stream_chat/test/src/client/channel_test.dart @@ -3474,6 +3474,166 @@ void main() { }, ); + group( + EventType.messageUpdated, + () { + const channelId = 'test-channel-id'; + const channelType = 'test-channel-type'; + late Channel channel; + + setUp(() { + final channelState = _generateChannelState( + channelId, + channelType, + mockChannelConfig: true, + ownCapabilities: const [ChannelCapability.readEvents], + ); + + channel = Channel.fromState(client, channelState); + }); + + tearDown(() => channel.dispose()); + + Event createUpdateMessageEvent(Message message) { + return Event( + cid: channel.cid, + type: EventType.messageUpdated, + message: message, + ); + } + + test( + "should update 'channel.state.pinnedMessages' and should add message to pinned messages only once if updatedMessage.pinned is true", + () async { + const messageId = 'test-message-id'; + final message = Message( + id: messageId, + user: client.state.currentUser, + pinned: true, + ); + + final newMessageEvent = createUpdateMessageEvent(message); + client.addEvent(newMessageEvent); + + // Wait for the event to get processed + await Future.delayed(Duration.zero); + + expect(channel.state?.pinnedMessages.length, equals(1)); + expect(channel.state?.pinnedMessages.first.id, equals(messageId)); + }, + ); + + test( + 'should update pinned message itself if updatedMessage.pinned is true and message is already pinned', + () async { + const messageId = 'test-message-id'; + const oldText = 'Old text'; + const newText = 'New text'; + final message = Message( + id: messageId, + user: client.state.currentUser, + text: oldText, + pinned: true, + ); + + final firstUpdateEvent = createUpdateMessageEvent(message); + client.addEvent(firstUpdateEvent); + + // Wait for the first event to get processed + await Future.delayed(Duration.zero); + + expect(channel.state?.pinnedMessages.length, equals(1)); + expect(channel.state?.pinnedMessages.first.id, equals(messageId)); + expect(channel.state?.pinnedMessages.first.text, equals(oldText)); + + final updatedMessage = message.copyWith(text: newText); + final secondUpdateEvent = createUpdateMessageEvent(updatedMessage); + client.addEvent(secondUpdateEvent); + + // Wait for the second event to get processed + await Future.delayed(Duration.zero); + + expect(channel.state?.pinnedMessages.length, equals(1)); + expect(channel.state?.pinnedMessages.first.id, equals(messageId)); + expect(channel.state?.pinnedMessages.first.text, equals(newText)); + }, + ); + + test( + "should update 'channel.state.pinnedMessages' and should add message to pinned messages " + 'and not unpin previous pinned message if updatedMessage.pinned is true and there is already another pinned message', + () async { + const firstMessageId = 'first-test-message-id'; + const secondMessageId = 'second-test-message-id'; + final firstMessage = Message( + id: firstMessageId, + user: client.state.currentUser, + pinned: true, + ); + final secondMessage = firstMessage.copyWith(id: secondMessageId); + + final firstUpdateEvent = createUpdateMessageEvent(firstMessage); + client.addEvent(firstUpdateEvent); + + // Wait for the first event to get processed + await Future.delayed(Duration.zero); + + expect(channel.state?.pinnedMessages.length, equals(1)); + expect( + channel.state?.pinnedMessages.first.id, + equals(firstMessageId), + ); + + final secondUpdateEvent = createUpdateMessageEvent(secondMessage); + client.addEvent(secondUpdateEvent); + + // Wait for the second event to get processed + await Future.delayed(Duration.zero); + + expect(channel.state?.pinnedMessages.length, equals(2)); + expect( + channel.state?.pinnedMessages.first.id, + equals(firstMessageId), + ); + expect( + channel.state?.pinnedMessages[1].id, + equals(secondMessageId), + ); + }, + ); + + test( + "should update 'channel.state.pinnedMessages' and should remove message from pinned messages if updatedMessage.pinned is false", + () async { + const messageId = 'test-message-id'; + final pinnedMessage = Message( + id: messageId, + user: client.state.currentUser, + pinned: true, + ); + + final pinEvent = createUpdateMessageEvent(pinnedMessage); + client.addEvent(pinEvent); + + // Wait for the pin event to get processed + await Future.delayed(Duration.zero); + + expect(channel.state?.pinnedMessages.length, equals(1)); + expect(channel.state?.pinnedMessages.first.id, equals(messageId)); + + final unpinnedMessage = pinnedMessage.copyWith(pinned: false); + final unpinEvent = createUpdateMessageEvent(unpinnedMessage); + client.addEvent(unpinEvent); + + // Wait for the unpin event to get processed + await Future.delayed(Duration.zero); + + expect(channel.state?.pinnedMessages, isEmpty); + }, + ); + }, + ); + group('Member Events', () { const channelId = 'test-channel-id'; const channelType = 'test-channel-type';