-
Notifications
You must be signed in to change notification settings - Fork 357
refactor!(ui): add support for customising reaction picker #2248
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: v10.0.0
Are you sure you want to change the base?
Conversation
…eat/reactions-v2 # Conflicts: # packages/stream_chat_flutter/lib/src/message_modal/message_actions_modal.dart # packages/stream_chat_flutter/lib/src/message_modal/message_reactions_modal.dart # packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart
This reverts commit 9551f18.
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## v10.0.0 #2248 +/- ##
==========================================
Coverage ? 63.44%
==========================================
Files ? 399
Lines ? 25104
Branches ? 0
==========================================
Hits ? 15927
Misses ? 9177
Partials ? 0 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Co-authored-by: xsahil03x <[email protected]>
# Conflicts: # packages/stream_chat_flutter/lib/src/fullscreen_media/full_screen_media_desktop.dart # packages/stream_chat_flutter/lib/src/message_action/message_action_type.dart # packages/stream_chat_flutter/lib/src/message_modal/message_actions_modal.dart # packages/stream_chat_flutter/lib/src/message_modal/message_modal.dart # packages/stream_chat_flutter/lib/src/message_modal/message_reactions_modal.dart # packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart # packages/stream_chat_flutter/lib/src/message_widget/reactions/reaction_picker.dart # packages/stream_chat_flutter/lib/src/message_widget/reactions/reaction_picker_icon_list.dart # packages/stream_chat_flutter/lib/src/message_widget/reactions/reactions_align.dart # packages/stream_chat_flutter/test/src/message_modal/message_reactions_modal_test.dart
Co-authored-by: xsahil03x <[email protected]>
# Conflicts: # packages/stream_chat_flutter/test/src/message_modal/goldens/ci/stream_message_reactions_modal_dark.png # packages/stream_chat_flutter/test/src/message_modal/goldens/ci/stream_message_reactions_modal_light.png # packages/stream_chat_flutter/test/src/message_modal/goldens/ci/stream_message_reactions_modal_reversed_dark.png # packages/stream_chat_flutter/test/src/message_modal/goldens/ci/stream_message_reactions_modal_reversed_light.png # packages/stream_chat_flutter/test/src/message_widget/reactions/goldens/ci/reaction_icon_button_selected_light.png # packages/stream_chat_flutter/test/src/message_widget/reactions/goldens/ci/reaction_icon_button_unselected_light.png # packages/stream_chat_flutter/test/src/message_widget/reactions/goldens/ci/reaction_picker_icon_list_light.png # packages/stream_chat_flutter/test/src/message_widget/reactions/goldens/ci/reaction_picker_icon_list_selected_light.png # packages/stream_chat_flutter/test/src/message_widget/reactions/goldens/ci/stream_reaction_picker_light.png # packages/stream_chat_flutter/test/src/message_widget/reactions/goldens/ci/stream_reaction_picker_selected_light.png # packages/stream_chat_flutter/test/src/misc/goldens/ci/reaction_bubble_2.png # packages/stream_chat_flutter/test/src/misc/goldens/ci/reaction_bubble_3_dark.png # packages/stream_chat_flutter/test/src/misc/goldens/ci/reaction_bubble_3_light.png # packages/stream_chat_flutter/test/src/misc/goldens/ci/reaction_bubble_like_dark.png # packages/stream_chat_flutter/test/src/misc/goldens/ci/reaction_bubble_like_light.png
# Conflicts: # packages/stream_chat_flutter/lib/src/message_action/message_action_type.dart # packages/stream_chat_flutter/lib/src/message_modal/message_actions_modal.dart # packages/stream_chat_flutter/lib/src/message_modal/message_modal.dart # packages/stream_chat_flutter/lib/src/message_modal/message_reactions_modal.dart # packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart # packages/stream_chat_flutter/lib/src/message_widget/message_widget_content_components.dart # packages/stream_chat_flutter/lib/src/message_widget/reactions/reaction_picker.dart # packages/stream_chat_flutter/lib/src/message_widget/reactions/reactions_align.dart # packages/stream_chat_flutter/lib/src/reactions/reaction_picker_icon_list.dart # packages/stream_chat_flutter/test/src/message_modal/goldens/ci/stream_message_reactions_modal_dark.png # packages/stream_chat_flutter/test/src/message_modal/goldens/ci/stream_message_reactions_modal_light.png # packages/stream_chat_flutter/test/src/message_modal/goldens/ci/stream_message_reactions_modal_reversed_dark.png # packages/stream_chat_flutter/test/src/message_modal/goldens/ci/stream_message_reactions_modal_reversed_light.png # packages/stream_chat_flutter/test/src/message_modal/message_reactions_modal_test.dart # packages/stream_chat_flutter/test/src/misc/goldens/ci/reaction_bubble_2.png # packages/stream_chat_flutter/test/src/misc/goldens/ci/reaction_bubble_3_dark.png # packages/stream_chat_flutter/test/src/misc/goldens/ci/reaction_bubble_3_light.png # packages/stream_chat_flutter/test/src/misc/goldens/ci/reaction_bubble_like_dark.png # packages/stream_chat_flutter/test/src/misc/goldens/ci/reaction_bubble_like_light.png
9718aae
to
3a60f29
Compare
@coderabbitai review |
✅ Actions performedReview triggered.
|
WalkthroughThis update introduces enhanced customization for reaction pickers in the Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant StreamMessageWidget
participant reactionPickerBuilder (custom or default)
participant Modal (Actions/Reactions)
User->>StreamMessageWidget: Long press / context menu on message
StreamMessageWidget->>Modal: Show actions/reactions modal with reactionPickerBuilder
Modal->>reactionPickerBuilder: Build custom reaction picker UI
reactionPickerBuilder->>Modal: Return picker widget
User->>reactionPickerBuilder: Select reaction
reactionPickerBuilder->>Modal: onReactionPicked callback
Modal->>StreamMessageWidget: Update message with new reaction
Assessment against linked issues
Poem
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🔭 Outside diff range comments (3)
sample_app/lib/app.dart (1)
448-472
:⚠️ Potential issueFix observer inconsistency with router caching.
The router caching optimization introduces a potential issue: the
LocalNotificationObserver
is recreated on every_setupRouter()
call, but the cached router retains the original observer reference. This means navigation events may not be properly observed after the first router creation.Consider one of these solutions:
Solution 1: Cache the observer along with the router
GoRouter _setupRouter() { + if (router != null) { + return router!; + } + if (localNotificationObserver != null) { localNotificationObserver!.dispose(); } localNotificationObserver = LocalNotificationObserver( _initNotifier.initData!.client, _navigatorKey); return router ??= GoRouter( refreshListenable: _initNotifier, initialLocation: Routes.CHANNEL_LIST_PAGE.path, navigatorKey: _navigatorKey, observers: [localNotificationObserver!], redirect: (context, state) { // ... rest of the method }, routes: appRoutes, ); }Solution 2: Reset router when observer needs to be recreated
GoRouter _setupRouter() { if (localNotificationObserver != null) { localNotificationObserver!.dispose(); + router = null; // Force router recreation when observer changes } localNotificationObserver = LocalNotificationObserver( _initNotifier.initData!.client, _navigatorKey); return router ??= GoRouter( // ... rest unchanged ); }
packages/stream_chat_flutter/lib/src/stream_chat_configuration.dart (1)
123-136
: 🛠️ Refactor suggestionGuard against accidental mutation of the default reaction icon list
StreamReactionIcon.defaultReactions
is now used as the global default.
Because lists are mutable, any downstream code could inadvertently add / remove
entries and thereby affect the whole application at runtime.A small defensive copy (wrapped in
List.unmodifiable
) makes the intent
explicit and protects against hard-to-trace bugs.- reactionIcons: reactionIcons ?? StreamReactionIcon.defaultReactions, + reactionIcons: + List.unmodifiable(reactionIcons ?? StreamReactionIcon.defaultReactions),packages/stream_chat_flutter/lib/src/reactions/picker/reaction_picker_icon_list.dart (1)
125-145
: 🛠️ Refactor suggestionRe-initialising animations only when the length changes can miss updates
didUpdateWidget
recreates the_iconAnimations
list only whenreactionIcons.length
changes.
If a caller swaps the list with icons of the same length (e.g. reordered or replaced with theme-specific builders), the old animations persist and are now out of sync with the new icons.-if (oldWidget.reactionIcons.length != widget.reactionIcons.length) { +if (!const ListEquality().equals( + oldWidget.reactionIcons.map((e) => e.type).toList(), + widget.reactionIcons.map((e) => e.type).toList(), + )) {Using
collection
’sListEquality
(already imported) keeps the cost low and guarantees the animations align with the actual icon list.
🧹 Nitpick comments (8)
packages/stream_chat_flutter/lib/src/reactions/user_reactions.dart (1)
59-66
: Consider adding empty state handling.While the current implementation handles null reactions gracefully, consider adding explicit handling for when
message.latestReactions
is empty to provide better user feedback.You could add an empty state message:
children: [ if (message.latestReactions?.isEmpty ?? true) Text( context.translations.noReactionsLabel, style: textTheme.body, textAlign: TextAlign.center, ) else ...message.latestReactions!.map((reaction) { return _UserReactionItem( key: Key('${reaction.userId}-${reaction.type}'), reaction: reaction, onTap: onUserAvatarTap, ); }), ],packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart (1)
1477-1513
: Excellent refactor using modern Dart pattern matching!The refactor from nested
if-else
statements to aswitch
expression with tuple pattern matching significantly improves code readability and maintainability. The logic is well-structured with clear cases:
- Case 1: Uses provided
onThreadTap
callback- Case 2: Creates default navigation with
StreamChatConfiguration
wrapper- Case 3: Falls back to
null
The addition of
StreamChatConfiguration
wrapper in Case 2 (lines 1491-1505) is particularly important as it ensures reaction icons and other configuration are properly propagated to the thread page, which aligns with the PR's reaction picker customization objectives.Consider adding tests for the new switch expression cases to improve coverage, though this refactor preserves existing behavior and the logic is straightforward.
🧰 Tools
🪛 GitHub Check: codecov/patch
[warning] 1481-1482: packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart#L1481-L1482
Added lines #L1481 - L1482 were not covered by tests
[warning] 1484-1484: packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart#L1484
Added line #L1484 was not covered by tests
[warning] 1490-1491: packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart#L1490-L1491
Added lines #L1490 - L1491 were not covered by tests
[warning] 1494-1497: packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart#L1494-L1497
Added lines #L1494 - L1497 were not covered by tests
[warning] 1499-1500: packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart#L1499-L1500
Added lines #L1499 - L1500 were not covered by tests
[warning] 1502-1502: packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart#L1502
Added line #L1502 was not covered by tests
[warning] 1507-1508: packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart#L1507-L1508
Added lines #L1507 - L1508 were not covered by testspackages/stream_chat_flutter/lib/src/stream_chat_configuration.dart (1)
180-189
: Nit: preferconst
where possible
Center
andStreamGradientAvatar
(with only compile-time literals) can be
markedconst
, enabling a tiny performance gain and clearer intent.- return Center( - child: StreamGradientAvatar( + return const Center( + child: StreamGradientAvatar(packages/stream_chat_flutter/lib/src/reactions/reaction_bubble.dart (1)
108-118
: Avoid repeated O(N²) look-ups when rendering many reactions
firstWhere
is executed once per reaction, scanning the full
reactionIcons
list each time.
With many, custom reaction sets this becomes unnecessarily expensive.Consider building a
Map<String, StreamReactionIcon>
once and doing O(1)
look-ups inside the loop.- final reactionIcon = reactionIcons.firstWhere( - (it) => it.type == reaction.type, - orElse: () => const StreamReactionIcon.unknown(), - ); + final reactionIcon = + _iconCache.putIfAbsent(reaction.type, () { + return reactionIcons.firstWhere( + (it) => it.type == reaction.type, + orElse: () => const StreamReactionIcon.unknown(), + ); + });Where
_iconCache
is a smallfinal _iconCache = <String, StreamReactionIcon>{};
kept in the widget (or passed in).This keeps the hot build path lean without altering behaviour.
packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart (1)
885-891
: Context-menu reaction picker lacks width constraintsOn desktop/web the picker widget is inserted directly into the context-menu
column. A custom picker with a fixed width could overflow or push the standard
action items off-screen.Consider wrapping the builder result in a
ConstrainedBox
or allowing the menu
to scroll.- widget.reactionPickerBuilder( + ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 280), + child: widget.reactionPickerBuilder( context, message, (reaction) => onActionTap( SelectReaction(message: message, reaction: reaction), ), ), + ),🧰 Tools
🪛 GitHub Check: codecov/patch
[warning] 885-885: packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart#L885
Added line #L885 was not covered by tests
[warning] 888-888: packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart#L888
Added line #L888 was not covered by testspackages/stream_chat_flutter/lib/src/reactions/picker/reaction_picker_icon_list.dart (3)
163-170
: Avoid O(m × n) look-ups when determining selected icons
Inside themapIndexed
loop we rebuildownReactions
andfirstWhereOrNull
for every icon. For long reaction sets this quickly becomes quadratic.- ...widget.reactionIcons.mapIndexed((index, icon) { + final ownTypes = { + for (final r in widget.message.ownReactions ?? const []) r.type + }; + ...widget.reactionIcons.mapIndexed((index, icon) { bool reactionCheck(Reaction reaction) => reaction.type == icon.type; - final ownReactions = [...?widget.message.ownReactions]; - final reaction = ownReactions.firstWhereOrNull(reactionCheck); + final isSelected = ownTypes.contains(icon.type);This flattens the complexity to O(m + n) and eliminates repeated list allocations.
Also applies to: 166-168
262-266
: Hard-coded size limits customisability
icon.builder(context, icon.isSelected, 24)
locks the icon to24 dp
. Consider forwarding theIconButton.iconSize
or exposing asize
property onReactionPickerIcon
so theme builders can resize consistently with text scale factors.
270-276
: Minor: the arrow-function needn’treturn
avoid
result
onPressed
ultimately expects avoid
callback, yet the closure returns the (void) result ofonPressed(type)
. Omitting thereturn
avoids an unnecessary expression and silences the “value of type ‘void’ used” lint in strict analysis.-final onPressed? => () { - final type = icon.type; - return onPressed(type); - }, +final onPressed? => () => onPressed(icon.type),
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (33)
packages/stream_chat_flutter/test/src/message_modal/goldens/ci/stream_message_reactions_modal_dark.png
is excluded by!**/*.png
packages/stream_chat_flutter/test/src/message_modal/goldens/ci/stream_message_reactions_modal_light.png
is excluded by!**/*.png
packages/stream_chat_flutter/test/src/message_modal/goldens/ci/stream_message_reactions_modal_reversed_dark.png
is excluded by!**/*.png
packages/stream_chat_flutter/test/src/message_modal/goldens/ci/stream_message_reactions_modal_reversed_light.png
is excluded by!**/*.png
packages/stream_chat_flutter/test/src/message_widget/reactions/goldens/ci/reaction_icon_button_selected_dark.png
is excluded by!**/*.png
packages/stream_chat_flutter/test/src/message_widget/reactions/goldens/ci/reaction_icon_button_selected_light.png
is excluded by!**/*.png
packages/stream_chat_flutter/test/src/message_widget/reactions/goldens/ci/reaction_icon_button_unselected_dark.png
is excluded by!**/*.png
packages/stream_chat_flutter/test/src/message_widget/reactions/goldens/ci/reaction_icon_button_unselected_light.png
is excluded by!**/*.png
packages/stream_chat_flutter/test/src/message_widget/reactions/goldens/ci/reaction_picker_icon_list_dark.png
is excluded by!**/*.png
packages/stream_chat_flutter/test/src/message_widget/reactions/goldens/ci/reaction_picker_icon_list_light.png
is excluded by!**/*.png
packages/stream_chat_flutter/test/src/message_widget/reactions/goldens/ci/reaction_picker_icon_list_selected_dark.png
is excluded by!**/*.png
packages/stream_chat_flutter/test/src/message_widget/reactions/goldens/ci/reaction_picker_icon_list_selected_light.png
is excluded by!**/*.png
packages/stream_chat_flutter/test/src/message_widget/reactions/goldens/ci/stream_reaction_picker_dark.png
is excluded by!**/*.png
packages/stream_chat_flutter/test/src/message_widget/reactions/goldens/ci/stream_reaction_picker_light.png
is excluded by!**/*.png
packages/stream_chat_flutter/test/src/message_widget/reactions/goldens/ci/stream_reaction_picker_selected_dark.png
is excluded by!**/*.png
packages/stream_chat_flutter/test/src/message_widget/reactions/goldens/ci/stream_reaction_picker_selected_light.png
is excluded by!**/*.png
packages/stream_chat_flutter/test/src/misc/goldens/ci/reaction_bubble_2.png
is excluded by!**/*.png
packages/stream_chat_flutter/test/src/misc/goldens/ci/reaction_bubble_3_dark.png
is excluded by!**/*.png
packages/stream_chat_flutter/test/src/misc/goldens/ci/reaction_bubble_3_light.png
is excluded by!**/*.png
packages/stream_chat_flutter/test/src/misc/goldens/ci/reaction_bubble_like_dark.png
is excluded by!**/*.png
packages/stream_chat_flutter/test/src/misc/goldens/ci/reaction_bubble_like_light.png
is excluded by!**/*.png
packages/stream_chat_flutter/test/src/reactions/goldens/ci/reaction_icon_button_selected_dark.png
is excluded by!**/*.png
packages/stream_chat_flutter/test/src/reactions/goldens/ci/reaction_icon_button_selected_light.png
is excluded by!**/*.png
packages/stream_chat_flutter/test/src/reactions/goldens/ci/reaction_icon_button_unselected_dark.png
is excluded by!**/*.png
packages/stream_chat_flutter/test/src/reactions/goldens/ci/reaction_icon_button_unselected_light.png
is excluded by!**/*.png
packages/stream_chat_flutter/test/src/reactions/goldens/ci/reaction_picker_icon_list_dark.png
is excluded by!**/*.png
packages/stream_chat_flutter/test/src/reactions/goldens/ci/reaction_picker_icon_list_light.png
is excluded by!**/*.png
packages/stream_chat_flutter/test/src/reactions/goldens/ci/reaction_picker_icon_list_selected_dark.png
is excluded by!**/*.png
packages/stream_chat_flutter/test/src/reactions/goldens/ci/reaction_picker_icon_list_selected_light.png
is excluded by!**/*.png
packages/stream_chat_flutter/test/src/reactions/goldens/ci/stream_reaction_picker_dark.png
is excluded by!**/*.png
packages/stream_chat_flutter/test/src/reactions/goldens/ci/stream_reaction_picker_light.png
is excluded by!**/*.png
packages/stream_chat_flutter/test/src/reactions/goldens/ci/stream_reaction_picker_selected_dark.png
is excluded by!**/*.png
packages/stream_chat_flutter/test/src/reactions/goldens/ci/stream_reaction_picker_selected_light.png
is excluded by!**/*.png
📒 Files selected for processing (26)
packages/stream_chat_flutter/CHANGELOG.md
(1 hunks)packages/stream_chat_flutter/lib/platform_widget_builder/src/platform_widget_builder.dart
(2 hunks)packages/stream_chat_flutter/lib/src/message_action/message_action_type.dart
(2 hunks)packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart
(1 hunks)packages/stream_chat_flutter/lib/src/message_modal/message_actions_modal.dart
(4 hunks)packages/stream_chat_flutter/lib/src/message_modal/message_modal.dart
(1 hunks)packages/stream_chat_flutter/lib/src/message_modal/message_reactions_modal.dart
(5 hunks)packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart
(8 hunks)packages/stream_chat_flutter/lib/src/message_widget/message_widget_content.dart
(2 hunks)packages/stream_chat_flutter/lib/src/message_widget/message_widget_content_components.dart
(0 hunks)packages/stream_chat_flutter/lib/src/message_widget/reactions/reaction_indicator.dart
(0 hunks)packages/stream_chat_flutter/lib/src/message_widget/reactions/reactions_card.dart
(0 hunks)packages/stream_chat_flutter/lib/src/misc/reaction_icon.dart
(2 hunks)packages/stream_chat_flutter/lib/src/reactions/desktop_reactions_builder.dart
(1 hunks)packages/stream_chat_flutter/lib/src/reactions/picker/reaction_picker.dart
(2 hunks)packages/stream_chat_flutter/lib/src/reactions/picker/reaction_picker_icon_list.dart
(5 hunks)packages/stream_chat_flutter/lib/src/reactions/reaction_bubble.dart
(2 hunks)packages/stream_chat_flutter/lib/src/reactions/reaction_indicator.dart
(1 hunks)packages/stream_chat_flutter/lib/src/reactions/user_reactions.dart
(1 hunks)packages/stream_chat_flutter/lib/src/stream_chat_configuration.dart
(2 hunks)packages/stream_chat_flutter/lib/stream_chat_flutter.dart
(1 hunks)packages/stream_chat_flutter/test/platform_widget_builder/platform_widget_builder_test.dart
(1 hunks)packages/stream_chat_flutter/test/src/message_modal/message_reactions_modal_test.dart
(2 hunks)packages/stream_chat_flutter/test/src/reactions/reaction_picker_icon_list_test.dart
(5 hunks)packages/stream_chat_flutter/test/src/reactions/reaction_picker_test.dart
(1 hunks)sample_app/lib/app.dart
(2 hunks)
💤 Files with no reviewable changes (3)
- packages/stream_chat_flutter/lib/src/message_widget/message_widget_content_components.dart
- packages/stream_chat_flutter/lib/src/message_widget/reactions/reaction_indicator.dart
- packages/stream_chat_flutter/lib/src/message_widget/reactions/reactions_card.dart
🧰 Additional context used
🪛 LanguageTool
packages/stream_chat_flutter/CHANGELOG.md
[style] ~50-~50: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...nable custom reaction picker widgets. - Added StreamReactionIcon.defaultReactions
p...
(ENGLISH_WORD_REPEAT_BEGINNING_RULE)
🪛 GitHub Check: codecov/patch
packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart
[warning] 1481-1482: packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart#L1481-L1482
Added lines #L1481 - L1482 were not covered by tests
[warning] 1484-1484: packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart#L1484
Added line #L1484 was not covered by tests
[warning] 1490-1491: packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart#L1490-L1491
Added lines #L1490 - L1491 were not covered by tests
[warning] 1494-1497: packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart#L1494-L1497
Added lines #L1494 - L1497 were not covered by tests
[warning] 1499-1500: packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart#L1499-L1500
Added lines #L1499 - L1500 were not covered by tests
[warning] 1502-1502: packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart#L1502
Added line #L1502 was not covered by tests
[warning] 1507-1508: packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart#L1507-L1508
Added lines #L1507 - L1508 were not covered by tests
packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart
[warning] 547-547: packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart#L547
Added line #L547 was not covered by tests
[warning] 679-679: packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart#L679
Added line #L679 was not covered by tests
[warning] 683-683: packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart#L683
Added line #L683 was not covered by tests
[warning] 688-688: packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart#L688
Added line #L688 was not covered by tests
[warning] 693-695: packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart#L693-L695
Added lines #L693 - L695 were not covered by tests
[warning] 699-699: packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart#L699
Added line #L699 was not covered by tests
[warning] 701-701: packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart#L701
Added line #L701 was not covered by tests
[warning] 703-704: packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart#L703-L704
Added lines #L703 - L704 were not covered by tests
[warning] 752-756: packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart#L752-L756
Added lines #L752 - L756 were not covered by tests
[warning] 885-885: packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart#L885
Added line #L885 was not covered by tests
[warning] 888-888: packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart#L888
Added line #L888 was not covered by tests
[warning] 908-910: packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart#L908-L910
Added lines #L908 - L910 were not covered by tests
[warning] 916-916: packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart#L916
Added line #L916 was not covered by tests
[warning] 919-920: packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart#L919-L920
Added lines #L919 - L920 were not covered by tests
[warning] 922-923: packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart#L922-L923
Added lines #L922 - L923 were not covered by tests
[warning] 925-925: packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart#L925
Added line #L925 was not covered by tests
[warning] 927-927: packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart#L927
Added line #L927 was not covered by tests
[warning] 929-929: packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart#L929
Added line #L929 was not covered by tests
[warning] 931-931: packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart#L931
Added line #L931 was not covered by tests
[warning] 940-942: packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart#L940-L942
Added lines #L940 - L942 were not covered by tests
[warning] 1035-1035: packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart#L1035
Added line #L1035 was not covered by tests
[warning] 1038-1039: packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart#L1038-L1039
Added lines #L1038 - L1039 were not covered by tests
[warning] 1041-1041: packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart#L1041
Added line #L1041 was not covered by tests
🔇 Additional comments (34)
packages/stream_chat_flutter/lib/src/message_action/message_action_type.dart (2)
75-78
: Consistent multi-line constructor formatting for MuteUser
This change brings theMuteUser
constructor in line with the existing style (multi-line params with a trailing comma), improving readability and diff clarity.
87-90
: Consistent multi-line constructor formatting for UnmuteUser
Similarly, updatingUnmuteUser
to use the same parameter-format style ensures consistency across message action types.sample_app/lib/app.dart (1)
437-437
: Good addition of router caching field.The nullable
GoRouter? router
field enables router caching to improve performance by avoiding unnecessary recreations.packages/stream_chat_flutter/lib/platform_widget_builder/src/platform_widget_builder.dart (3)
28-28
: LGTM: Well-designed parameter addition.The
desktopOrWeb
parameter provides a clean way to specify a unified widget for both desktop and web platforms while maintaining backward compatibility.
43-47
: Excellent documentation and design.The documentation clearly explains the precedence behavior, ensuring developers understand that explicit
desktop
orweb
builders will override thedesktopOrWeb
fallback.
51-58
: Clean fallback implementation.The null-aware operator usage provides clean fallback logic that correctly prioritizes specific platform builders over the unified
desktopOrWeb
builder.packages/stream_chat_flutter/lib/src/message_modal/message_modal.dart (1)
144-144
: Good layout improvement.Wrapping the content in
Flexible
prevents potential overflow issues and allows the modal content to adapt flexibly to available space within theColumn
layout.packages/stream_chat_flutter/test/src/reactions/reaction_picker_test.dart (1)
186-186
: Consistent theming update.The background color change from
appBg
tooverlay
aligns the test environment with the modal/overlay theming used in the actual application, ensuring visual consistency.packages/stream_chat_flutter/lib/src/reactions/desktop_reactions_builder.dart (1)
103-103
: Good component modernization.The replacement of
ReactionsCard
withStreamUserReactions
simplifies the API by removing explicitcurrentUser
andmessageTheme
parameters, suggesting better internal encapsulation and cleaner component interfaces.packages/stream_chat_flutter/test/src/message_modal/message_reactions_modal_test.dart (1)
70-70
: LGTM! Test updates correctly reflect widget refactoring.The replacement of
ReactionsCard
withStreamUserReactions
in test expectations properly aligns with the main codebase refactoring described in the AI summary.Also applies to: 96-96
packages/stream_chat_flutter/test/platform_widget_builder/platform_widget_builder_test.dart (1)
75-98
: Good test coverage for the new desktopOrWeb feature.The test properly verifies that
PlatformWidgetBuilder
correctly handles the newdesktopOrWeb
builder parameter across multiple desktop platforms and web (using the Fuchsia hack). The test structure is consistent with existing tests.packages/stream_chat_flutter/test/src/reactions/reaction_picker_icon_list_test.dart (2)
57-63
: Well-implemented test updates for ReactionPickerIcon abstraction.The changes correctly implement the new
ReactionPickerIcon
abstraction, properly extractingtype
andbuilder
properties from the originalreactionIcons.first
. The updated callback signature with thetype
parameter aligns with the newOnReactionPickerIconPressed
typedef.Also applies to: 80-86, 109-114, 125-131
385-385
: Background color change aligns with UI updates.The change from
theme.colorTheme.appBg
totheme.colorTheme.overlay
for the scaffold background is consistent with UI theming updates mentioned in the AI summary.packages/stream_chat_flutter/lib/src/reactions/picker/reaction_picker.dart (2)
4-19
: Excellent API design with comprehensive documentation.The
ReactionPickerBuilder
typedef provides a clean, well-documented interface for customizing reaction pickers. The template documentation clearly explains usage in modal components and parameter purposes.
111-114
: Good improvement from DecoratedBox to Material widget.Changing from
DecoratedBox
toMaterial
follows Flutter best practices and provides better platform integration, touch feedback, and accessibility while maintaining the same visual properties (border radius, color, clip behavior).packages/stream_chat_flutter/lib/stream_chat_flutter.dart (1)
106-110
: LGTM! Well-organized export restructuring.The reorganization of reaction-related exports from
message_widget/reactions
to a dedicatedreactions
directory improves code organization and makes the reaction functionality more discoverable. The export paths are correctly structured and follow Flutter package conventions.packages/stream_chat_flutter/CHANGELOG.md (1)
46-50
: Clear and comprehensive feature documentation.The changelog entries effectively document the new features being introduced. The descriptions are concise yet informative, properly explaining the purpose and scope of each addition. The formatting is consistent with existing changelog conventions.
🧰 Tools
🪛 LanguageTool
[style] ~50-~50: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...nable custom reaction picker widgets. - AddedStreamReactionIcon.defaultReactions
p...(ENGLISH_WORD_REPEAT_BEGINNING_RULE)
packages/stream_chat_flutter/lib/src/message_widget/message_widget_content.dart (4)
4-4
: Import path correctly updated for reorganized directory structure.The import path has been properly updated to reflect the movement of
desktop_reactions_builder.dart
frommessage_widget/reactions
to the newreactions
directory structure.
288-294
: Simplified reaction indicator configuration.The
portalFollower
is now always assigned aReactionIndicator
instance, removing the previous conditional logic. This simplification appears to streamline the reaction display logic while maintaining functionality.
296-300
: Enhanced anchor positioning with improved alignment controls.The anchor configuration has been restructured with more explicit alignment properties and added
offset
andshiftToWithinBound
parameters. This provides better control over reaction indicator positioning and boundary handling.
306-306
: Verify visual impact of increased padding.The top padding has been increased from 18 to 28 pixels when reactions are shown. This change affects the visual spacing around the reaction indicator.
Please review the visual impact of this padding change to ensure it doesn't create excessive spacing or alignment issues with the reaction indicator layout, especially across different screen sizes and orientations.
packages/stream_chat_flutter/lib/src/reactions/user_reactions.dart (2)
15-75
: Well-structured widget implementation with good Material Design principles.The
StreamUserReactions
widget is well-implemented with:
- Proper theme integration using
StreamChatTheme
- Responsive layout with
Flexible
andSingleChildScrollView
- Appropriate use of Material Design with rounded corners and theme-based colors
- Good accessibility with proper text styling and localized strings
92-92
: Good null safety handling for reaction users.The null check for
reactionUser
properly handles cases where reaction data might be incomplete, returning an empty widget instead of causing runtime errors.packages/stream_chat_flutter/lib/src/message_modal/message_actions_modal.dart (3)
2-2
: Import path updated for folder reorganization.The import path change from
src/message_widget/reactions/reactions_align.dart
tosrc/reactions/reactions_align.dart
aligns with the broader package reorganization to consolidate reaction-related components into a dedicatedreactions
folder.
24-24
: Well-implemented builder pattern for reaction picker customization.The addition of the
reactionPickerBuilder
parameter with a sensible default (StreamReactionPicker.builder
) enables customization while maintaining backward compatibility. This follows the established builder pattern conventions in the Flutter ecosystem.Also applies to: 62-63
100-100
: Clean abstraction using the injected builder.Replacing the direct
StreamReactionPicker
instantiation with the injectedreactionPickerBuilder
function call properly abstracts the reaction picker creation, allowing for flexible customization while maintaining the same interface.packages/stream_chat_flutter/lib/src/reactions/reaction_indicator.dart (2)
38-57
: Robust reaction filtering and sorting logic.The implementation correctly handles reaction deduplication and sorting:
- Filtering (lines 38-45): Ensures only one reaction per type, prioritizing the current user's reactions when duplicates exist
- Sorting (lines 47-57): Properly handles null user IDs and places current user's reactions last for better UX
The logic handles edge cases well, including null user IDs and ensures consistent behavior.
59-72
: Clean widget composition with proper theming.The widget structure is well-designed:
GestureDetector
for interaction handlingStreamReactionBubble
for consistent visual presentation- Proper integration with
messageTheme
properties for styling consistencyThe use of
ValueKey
with message ID ensures proper widget rebuilding when reactions change.packages/stream_chat_flutter/lib/src/misc/reaction_icon.dart (3)
26-29
: Useful unknown reaction constructor for fallback scenarios.The
unknown
constructor provides a sensible fallback for unrecognized reaction types, using a help icon which clearly indicates an unknown state to users.
37-54
: Well-documented default reactions list.The
defaultReactions
static list provides a comprehensive set of common reactions (love, like, sad, haha, wow) with excellent documentation. This serves as both a ready-to-use default and a clear example for developers implementing custom reactions.
56-162
: Consistent and well-structured reaction builders.All reaction builders follow a consistent pattern:
- Proper theme integration using
StreamChatTheme.of(context)
- Clean color logic with switch expressions for highlighted/unhighlighted states
- Consistent use of
StreamSvgIcon
for SVG assets and standardIcon
for the unknown fallback- Appropriate parameterization (context, highlighted state, size)
The color switching between
accentPrimary
andprimaryIconTheme.color
provides clear visual feedback for user interaction states.packages/stream_chat_flutter/lib/src/message_modal/message_reactions_modal.dart (1)
93-96
: Null-safety check foronReactionPicked
reactionPickerBuilder
is invoked withonReactionPicked
, which can benull
(line 84). If a custom builder assumes a non-null callback it will throw at
runtime.Either:
- Document that the third parameter may be
null
, or- Pass a default no-op callback when
showReactionPicker == true
.- child: reactionPickerBuilder(context, message, onReactionPicked), + child: reactionPickerBuilder( + context, + message, + onReactionPicked ?? (_) {}, + ),Please pick one approach so custom builders have a clear contract.
packages/stream_chat_flutter/lib/src/reactions/picker/reaction_picker_icon_list.dart (2)
54-66
: Great addition – open-endediconBuilder
unlocks theming flexibility
The newiconBuilder
parameter (with a sensible default) decouples rendering from business logic, allowing callers to plug in fully-custom widgets – nice!
213-238
: Documentation ✨
Clear, concise docs forReactionPickerIcon
make the new data object self-explanatory – nicely done.
), | ||
const SizedBox(height: 8), | ||
Text( | ||
reactionUser.name.split(' ')[0], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve robustness of name parsing logic.
The current implementation uses reactionUser.name.split(' ')[0]
to extract the first name, which assumes the name contains spaces and could fail for single-word names or names with different formatting.
Consider using a more robust approach:
- Text(
- reactionUser.name.split(' ')[0],
+ Text(
+ reactionUser.name.split(' ').firstOrNull ?? reactionUser.name,
style: theme.textTheme.footnoteBold,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
Alternatively, you could use a helper method to safely extract the first name:
String _getFirstName(String fullName) {
final parts = fullName.trim().split(' ');
return parts.isNotEmpty ? parts.first : fullName;
}
🤖 Prompt for AI Agents
In packages/stream_chat_flutter/lib/src/reactions/user_reactions.dart at line
137, the current code uses reactionUser.name.split(' ')[0] to get the first
name, which can fail if the name has no spaces or unusual formatting. Replace
this with a helper method that trims the name, splits by spaces, and returns the
first part if available, otherwise returns the full name. Then use this helper
method to safely extract the first name.
Resolves: #2225, #2216
Description of the pull request
This pull request Introduces a
reactionPickerBuilder
parameter inStreamMessageWidget
, enabling developers to provide custom reaction picker implementations. Also fixed the issue where overriding the reaction icons in a subtree was not getting used in the reaction picker.Screen.Recording.2025-05-26.at.02.29.35.mov
Summary by CodeRabbit
New Features
Refactor
Bug Fixes
Tests
Chores