Skip to content

Comments

Update design of message reactions#6160

Merged
gpunto merged 8 commits intov7from
redesign/message-reactions
Feb 19, 2026
Merged

Update design of message reactions#6160
gpunto merged 8 commits intov7from
redesign/message-reactions

Conversation

@gpunto
Copy link
Contributor

@gpunto gpunto commented Feb 16, 2026

Goal

Update message reactions design, including two styles: clustered and segmented

Implementation

  • Introduce three composables: clustered + segmented + adaptive that selects one of the two based on available width
  • Introduce MessageReactionItemState because we need to pass the reactions count

🎨 UI Changes

Before After (segmented) (clustered)
Screenshot_20260216_181509 Screenshot_20260217_112358 Screenshot_20260216_181624

Testing

By default we're using segmented. If you want to see the clustered style in the sample you can:

  • Call it directly from ChatComponentFactory.MessageReactions (easiest option)
  • Manage to add enough reactions to a message to overflow the screen width

Summary by CodeRabbit

  • New Features

    • Added adaptive message reactions with segmented and clustered display modes that automatically adjust based on available space
    • Reactions now position above message content with improved visual layout
  • Improvements

    • Simplified reaction filtering and display logic
    • Enhanced reaction rendering with updated visual design and spacing

@gpunto gpunto added the pr:new-feature New feature label Feb 16, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Feb 16, 2026

PR checklist ✅

All required conditions are satisfied:

  • Title length is OK (or ignored by label).
  • At least one pr: label exists.
  • Sections ### Goal, ### Implementation, and ### Testing are filled.

🎉 Great job! This PR is ready for review.

@gpunto
Copy link
Contributor Author

gpunto commented Feb 16, 2026

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Feb 16, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 16, 2026

SDK Size Comparison 📏

SDK Before After Difference Status
stream-chat-android-client 5.25 MB 5.66 MB 0.41 MB 🟡
stream-chat-android-ui-components 10.60 MB 10.93 MB 0.33 MB 🟡
stream-chat-android-compose 12.81 MB 11.88 MB -0.93 MB 🚀

@coderabbitai
Copy link

coderabbitai bot commented Feb 16, 2026

Walkthrough

This PR refactors the message reactions API by introducing a new MessageReactionItemState data class and replacing the existing MessageReactions composable with three new variants (AdaptiveMessageReactions, ClusteredMessageReactions, SegmentedMessageReactions). The MessageContent function signature is modified to remove the ColumnScope receiver, and the MessageReactionItem function is removed from the public API. Message container layout is updated to handle reaction rendering with conditional positioning.

Changes

Cohort / File(s) Summary
Reaction State Model
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/state/messages/MessageReactionItemState.kt
New data class added with type, emoji, and count properties to replace legacy ReactionOptionItemState for reaction representation.
Message Reactions Components
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/MessageReactions.kt
Replaced single MessageReactions API with three new public composables: AdaptiveMessageReactions (delegates based on width), ClusteredMessageReactions (single aggregated pill), and SegmentedMessageReactions (per-reaction pills). Added onClick parameter for reaction interaction handling.
Component Factory & Theme API
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/ChatComponentFactory.kt, ChatComponentFactoryParams.kt, stream-chat-android-compose/api/stream-chat-android-compose.api
Updated MessageContent signature to remove ColumnScope receiver and made it a top-level function; removed public MessageReactionItem entry point; replaced MessageReactions implementation to delegate to AdaptiveMessageReactions; updated MessageReactionsParams.reactions to use MessageReactionItemState and removed MessageReactionItemParams from public API.
Message Container Layout
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/list/MessageContainer.kt
Added new MessageContentWithReactions layout composable; introduced rememberMessageReactions helper to compute visible reactions and map them to MessageReactionItemState; replaced direct MessageContent usage with MessageContentWithReactions wrapper; updated alignment modifiers to explicit Alignment constants.
Preview & Sample Data
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/previewdata/PreviewReactionData.kt, PreviewUserReactionData.kt
Renamed PreviewReactionOptionData to PreviewReactionData and updated reaction data functions to return MessageReactionItemState instead of ReactionOptionItemState; removed @Composable annotations from preview helper functions in PreviewUserReactionData.
Sample Location Component
stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/location/LocationComponentFactory.kt
Removed ColumnScope receiver from LocationComponentFactory.MessageContent composable, changing it from an extension function to a plain composable.
Selected Reactions Menu
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/selectedmessage/SelectedReactionsMenu.kt
Replaced filtering logic to use mapNotNull instead of explicit supportedReactions check, allowing reactions with non-null users to be included more permissively.

Sequence Diagram

sequenceDiagram
    participant MessageContainer
    participant rememberMessageReactions as rememberMessageReactions
    participant ReactionResolver
    participant AdaptiveMessageReactions
    participant SegmentedMessageReactions
    participant ClusteredMessageReactions

    MessageContainer->>rememberMessageReactions: Compute visible reactions
    rememberMessageReactions->>ReactionResolver: Filter by supportedReactions
    ReactionResolver-->>rememberMessageReactions: Filtered reactions
    rememberMessageReactions->>rememberMessageReactions: Map to MessageReactionItemState
    rememberMessageReactions-->>MessageContainer: List<MessageReactionItemState>
    
    MessageContainer->>AdaptiveMessageReactions: Render reactions with list
    AdaptiveMessageReactions->>SegmentedMessageReactions: Measure width (first attempt)
    alt Width fits constraints
        SegmentedMessageReactions-->>AdaptiveMessageReactions: Render per-reaction pills
    else Width exceeds constraints
        AdaptiveMessageReactions->>ClusteredMessageReactions: Fallback to clustered
        ClusteredMessageReactions-->>AdaptiveMessageReactions: Render aggregated pill
    end
    AdaptiveMessageReactions-->>MessageContainer: Rendered reaction UI
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

pr:breaking-change

Suggested reviewers

  • andremion
  • VelikovPetar

Poem

🐰 A rabbit's ode to reactions anew:
Reactions now cluster and segment with grace,
With state that is neat in a class full of place,
No ColumnScope binding the messages down,
The adaptive layout wears Compose's new crown! 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 41.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Merge Conflict Detection ⚠️ Warning ❌ Merge conflicts detected (26 files):

⚔️ stream-chat-android-compose-sample/src/main/java/io/getstream/chat/android/compose/sample/ui/location/LocationComponentFactory.kt (content)
⚔️ stream-chat-android-compose/api/stream-chat-android-compose.api (content)
⚔️ stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/previewdata/PreviewUserReactionData.kt (content)
⚔️ stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/MessageReactions.kt (content)
⚔️ stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/PollMessageContent.kt (content)
⚔️ stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollMoreOptionsDialog.kt (content)
⚔️ stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollOptionVotesDialog.kt (content)
⚔️ stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/poll/PollViewResultDialog.kt (content)
⚔️ stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/selectedmessage/SelectedReactionsMenu.kt (content)
⚔️ stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/list/MessageContainer.kt (content)
⚔️ stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/ChatComponentFactory.kt (content)
⚔️ stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/ChatComponentFactoryParams.kt (content)
⚔️ stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/MessageStyling.kt (content)
⚔️ stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/StreamColors.kt (content)
⚔️ stream-chat-android-compose/src/main/res/values/strings.xml (content)
⚔️ stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.messages_PollMessageContentTest_poll_content.png (content)
⚔️ stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.poll_PollMoreOptionsDialogTest_dark_mode.png (content)
⚔️ stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.poll_PollMoreOptionsDialogTest_light_mode.png (content)
⚔️ stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.poll_PollViewResultDialogTest_dark_mode.png (content)
⚔️ stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.poll_PollViewResultDialogTest_light_mode.png (content)
⚔️ stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.messages_MessageListTest_loaded_messages.png (content)
⚔️ stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.messages_MessageListTest_loaded_messages_in_dark_mode.png (content)
⚔️ stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.messages_MessageListTest_scroll_to_bottom_button.png (content)
⚔️ stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.messages_MessageListTest_scroll_to_bottom_button_in_dark_mode.png (content)
⚔️ stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/utils/PollsConstants.kt (content)
⚔️ stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/messages/list/adapter/view/internal/PollView.kt (content)

These conflicts must be resolved before merging into v7.
Resolve conflicts locally and push changes to this branch.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Update design of message reactions' directly describes the main change—a redesign of message reactions with new clustered and segmented styles.
Description check ✅ Passed PR description includes goal, implementation details, UI changes with before/after screenshots, and testing instructions, but lacks testing methodology, KDoc updates info, and contributor/reviewer checklists.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch redesign/message-reactions
⚔️ Resolve merge conflicts (beta)
  • Auto-commit resolved conflicts to branch redesign/message-reactions
  • Create stacked PR with resolved conflicts
  • Post resolved changes as copyable diffs in a comment

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


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 and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/ChatComponentFactoryParams.kt (1)

25-37: ⚠️ Potential issue | 🟡 Minor

Update MessageReactionsParams KDoc for the new reaction item type (and add thread/state notes).

The KDoc still says “reaction options” and doesn’t include thread/state notes, which is now more important with MessageReactionItemState (type/emoji/count).

📝 Suggested KDoc update
  /**
   * Parameters for the [ChatComponentFactory.MessageReactions] component.
   *
   * `@param` modifier Modifier for styling.
   * `@param` message The message for which the reactions are displayed.
-  * `@param` reactions The list of reaction options to display.
+  * `@param` reactions The list of reaction items (type/emoji/count) to display.
   * `@param` onClick Handler when the reaction list is clicked. The message is provided as a parameter.
+  *
+  * Threading: Compose/main thread only.
+  * State: Treat [reactions] as immutable snapshot state.
   */

As per coding guidelines: 'Document public APIs with KDoc, including thread expectations and state notes'.

stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/ChatComponentFactory.kt (1)

1136-1197: ⚠️ Potential issue | 🟡 Minor

Add thread/state notes to MessageContent and MessageReactions KDoc.

These are public APIs, and the signature change makes it a good moment to add thread expectations/state notes.

📝 Suggested KDoc additions
 /**
  * The default content of the message bubble.
  * Usually contains attachments and text.
+ *
+ * Threading: Compose/main thread only.
+ * State: Treat [messageItem] as immutable UI state.
  */
@@
 /**
  * The default reactions displayed overlaying the message bubble border.
+ *
+ * Threading: Compose/main thread only.
+ * State: Treat [params] as immutable UI state.
  */

As per coding guidelines: 'Document public APIs with KDoc, including thread expectations and state notes'.

🤖 Fix all issues with AI agents
In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/state/messages/MessageReactionItemState.kt`:
- Around line 19-30: Update the public KDoc for the data class
MessageReactionItemState to include a short "Thread / State notes" section
describing its expected usage and thread-safety (for example: that instances are
immutable, safe to read from any thread, and represent UI state derived from
Message objects rather than the source message itself), while keeping the
existing `@param` descriptions for type, emoji and count; ensure the new note is
concise and placed in the class-level KDoc so it appears in the public API docs
alongside MessageReactionItemState.

In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/MessageReactions.kt`:
- Around line 46-53: Add KDoc thread expectations and state notes to the three
public composables AdaptiveMessageReactions, ClusteredMessageReactions, and
SegmentedMessageReactions: update each KDoc block to include a short "Thread
expectations" line (e.g., whether the composable is intended for message list vs
thread/parent messages) and a "State notes" line describing how it reads/depends
on reaction state (local vs server-driven, whether it expects a stable reactions
list, and recomposition behavior). Locate the three symbols
AdaptiveMessageReactions, ClusteredMessageReactions, and
SegmentedMessageReactions in MessageReactions.kt and add the concise KDoc
entries consistent with existing project style for public APIs.
- Around line 188-217: Replace the Compose `@Preview` annotations with the project
helper `@StreamPreview` for the preview composables; update the annotations on
SingleClusteredMessageReactionsPreview,
MultipleClusteredMessageReactionsPreview,
SingleSegmentedMessageReactionsPreview, and
MultipleSegmentedMessageReactionsPreview so they use `@StreamPreview` while
leaving their bodies (which call ClusteredMessageReactions and
SegmentedMessageReactions with PreviewReactionData) unchanged.
- Around line 55-76: In AdaptiveMessageReactions the decision to use the
clustered fallback is wrong because `segmented` is measured with the same
`constraints.maxWidth` so it can never exceed it; change the measurement
strategy to first measure `segmented` with an effectively unconstrained max
width (e.g., copy constraints to remove maxWidth limit) to get its intrinsic
width, then compare that intrinsic width to the original `constraints.maxWidth`;
if it’s larger, measure `clustered` with the original constraints and layout
that, otherwise measure/place `segmented` with the original constraints—update
references to `segmented`, `clustered`, `SubcomposeLayout` and the constraint
copies in `AdaptiveMessageReactions` accordingly.
🧹 Nitpick comments (1)
stream-chat-android-compose/api/stream-chat-android-compose.api (1)

1849-1851: Consider a deprecated MessageReactions wrapper for migration.

The new clustered/segmented/adaptive APIs replace the previous single entry point; a deprecated wrapper delegating to AdaptiveMessageReactions would soften upgrade friction for existing users.

@gpunto gpunto force-pushed the redesign/message-reactions branch 2 times, most recently from 3b79d47 to ae66083 Compare February 16, 2026 17:46
@gpunto gpunto marked this pull request as ready for review February 17, 2026 11:09
@gpunto gpunto requested a review from a team as a code owner February 17, 2026 11:09
@gpunto gpunto force-pushed the redesign/message-reactions branch 2 times, most recently from 7955bec to 39db86d Compare February 17, 2026 15:59
@gpunto gpunto force-pushed the redesign/message-reactions branch from 589f85b to 6fb7a89 Compare February 19, 2026 08:44
@gpunto gpunto force-pushed the redesign/message-reactions branch from 6fb7a89 to 2187991 Compare February 19, 2026 09:00
@gpunto gpunto enabled auto-merge (squash) February 19, 2026 09:03
@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
60.1% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@gpunto gpunto disabled auto-merge February 19, 2026 09:16
@gpunto gpunto merged commit 6ca83e8 into v7 Feb 19, 2026
14 of 15 checks passed
@gpunto gpunto deleted the redesign/message-reactions branch February 19, 2026 09:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr:new-feature New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants