diff --git a/packages/stream_chat/CHANGELOG.md b/packages/stream_chat/CHANGELOG.md index a063de46f..83018bf1b 100644 --- a/packages/stream_chat/CHANGELOG.md +++ b/packages/stream_chat/CHANGELOG.md @@ -22,6 +22,8 @@ - Fixed `Channel.name`/`image`/`extraData` setters throwing after a *failed* initialization; they now only throw once the channel is successfully initialized. - Fixed `Channel.initialized` staying errored after a failed init; it now reflects a subsequent successful (re)initialization. - Fixed a `StateError` (`Cannot add new events after calling close`) thrown when the client is disposed while a reconnect recovery is still in flight. +- Fixed `Message.deleteMyReaction` dropping an entire reaction group when its summed scores reached zero even though other users' reactions kept the count positive; the group is now retained as long as its count stays above zero. +- Fixed reaction groups synthesized from legacy `reaction_counts`/`reaction_scores` payloads being discarded at parse time when their score total was zero or negative despite a positive count. ## 10.2.0 diff --git a/packages/stream_chat/lib/src/core/models/message.dart b/packages/stream_chat/lib/src/core/models/message.dart index 8b2707c80..025e66621 100644 --- a/packages/stream_chat/lib/src/core/models/message.dart +++ b/packages/stream_chat/lib/src/core/models/message.dart @@ -172,7 +172,8 @@ class Message extends Equatable implements ComparableFieldProvider { final count = reactionCounts?[type] ?? 0; final sumScores = reactionScores?[type] ?? 0; - if (count == 0 || sumScores == 0) continue; + // Keep the group while count is positive; score may be zero or negative. + if (count <= 0) continue; final now = DateTime.timestamp(); groups[type] = { 'count': count, @@ -1025,11 +1026,11 @@ extension MessageReactionHelper on Message { final group = reactionGroups.remove(type); if (group == null) continue; - // Update the reaction group. + // Keep the group while count is positive; score may be zero or negative. final updatedCount = group.count - 1; final updatedSumScores = group.sumScores - reaction.score; - if (updatedCount > 0 && updatedSumScores > 0) { + if (updatedCount > 0) { reactionGroups[type] = group.copyWith( count: updatedCount, sumScores: updatedSumScores, diff --git a/packages/stream_chat/test/src/core/models/message_reaction_helper_test.dart b/packages/stream_chat/test/src/core/models/message_reaction_helper_test.dart index 0aa5ea556..de1960eb6 100644 --- a/packages/stream_chat/test/src/core/models/message_reaction_helper_test.dart +++ b/packages/stream_chat/test/src/core/models/message_reaction_helper_test.dart @@ -361,6 +361,51 @@ void main() { // Should have updated the reaction group counts expect(updatedMessage.reactionGroups, isEmpty); }); + + test('should keep reaction group with non-positive score sum while ' + 'count remains positive', () { + // A positively-scored own reaction and a negatively-scored reaction + // from another user net the group score to zero while count is 2. + final ownReaction = Reaction( + type: 'like', + score: 1, + user: testUser, + userId: testUser.id, + messageId: emptyMessage.id, + ); + + final otherUser = User(id: 'other-user-id'); + final otherReaction = Reaction( + type: 'like', + score: -1, + user: otherUser, + userId: otherUser.id, + messageId: emptyMessage.id, + ); + + final messageWithReactions = emptyMessage.copyWith( + ownReactions: [ownReaction], + latestReactions: [ownReaction, otherReaction], + reactionGroups: { + 'like': ReactionGroup( + count: 2, + sumScores: 0, + firstReactionAt: ownReaction.createdAt, + lastReactionAt: otherReaction.createdAt, + ), + }, + ); + + final updatedMessage = messageWithReactions.deleteMyReaction(); + + // The group must survive because another user's reaction remains, even + // though the remaining summed score is negative. + expect(updatedMessage.reactionGroups!.length, 1); + expect(updatedMessage.reactionGroups!['like']!.count, 1); + expect(updatedMessage.reactionGroups!['like']!.sumScores, -1); + expect(updatedMessage.latestReactions!.length, 1); + expect(updatedMessage.latestReactions!.first.userId, otherUser.id); + }); }); }); } diff --git a/packages/stream_chat/test/src/core/models/message_test.dart b/packages/stream_chat/test/src/core/models/message_test.dart index 5185fd42e..e2b8ef14a 100644 --- a/packages/stream_chat/test/src/core/models/message_test.dart +++ b/packages/stream_chat/test/src/core/models/message_test.dart @@ -520,6 +520,25 @@ void main() { expect(message.reactionGroups!['love']!.sumScores, 5); }, ); + + test( + 'synthesizes groups from legacy reaction_counts/reaction_scores ' + 'even when the score total is zero or negative', + () { + final message = Message.fromJson(const { + 'reaction_counts': {'like': 2, 'dislike': 3}, + 'reaction_scores': {'like': 0, 'dislike': -3}, + }); + + // Both groups must be retained because their count is positive, even + // though the summed scores are zero and negative respectively. + expect(message.reactionGroups, isNotNull); + expect(message.reactionGroups!['like']!.count, 2); + expect(message.reactionGroups!['like']!.sumScores, 0); + expect(message.reactionGroups!['dislike']!.count, 3); + expect(message.reactionGroups!['dislike']!.sumScores, -3); + }, + ); }); });