feat(ui): add onReactionTap reporting the tapped reaction - #2852
Conversation
Displayed reactions previously fired onReactionsTap with only the message, so integrators couldn't tell which reaction was tapped. Add onReactionTap(Reaction? reaction, Message message) on StreamMessageItem and StreamMessageListView, reporting the tapped reaction (the user's full own reaction when present, else a type + emojiCode template) or null for a clustered/overflow chip that maps to no single reaction. Deprecate onReactionsTap and the OnReactionsTap typedef in favor of onReactionTap, following the onReactionPicked -> onReactionSelected pattern. Depends on GetStream/stream-core-flutter#140 (StreamReactions.onReactionPressed); pinned via git ref until it merges. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThe PR adds reaction-aware tap callbacks across message reaction widgets, deprecates previous reaction-area callbacks, resolves tapped reactions to full, template, or null values, and adds widget tests for segmented and clustered displays. ChangesReaction tap callback migration
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant StreamMessageReactions
participant StreamMessageContent
participant DefaultStreamMessageItem
User->>StreamMessageReactions: Tap reaction chip
StreamMessageReactions->>StreamMessageContent: Resolve Reaction or null
StreamMessageContent->>DefaultStreamMessageItem: Forward reaction callback
DefaultStreamMessageItem->>User: Invoke callback with ReactionTapDetails
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@packages/stream_chat_flutter/lib/src/message_widget/stream_message_item.dart`:
- Around line 315-316: Mark the StreamMessageItemProps.onReactionsTap field
declaration with the same `@Deprecated` annotation already used for its
constructor and copyWith parameters, directing callers to onReactionTap.
- Around line 174-175: Update the StreamMessageItemProps construction or
validation to assert that deprecated onReactionsTap and onReactionTap are not
both provided, mirroring the existing StreamMessageItem assertion. Ensure this
exclusivity applies to StreamMessageListView and direct props construction paths
while preserving each callback’s existing behavior when supplied alone.
In `@packages/stream_chat_flutter/lib/src/utils/typedefs.dart`:
- Around line 190-191: Add a concise /// doc comment immediately before the
deprecated public typedef OnReactionsTap, describing its legacy purpose and
directing users to OnReactionTap; retain the existing `@Deprecated` annotation and
typedef unchanged.
In `@packages/stream_chat_flutter/pubspec.yaml`:
- Line 69: Remove the direct dependency pin from
packages/stream_chat_flutter/pubspec.yaml and manage the
77d1cb7d35fe157a5b27a2650d3a11bf4995f11b constraint in melos.yaml instead, then
run melos bootstrap to propagate the generated dependency update.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 3417c8a8-7601-463a-9863-d6217a469c5d
📒 Files selected for processing (10)
docs/docs_screenshots/pubspec.yamlmelos.yamlpackages/stream_chat_flutter/CHANGELOG.mdpackages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dartpackages/stream_chat_flutter/lib/src/message_widget/components/stream_message_content.dartpackages/stream_chat_flutter/lib/src/message_widget/components/stream_message_reactions.dartpackages/stream_chat_flutter/lib/src/message_widget/stream_message_item.dartpackages/stream_chat_flutter/lib/src/utils/typedefs.dartpackages/stream_chat_flutter/pubspec.yamlpackages/stream_chat_flutter/test/src/message_widget/stream_message_reactions_test.dart
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #2852 +/- ##
==========================================
+ Coverage 72.83% 72.85% +0.01%
==========================================
Files 428 429 +1
Lines 27658 27674 +16
==========================================
+ Hits 20145 20162 +17
+ Misses 7513 7512 -1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…hadowing Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…n props Add the onReactionsTap/onReactionTap exclusivity assert to StreamMessageItemProps so .fromProps/copyWith/direct construction paths are covered (per review). Add a test that tapping the segmented overflow chip reports null. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| /// Reports the tapped [Reaction], or `null` when the tap does not map to a | ||
| /// single reaction (for example a clustered or overflow chip). | ||
| /// {@endtemplate} | ||
| typedef OnReactionTap = void Function(Reaction? reaction, Message message); |
There was a problem hiding this comment.
I think we should by default now make a new object so we can always add more data whenever we want. Also context might be useful
typedef OnReactionTap = void Function(BuildContext context, ReactionTapData data);
class ReactionTapData {
Message message;
Reaction? reaction;
}Change OnReactionTap from (Reaction?, Message) to a single ReactionTapDetails payload (message + reaction), following Flutter's <Event>Details convention (TapDownDetails etc.). Extensible without breaking the signature; no BuildContext, matching Flutter's action-callback convention. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/stream_chat_flutter/lib/src/message_widget/stream_message_item.dart (1)
391-419: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winAllow
copyWithto clear the legacy callback.If an existing props object has
onReactionsTap, callingcopyWith(onReactionTap: newCallback)can retain both callbacks and trigger the new exclusivity assertion. PassingonReactionsTap: nullmust explicitly clear the legacy callback rather than retain it; use a sentinel or dedicated clear semantics, and add a migration regression test.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/stream_chat_flutter/lib/src/message_widget/stream_message_item.dart` around lines 391 - 419, Update StreamMessageItemProps.copyWith so callers can explicitly clear an existing onReactionsTap while setting onReactionTap, using a sentinel or equivalent dedicated clear semantics instead of treating null as “retain.” Ensure the resulting props cannot retain both callbacks, and add a regression test covering copyWith(onReactionTap: newCallback, onReactionsTap: null) on props that already contain the legacy callback.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/stream_chat_flutter/lib/src/utils/typedefs.dart`:
- Around line 207-211: Update the documentation for the nullable reaction field
in the reaction-resolution type to state that non-null values may be either the
user’s complete reaction, including actor metadata, or a type/emoji-code
template without actor metadata. Preserve the existing explanation for clustered
and overflow taps.
---
Outside diff comments:
In
`@packages/stream_chat_flutter/lib/src/message_widget/stream_message_item.dart`:
- Around line 391-419: Update StreamMessageItemProps.copyWith so callers can
explicitly clear an existing onReactionsTap while setting onReactionTap, using a
sentinel or equivalent dedicated clear semantics instead of treating null as
“retain.” Ensure the resulting props cannot retain both callbacks, and add a
regression test covering copyWith(onReactionTap: newCallback, onReactionsTap:
null) on props that already contain the legacy callback.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 305e07dd-0597-4a41-a98b-1a5b3add56e2
📒 Files selected for processing (3)
packages/stream_chat_flutter/CHANGELOG.mdpackages/stream_chat_flutter/lib/src/message_widget/stream_message_item.dartpackages/stream_chat_flutter/lib/src/utils/typedefs.dart
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/stream_chat_flutter/CHANGELOG.md
…s component Relocate the reaction-tap callback typedef and its payload from the generic typedefs bucket into stream_message_reactions.dart, where they belong. The component stays internal; a show-scoped barrel export keeps only the two public types visible. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Revert the relocation into the reactions component and keep the internal widgets on the minimal ValueSetter<Reaction?> signal; ReactionTapDetails is assembled once at the public boundary (the StreamMessageItem dispatcher). Narrow internal interface, rich public interface — the public payload type stays at the public edge. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…eftover) Accidentally swept into the previous commit by git add -A; it is not part of this PR. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
A message's displayed reactions only fired
onReactionsTap, which hands back just theMessage— integrators had no way to tell which reaction chip was tapped (e.g. to toggle that specific reaction). This addsonReactionTap, which reports the tapped reaction.Requested on Slack (tap a specific reaction to increment/decrement it). Cross-SDK, React Native / UIKit already expose a per-reaction tap; this brings Flutter in line.
What changed
onReactionTaponStreamMessageItemandStreamMessageListView:reactionis the tapped reaction — the user's full own reaction when they've reacted with that type (populateduser/score/timestamps), otherwise atype+emojiCodetemplate (mirrors the reaction picker's resolution)reactionisnullfor a clustered or overflow chip, which maps to no single reactionReactionTapDetailsobject (not positional params) so it can grow non-breakingly, following Flutter's<Event>Detailsconvention (TapDownDetails,DragUpdateDetails, …). The callback also receives the tapped message'sBuildContext, so integrators can navigate or show overlays relative to it without threading a context in themselves.onReactionsTap(and theOnReactionsTaptypedef) in favor ofonReactionTap, following theonReactionPicked→onReactionSelecteddeprecation pattern. Exclusivity is asserted on both theStreamMessageItemconstructor andStreamMessageItemProps. Default "who reacted" behavior is unchanged.StreamMessageContent,StreamMessageReactions— not exported) migrated cleanly; deprecation is only on the public, exported entry points.Usage
Dependency
Depends on GetStream/stream-core-flutter#140 (
StreamReactions.onReactionPressed). Pinned via a git ref inmelos.yaml/pubspec.yaml; the ref must be updated to the merged commit once #140 lands (and swapped back to a pub version before release).Tests
test/src/message_widget/stream_message_reactions_test.dart:nullnull🤖 Generated with Claude Code