Skip to content

feat(ui): add onReactionTap reporting the tapped reaction - #2852

Merged
xsahil03x merged 11 commits into
masterfrom
sahil/on-reaction-tap-callback
Jul 30, 2026
Merged

xsahil03x merged 11 commits into
masterfrom
sahil/on-reaction-tap-callback

Conversation

@xsahil03x

@xsahil03x xsahil03x commented Jul 29, 2026 •

Copy link
Copy Markdown
Member

Summary

A message's displayed reactions only fired onReactionsTap, which hands back just the Message — integrators had no way to tell which reaction chip was tapped (e.g. to toggle that specific reaction). This adds onReactionTap, 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

  • New onReactionTap on StreamMessageItem and StreamMessageListView:
    typedef OnReactionTap = void Function(BuildContext context, ReactionTapDetails details);
    
    class ReactionTapDetails {
      final Message message;
      final Reaction? reaction; // null for a clustered/overflow chip
    }
    • reaction is the tapped reaction — the user's full own reaction when they've reacted with that type (populated user/score/timestamps), otherwise a type + emojiCode template (mirrors the reaction picker's resolution)
    • reaction is null for a clustered or overflow chip, which maps to no single reaction
  • Payload is a ReactionTapDetails object (not positional params) so it can grow non-breakingly, following Flutter's <Event>Details convention (TapDownDetails, DragUpdateDetails, …). The callback also receives the tapped message's BuildContext, so integrators can navigate or show overlays relative to it without threading a context in themselves.
  • Deprecated onReactionsTap (and the OnReactionsTap typedef) in favor of onReactionTap, following the onReactionPicked → onReactionSelected deprecation pattern. Exclusivity is asserted on both the StreamMessageItem constructor and StreamMessageItemProps. Default "who reacted" behavior is unchanged.
  • Internal plumbing (StreamMessageContent, StreamMessageReactions — not exported) migrated cleanly; deprecation is only on the public, exported entry points.

Usage

StreamMessageListView(
  onReactionTap: (context, details) {
    final reaction = details.reaction;
    if (reaction == null) return; // clustered/overflow chip
    final channel = StreamChannel.of(context).channel;
    final own = details.message.ownReactions?.any((r) => r.type == reaction.type) ?? false;
    own
        ? channel.deleteReaction(details.message, reaction)
        : channel.sendReaction(details.message, reaction);
  },
);

Dependency

Depends on GetStream/stream-core-flutter#140 (StreamReactions.onReactionPressed). Pinned via a git ref in melos.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:

  • segmented: tapping a chip reports its reaction
  • segmented: reports the full own reaction when the user has reacted with that type
  • segmented: tapping the "+N" overflow chip reports null
  • clustered: tapping the grouped chip reports null

🤖 Generated with Claude Code

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>
@coderabbitai

coderabbitai Bot commented Jul 29, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The 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.

Changes

Reaction tap callback migration

Layer / File(s) Summary
Reaction callback contracts
packages/stream_chat_flutter/lib/src/utils/typedefs.dart, packages/stream_chat_flutter/lib/src/message_widget/stream_message_item.dart, packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart, packages/stream_chat_flutter/CHANGELOG.md
Adds OnReactionTap with ReactionTapDetails, propagates it through message widgets and list views, and deprecates the previous callback APIs.
Reaction resolution and rendering flow
packages/stream_chat_flutter/lib/src/message_widget/components/*, packages/stream_chat_flutter/test/src/message_widget/stream_message_reactions_test.dart
Resolves tapped chips to full, template, or null reactions, forwards callbacks through message rendering, and tests segmented and clustered behavior.
Dependency and release updates
packages/stream_chat_flutter/pubspec.yaml, melos.yaml, docs/docs_screenshots/pubspec.yaml
Updates pinned stream_core_flutter references and documents the callback API changes.

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
Loading

Suggested reviewers: velikovpetar, renefloor

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main change: adding onReactionTap to report the tapped reaction.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sahil/on-reaction-tap-callback

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 2507ca5 and 156ef61.

📒 Files selected for processing (10)
  • docs/docs_screenshots/pubspec.yaml
  • melos.yaml
  • packages/stream_chat_flutter/CHANGELOG.md
  • packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart
  • packages/stream_chat_flutter/lib/src/message_widget/components/stream_message_content.dart
  • packages/stream_chat_flutter/lib/src/message_widget/components/stream_message_reactions.dart
  • packages/stream_chat_flutter/lib/src/message_widget/stream_message_item.dart
  • packages/stream_chat_flutter/lib/src/utils/typedefs.dart
  • packages/stream_chat_flutter/pubspec.yaml
  • packages/stream_chat_flutter/test/src/message_widget/stream_message_reactions_test.dart

Comment thread packages/stream_chat_flutter/lib/src/utils/typedefs.dart
Comment thread packages/stream_chat_flutter/pubspec.yaml Outdated
@codecov

codecov Bot commented Jul 29, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 68.18182% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 72.85%. Comparing base (4ba19d7) to head (8b36754).

Files with missing lines Patch % Lines
...er/lib/src/message_widget/stream_message_item.dart 25.00% 6 Missing ⚠️
...es/stream_chat_flutter/lib/src/utils/typedefs.dart 0.00% 1 Missing ⚠️
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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

xsahil03x and others added 3 commits July 29, 2026 18:27
…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);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 win

Allow copyWith to clear the legacy callback.

If an existing props object has onReactionsTap, calling copyWith(onReactionTap: newCallback) can retain both callbacks and trigger the new exclusivity assertion. Passing onReactionsTap: null must 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

📥 Commits

Reviewing files that changed from the base of the PR and between df59204 and 7b41850.

📒 Files selected for processing (3)
  • packages/stream_chat_flutter/CHANGELOG.md
  • packages/stream_chat_flutter/lib/src/message_widget/stream_message_item.dart
  • packages/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

Comment thread packages/stream_chat_flutter/lib/src/utils/typedefs.dart
xsahil03x and others added 5 commits July 30, 2026 14:19
…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>
@xsahil03x
xsahil03x merged commit 1601cf3 into master Jul 30, 2026
25 of 26 checks passed
@xsahil03x
xsahil03x deleted the sahil/on-reaction-tap-callback branch July 30, 2026 13:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants