Conversation
PR checklist ✅All required conditions are satisfied:
🎉 Great job! This PR is ready for review. |
SDK Size Comparison 📏
|
...hat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/StreamColors.kt
Show resolved
Hide resolved
WalkthroughA refactoring of the reactions UI in Stream Chat Android Compose, transitioning from column/grid-based layouts to horizontal row-based layouts, simplifying the SelectedReactionsMenu API surface, and restructuring theme colors with the introduction of StreamLegacyColors. Changes
Sequence DiagramsequenceDiagram
participant User
participant MessagesScreen
participant SelectedReactionsMenu
participant ReactionsMenuContent
participant ReactionCountRow
participant UserReactionRow
participant Message
participant ChatTheme
User->>MessagesScreen: Tap message reactions
MessagesScreen->>SelectedReactionsMenu: Show reactions in ModalBottomSheet
SelectedReactionsMenu->>ReactionsMenuContent: Initialize with message
ReactionsMenuContent->>Message: Extract reactionGroups & reactions
Message-->>ReactionsMenuContent: Return grouped reactions & user reactions
ReactionsMenuContent->>ChatTheme: Get theme colors & styling
ChatTheme-->>ReactionsMenuContent: Return color palette & theme tokens
ReactionsMenuContent->>ReactionCountRow: Render reaction chips with counts
ReactionsMenuContent->>UserReactionRow: Render individual user reactions
User->>ReactionCountRow: Tap reaction chip or add button
ReactionCountRow->>SelectedReactionsMenu: Trigger onReactionOptionSelected or onAddReactionClick
User->>UserReactionRow: Tap user reaction (if own reaction)
UserReactionRow->>SelectedReactionsMenu: Trigger remove reaction callback
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/StreamColors.kt (1)
25-114:⚠️ Potential issue | 🟡 MinorAdd KDoc entry for
chipText.
chipTextis a public color but isn’t documented in the class KDoc.📝 Suggested change
- * `@param` chatWaveformBarPlaying Used for play control button icon. + * `@param` chatWaveformBarPlaying Used for play control button icon. + * `@param` chipText Used for reaction chip text and count color. * `@param` controlPlayControlBg Used for play control button background.As per coding guidelines Document public APIs with KDoc, including thread expectations and state notes.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/StreamColors.kt` around lines 25 - 114, The class KDoc is missing a parameter description for the public color chipText; update the StreamColors KDoc to add a `@param` entry named chipText explaining its purpose (e.g., color used for text inside chips/pills), typical usage context, and any state/thread expectations, so the public API is fully documented; locate the KDoc block at the top of StreamColors.kt where other `@param` tags are listed and insert the chipText entry consistent with the existing style.
🧹 Nitpick comments (5)
stream-chat-android-previewdata/src/main/kotlin/io/getstream/chat/android/previewdata/PreviewReactionData.kt (1)
52-64: Use a fixed preview timestamp to keep previews/tests deterministic.
Date()makes the preview data time-dependent; a stable value keeps snapshots and previews consistent.♻️ Suggested change
public val manyReaction: List<Reaction> = listOf(reaction1, reaction2, reaction3, reaction4) + private val previewReactionDate = Date(0L) + public val oneReactionGroup: Map<String, ReactionGroup> = oneReaction.toReactionGroups() @@ - firstReactionAt = Date(), - lastReactionAt = Date(), + firstReactionAt = previewReactionDate, + lastReactionAt = previewReactionDate,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@stream-chat-android-previewdata/src/main/kotlin/io/getstream/chat/android/previewdata/PreviewReactionData.kt` around lines 52 - 64, The preview data uses Date() in List<Reaction>.toReactionGroups(), which yields time-dependent timestamps; replace Date() with a fixed deterministic timestamp (e.g., a shared constant like PREVIEW_DATE or Date(0)) so firstReactionAt and lastReactionAt are stable for previews/tests; update the toReactionGroups() implementation (and any references such as oneReactionGroup and manyReactionGroups) to use that fixed timestamp constant.stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/selectedmessage/SelectedReactionsMenu.kt (3)
77-94: ConsiderskipPartiallyExpanded = trueto match thefillMaxHeight()content
rememberModalBottomSheetState()defaults toskipPartiallyExpanded = false, meaning the sheet first opens at half-height. However, the content passed toReactionsMenuContentusesModifier.fillMaxHeight(), which forces the content to occupy the full available height. The result is that the partially-expanded stop becomes a full-height column crammed into a half-height sheet — awkward visually. SettingskipPartiallyExpanded = trueensures the sheet always expands to the Expanded state and moves directly to Hidden when dismissed.♻️ Proposed fix
- sheetState = rememberModalBottomSheetState(), + sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/selectedmessage/SelectedReactionsMenu.kt` around lines 77 - 94, The modal sheet opens partially while ReactionsMenuContent uses Modifier.fillMaxHeight(), causing layout mismatch; update the ModalBottomSheet's sheetState to use rememberModalBottomSheetState(skipPartiallyExpanded = true) so the sheet skips the half-expanded stop and goes straight to expanded (and Hidden on dismiss); locate the ModalBottomSheet instantiation and replace the current rememberModalBottomSheetState() call with rememberModalBottomSheetState(skipPartiallyExpanded = true) while leaving other parameters (onDismissRequest, dragHandle, etc.) unchanged.
169-179: TrailingSpaceris emitted after the last itemThe
Spacer(modifier = Modifier.height(8.dp))inside theforEachloop is appended after every row including the final one. This adds unnecessary extra bottom padding inside the scrollable column. UsingverticalArrangement = Arrangement.spacedBy(8.dp)on theColumn(removing the manualSpacer) would be cleaner and avoid the trailing gap.♻️ Proposed fix
Column( horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(8.dp), modifier = modifier ... ) { ... userReactions.forEach { item -> UserReactionRow( item = item, onClick = if (item.isMine) { { onReactionOptionSelected(item.type) } } else { null }, ) - Spacer(modifier = Modifier.height(8.dp)) } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/selectedmessage/SelectedReactionsMenu.kt` around lines 169 - 179, The loop in SelectedReactionsMenu emits a trailing Spacer after the last UserReactionRow causing extra bottom padding; remove the Spacer from the userReactions.forEach block and instead set verticalArrangement = Arrangement.spacedBy(8.dp) on the surrounding Column (where UserReactionRow items are rendered) so spacing is applied only between items; keep the existing conditional onClick logic for UserReactionRow (item.isMine) unchanged.
84-84:dragHandleassignment is redundant
ModalBottomSheet'sdragHandleparameter already defaults to{ BottomSheetDefaults.DragHandle() }, so this explicit assignment can be removed.♻️ Proposed simplification
- dragHandle = { BottomSheetDefaults.DragHandle() },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/selectedmessage/SelectedReactionsMenu.kt` at line 84, Remove the redundant explicit dragHandle assignment from the ModalBottomSheet call in SelectedReactionsMenu (the line setting dragHandle = { BottomSheetDefaults.DragHandle() }); ModalBottomSheet already defaults to that drag handle, so simply delete the dragHandle parameter and leave the rest of the ModalBottomSheet invocation intact (look for the ModalBottomSheet(...) in SelectedReactionsMenu.kt).stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/MessagesScreen.kt (1)
503-530: Redundant null-safe call after smart cast (line 528)
selectedMessageStateis already smart-cast to non-nullSelectedMessageReactionsStateby theischeck on line 503, so?.ownCapabilities ?: setOf()can be simplified.♻️ Proposed simplification
- ownCapabilities = selectedMessageState?.ownCapabilities ?: setOf(), + ownCapabilities = selectedMessageState.ownCapabilities,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/MessagesScreen.kt` around lines 503 - 530, The code redundantly uses a null-safe access on selectedMessageState after an is-check; update the ReactionsMenu call to use the smart-cast directly by replacing selectedMessageState?.ownCapabilities ?: setOf() with selectedMessageState.ownCapabilities (or selectedMessageState.ownCapabilities.ifEmpty { setOf() } if you want a non-empty fallback) so the ownCapabilities parameter references the non-null SelectedMessageReactionsState instance used in this branch (within the ChatTheme.componentFactory.ReactionsMenu invocation).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@stream-chat-android-compose/api/stream-chat-android-compose.api`:
- Around line 3648-3651: Update the release notes and add a small migration
shim: document that StreamColors' primary constructor and copy() now take
StreamLegacyColors as the first parameter (affecting destructuring/component1
and copy usage) and provide explicit migration examples using the new signature
and the library factory functions defaultColors() and defaultDarkColors();
additionally, add a deprecated convenience wrapper (e.g., a deprecated factory
or overload) that accepts the old parameters and delegates to the new
constructor to ease migration for downstream consumers, and reference
StreamColors, StreamLegacyColors, copy(), defaultColors(), and
defaultDarkColors() in the changelog entry.
In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/selectedmessage/ReactionCountRow.kt`:
- Around line 131-136: The ReactionCountRow currently renders the count even
when it's 1, contradicting its KDoc; update the rendering guard in
ReactionCountRow (where count is checked now with count?.toString()?.let) to
only show the Text when count is non-null and greater than 1 (e.g., check count
!= null && count > 1) and use count.toString() for the displayed text so single
reactions (count == 1) are hidden.
In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/selectedmessage/UserReactionRow.kt`:
- Around line 71-86: The "Tap to remove" hint in UserReactionRow is shown
whenever item.isMine is true even if the row is not clickable; update the
conditional that renders the remove hint so it only shows when the row is
clickable by checking the onClick parameter as well (e.g., in UserReactionRow,
change the remove-hint branch to require item.isMine && onClick != null) so the
hint is hidden when onClick is null.
In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/StreamColors.kt`:
- Around line 378-382: In the dark ColorPalette entries, replace the light
resource references used for media shimmer with their dark variants: update
mediaShimmerBase to use R.color.stream_compose_input_background_dark and
mediaShimmerHighlights to use R.color.stream_compose_app_background_dark (the
other dark usages like threadSeparatorGradientStart/End and
imageBackgroundMessageList are already correct) so shimmer colors are
appropriate for dark mode.
- Around line 462-490: Add/update the KDoc for the public data class
StreamLegacyColors to state it is an immutable, thread-safe representation of
legacy theme colors and should not hold mutable state; mention that instances
are expected to be treated as read-only (safe for concurrent access) and update
any existing KDoc above StreamLegacyColors to include this note and a brief
guidance for clients to create new instances for changes rather than mutating
shared objects.
---
Outside diff comments:
In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/StreamColors.kt`:
- Around line 25-114: The class KDoc is missing a parameter description for the
public color chipText; update the StreamColors KDoc to add a `@param` entry named
chipText explaining its purpose (e.g., color used for text inside chips/pills),
typical usage context, and any state/thread expectations, so the public API is
fully documented; locate the KDoc block at the top of StreamColors.kt where
other `@param` tags are listed and insert the chipText entry consistent with the
existing style.
---
Nitpick comments:
In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/selectedmessage/SelectedReactionsMenu.kt`:
- Around line 77-94: The modal sheet opens partially while ReactionsMenuContent
uses Modifier.fillMaxHeight(), causing layout mismatch; update the
ModalBottomSheet's sheetState to use
rememberModalBottomSheetState(skipPartiallyExpanded = true) so the sheet skips
the half-expanded stop and goes straight to expanded (and Hidden on dismiss);
locate the ModalBottomSheet instantiation and replace the current
rememberModalBottomSheetState() call with
rememberModalBottomSheetState(skipPartiallyExpanded = true) while leaving other
parameters (onDismissRequest, dragHandle, etc.) unchanged.
- Around line 169-179: The loop in SelectedReactionsMenu emits a trailing Spacer
after the last UserReactionRow causing extra bottom padding; remove the Spacer
from the userReactions.forEach block and instead set verticalArrangement =
Arrangement.spacedBy(8.dp) on the surrounding Column (where UserReactionRow
items are rendered) so spacing is applied only between items; keep the existing
conditional onClick logic for UserReactionRow (item.isMine) unchanged.
- Line 84: Remove the redundant explicit dragHandle assignment from the
ModalBottomSheet call in SelectedReactionsMenu (the line setting dragHandle = {
BottomSheetDefaults.DragHandle() }); ModalBottomSheet already defaults to that
drag handle, so simply delete the dragHandle parameter and leave the rest of the
ModalBottomSheet invocation intact (look for the ModalBottomSheet(...) in
SelectedReactionsMenu.kt).
In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/MessagesScreen.kt`:
- Around line 503-530: The code redundantly uses a null-safe access on
selectedMessageState after an is-check; update the ReactionsMenu call to use the
smart-cast directly by replacing selectedMessageState?.ownCapabilities ?:
setOf() with selectedMessageState.ownCapabilities (or
selectedMessageState.ownCapabilities.ifEmpty { setOf() } if you want a non-empty
fallback) so the ownCapabilities parameter references the non-null
SelectedMessageReactionsState instance used in this branch (within the
ChatTheme.componentFactory.ReactionsMenu invocation).
In
`@stream-chat-android-previewdata/src/main/kotlin/io/getstream/chat/android/previewdata/PreviewReactionData.kt`:
- Around line 52-64: The preview data uses Date() in
List<Reaction>.toReactionGroups(), which yields time-dependent timestamps;
replace Date() with a fixed deterministic timestamp (e.g., a shared constant
like PREVIEW_DATE or Date(0)) so firstReactionAt and lastReactionAt are stable
for previews/tests; update the toReactionGroups() implementation (and any
references such as oneReactionGroup and manyReactionGroups) to use that fixed
timestamp constant.
...ain/java/io/getstream/chat/android/compose/ui/components/selectedmessage/ReactionCountRow.kt
Show resolved
Hide resolved
...main/java/io/getstream/chat/android/compose/ui/components/selectedmessage/UserReactionRow.kt
Show resolved
Hide resolved
...hat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/StreamColors.kt
Show resolved
Hide resolved
...hat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/StreamColors.kt
Show resolved
Hide resolved
...ain/java/io/getstream/chat/android/compose/ui/components/selectedmessage/ReactionCountRow.kt
Outdated
Show resolved
Hide resolved
stream-chat-android-compose/src/main/res/drawable/stream_compose_ic_reaction_add.xml
Outdated
Show resolved
Hide resolved
...ava/io/getstream/chat/android/compose/ui/components/selectedmessage/SelectedReactionsMenu.kt
Outdated
Show resolved
Hide resolved
...e/src/test/kotlin/io/getstream/chat/android/compose/ui/messages/SelectedReactionsMenuTest.kt
Outdated
Show resolved
Hide resolved
...e/src/test/kotlin/io/getstream/chat/android/compose/ui/messages/SelectedReactionsMenuTest.kt
Outdated
Show resolved
Hide resolved
...e/src/test/kotlin/io/getstream/chat/android/compose/ui/messages/SelectedReactionsMenuTest.kt
Outdated
Show resolved
Hide resolved
29bd675 to
6a9f716
Compare
6a9f716 to
444e6eb
Compare
...ava/io/getstream/chat/android/compose/ui/components/selectedmessage/SelectedReactionsMenu.kt
Outdated
Show resolved
Hide resolved
|


Goal
Update design of the sheet with a message's reaction details, i.e. what reactions are there and who reacted.
Implementation
ModalBottomSheetto provide the expected experience (draggable sheet)ReactionsMenu+ReactionsMenuContentso the menu container & content can be customized independentlymessageOptionsUserReactionAlignment🎨 UI Changes
Testing
Can be checked in the sample
Summary by CodeRabbit
New Features
UI/UX Changes
Refactoring