From 3c5ab3b6eaf0c3b58dc6a80dd909425fb0898f83 Mon Sep 17 00:00:00 2001 From: Suryadeep Pal Date: Thu, 24 Sep 2026 11:04:20 +0530 Subject: [PATCH] feat: showcase reactions in Flutter UI examples --- lib/showcase/component_showcase_app.dart | 64 ++++++++++++++++++++++++ pubspec.yaml | 4 +- test/component_showcase_test.dart | 6 ++- 3 files changed, 70 insertions(+), 4 deletions(-) diff --git a/lib/showcase/component_showcase_app.dart b/lib/showcase/component_showcase_app.dart index e80f833..5b4ce97 100644 --- a/lib/showcase/component_showcase_app.dart +++ b/lib/showcase/component_showcase_app.dart @@ -109,6 +109,67 @@ class _ShowcasePageState extends State<_ShowcasePage> { // directly: it renders `_window`, the range the host has loaded, the way an // SDK-backed controller renders the page it fetched. late List _history = List.of(_showcaseMessages); + final Map _reactionSummaries = { + _showcaseMessages.last.id: MessageReactionSummary( + messageId: _showcaseMessages.last.id, + reactions: const [ + ReactionSummary(emoji: '❤️', count: 2, reactedByMe: false), + ], + hasMore: false, + ), + }; + + Future _toggleReaction(Message message, String emoji) async { + setState(() { + final previous = + _reactionSummaries[message.id]?.reactions ?? + const []; + final found = previous.where((row) => row.emoji == emoji).firstOrNull; + final count = (found?.count ?? 0) + (found?.reactedByMe == true ? -1 : 1); + _reactionSummaries[message.id] = MessageReactionSummary( + messageId: message.id, + reactions: [ + for (final row in previous) + if (row.emoji != emoji) row, + if (count > 0) + ReactionSummary( + emoji: emoji, + count: count, + reactedByMe: found?.reactedByMe != true, + ), + ], + hasMore: false, + ); + }); + return true; + } + + Future _listReactionUsers( + Message message, + String emoji, + String? cursor, + ) async => ReactionUsersPage( + data: + cursor != null + ? const [] + : [ + if (_reactionSummaries[message.id]?.reactions.any( + (row) => row.emoji == emoji && row.reactedByMe, + ) == + true) + ReactionUser( + userId: _currentUserId, + name: 'Maya Chen', + reactedAt: DateTime.now(), + ), + ReactionUser( + userId: 'alex', + name: 'Alex Rivera', + reactedAt: DateTime.now(), + ), + ], + nextCursor: null, + ); int _windowStart = 0; int _windowEnd = 0; // Whether the rendered window is a jumped-to one rather than the live tail. @@ -360,6 +421,9 @@ class _ShowcasePageState extends State<_ShowcasePage> { onReplyToMessage: _startReply, onCancelReply: _cancelReply, replyPreviewByMessageId: _replyPreviews(), + reactionSummaries: _reactionSummaries, + onToggleReaction: _toggleReaction, + onListReactionUsers: _listReactionUsers, // Jump, highlight and the window around it. onJumpToMessage: _jumpToMessage, highlightedMessageId: _highlightedMessageId, diff --git a/pubspec.yaml b/pubspec.yaml index 734d9c9..bd1b702 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -8,8 +8,8 @@ environment: dependencies: flutter: sdk: flutter - convokit_flutter: ^0.9.0 - convokit_flutter_ui: ^0.9.0 + convokit_flutter: ^0.10.0 + convokit_flutter_ui: ^0.10.0 http: ^1.2.0 dev_dependencies: diff --git a/test/component_showcase_test.dart b/test/component_showcase_test.dart index edf0d23..90e1a87 100644 --- a/test/component_showcase_test.dart +++ b/test/component_showcase_test.dart @@ -715,8 +715,10 @@ void main() { // Scrolling toward the newer edge calls `onLoadNewer`, which extends // the window forwards. - await tester.drag(history, const Offset(0, -300)); - await tester.pumpAndSettle(); + for (var i = 0; i < 4 && find.text(beyondWindow).evaluate().isEmpty; i++) { + await tester.drag(history, const Offset(0, -300)); + await tester.pumpAndSettle(); + } expect(find.text(beyondWindow), findsOneWidget); // Keeping at it reaches the room's newest message, which rejoins the