Skip to content

compose: Render no-caption voice message replies on the bubble background#6552

Open
andremion wants to merge 5 commits into
developfrom
feature/and-1288-quoted-message-background
Open

compose: Render no-caption voice message replies on the bubble background#6552
andremion wants to merge 5 commits into
developfrom
feature/and-1288-quoted-message-background

Conversation

@andremion

@andremion andremion commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Goal

When a voice message reply has no caption, the player rendered inside the darker attachment card and carried the attachment section padding plus the bubble's bottom spacer, instead of sitting on the message bubble background like a standalone voice message. The finalized Figma Reply States show a no-caption voice reply on the bubble background with no card, so this aligns the Compose SDK with the design.

The quoted (replied-to) preview keeps its darker attachment background, which is correct per the same spec, only the reply's own player was wrong.

Resolves AND-1288

Implementation

  • AudioRecordAttachmentContent: gate the player's darker attachment card and its section padding on caption presence (hasCaption). A no-caption reply renders on the bubble background with only the player's own content padding; a captioned reply keeps the card, and the caption provides the bottom padding. Extracted a stateless AudioRecordAttachmentContentItems so the reply states can be previewed and snapshot-tested.
  • MessageContent: skip the bubble's bottom spacer when the trailing content is a no-caption voice recording, which already supplies its own bottom padding. Media and file sections have no bottom padding of their own, so they still rely on the shared spacer.

🎨 UI Changes

Voice message reply without a caption, in the message list. Before, the player sits in the darker attachment card with extra inset and a doubled bottom padding; after, it sits on the bubble background like a standalone voice message. A captioned voice reply is unchanged (it keeps the card).

Before After
before_voice_reply after_voice_reply

Testing

  1. Open a channel and reply to a voice message by recording and sending a voice message with no caption.
  2. The reply's player should render on the message bubble background (no darker card), aligned like a standalone voice message, with no extra bottom padding.
  3. Reply again with a voice message that includes a caption. The player should keep the darker card, and the caption should sit below it.
  4. Confirm a standalone voice message (not a reply) is unchanged.

Unit tests: added audio record reply without caption and audio record reply with caption Paparazzi snapshots to AttachmentsContentTest, covering the no-card and card states.

Summary by CodeRabbit

  • New Features

    • Added reply-style audio recording attachment previews for both caption and no-caption message layouts.
    • Improved audio attachment rendering so playback state and controls are passed through consistently.
  • Bug Fixes

    • Adjusted spacing around messages with recordings to better match text and attachment combinations.
    • Updated audio attachment layout padding to respond to caption presence more accurately.
  • Tests

    • Added snapshot coverage for the new audio reply attachment states in both light and dark mode.

andremion added 2 commits July 7, 2026 11:14
…ound

A voice message reply with no caption showed its player in the darker
attachment card, because shouldBeDisplayedAsFullSizeAttachment() returns
false for replies. Gate the darker card on caption presence instead, so
a no-caption reply renders the player on the message bubble background and
a captioned one keeps the card.

Extract the stateless AudioRecordAttachmentContentItems so the reply states
can be previewed and snapshot-tested.
The no-caption reply player already renders on the bubble background, but
it still carried the attachment-card section padding, which belongs only
when the player is a card sharing the bubble with a caption. Gate the
section padding on caption presence so the no-caption player uses only its
own content padding, matching the design.
@andremion andremion added the pr:bug Bug fix label Jul 7, 2026
@andremion

Copy link
Copy Markdown
Contributor Author

@CodeRabbit review

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

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

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

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, or the PR is bot-authored.
  • An issue is linked (Linear ticket or GitHub issue), or the PR is bot-authored.

🎉 Great job! This PR is ready for review.

@andremion andremion added pr:improvement Improvement and removed pr:bug Bug fix labels Jul 7, 2026
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

SDK Size Comparison 📏

SDK Before After Difference Status
stream-chat-android-client 5.93 MB 5.93 MB 0.00 MB 🟢
stream-chat-android-ui-components 11.20 MB 11.20 MB 0.00 MB 🟢
stream-chat-android-compose 12.66 MB 12.67 MB 0.00 MB 🟢

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

AudioRecordAttachmentContent now hoists AudioPlayerState collection to the parent composable and passes it with playback callbacks down to child items, replacing direct viewModel access. Padding logic switches to caption-based checks. MessageContent adjusts bottom spacer for recordings without text. New reply preview composables and tests were added.

Changes

Audio record attachment refactor

Layer / File(s) Summary
Hoist AudioPlayerState and wire callbacks
AudioRecordAttachmentContent.kt
Player state is collected once in the parent composable and passed with play/speed/seek callbacks into content items, replacing direct viewModel access; unused import removed.
Padding and spacer adjustments
AudioRecordAttachmentContent.kt, MessageContent.kt
Content item padding now derives from hasCaption; the message bottom spacer is suppressed when recordings exist without text.
Reply preview composables, generated API, and tests
AudioRecordAttachmentContent.kt, stream-chat-android-compose.api, AttachmentsContentTest.kt
Preview URLs centralized into a shared constant, existing previews made private, new reply-content composables (with/without caption) and previews added, generated Compose lambda getters updated, and snapshot tests added for the new reply variants.

Estimated code review effort: 2 (Simple) | ~15 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AudioRecordAttachmentContent
  participant ViewModel
  participant AudioRecordAttachmentContentItems
  participant AudioRecordAttachmentContentItem

  AudioRecordAttachmentContent->>ViewModel: collectAsStateWithLifecycle(state)
  AudioRecordAttachmentContent->>AudioRecordAttachmentContentItems: pass playerState, callbacks
  AudioRecordAttachmentContentItems->>AudioRecordAttachmentContentItem: pass onPlayToggleClick, onPlaySpeedClick, onThumbDragStart, onThumbDragStop
  AudioRecordAttachmentContentItem-->>ViewModel: trigger playback action via callback
Loading

Suggested reviewers: gpunto

Poem

A rabbit taps play with a twitch of the nose,
State hoisted up high where the audio flows,
No caption? More padding to make the beat land,
Replies now preview just as we planned,
Thump-thump goes my heart for this tidy landing! 🐰🎧

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: rendering no-caption voice message replies on the bubble background.
Description check ✅ Passed The description covers goal, implementation, UI changes, and testing, and is mostly complete despite placeholders for screenshots and checklist items.
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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/and-1288-quoted-message-background

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

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/MessageContent.kt`:
- Around line 306-307: The bottom-padding guard in MessageContent should account
for custom/unknown attachments in addition to recordings, since
`recordingSuppliesBottomPadding` currently only checks `info.hasRecordings` and
`message.text.isEmpty()`. Update the logic around
`recordingSuppliesBottomPadding` and the `!info.displaysFullSizeAttachment`
condition so that a no-caption recording combined with later custom attachments
still keeps the spacer when `CustomAttachmentContent` is rendered last. Use the
existing attachment flags in `MessageContent`/`info` (especially `hasUnknown`)
to decide when the last attachment still needs bottom inset.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: db97add8-67c9-4979-8d80-6461c03c0729

📥 Commits

Reviewing files that changed from the base of the PR and between 26a7df3 and 4c34fbd.

⛔ Files ignored due to path filters (2)
  • stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.attachments.content_AttachmentsContentTest_audio_record_reply_with_caption.png is excluded by !**/*.png
  • stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.attachments.content_AttachmentsContentTest_audio_record_reply_without_caption.png is excluded by !**/*.png
📒 Files selected for processing (4)
  • stream-chat-android-compose/api/stream-chat-android-compose.api
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/content/AudioRecordAttachmentContent.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/MessageContent.kt
  • stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/attachments/content/AttachmentsContentTest.kt

…eplies

A voice recording supplies its own bottom padding, unlike media and file
sections which rely on the bubble's shared bottom spacer. When a no-caption
voice recording was the trailing content, the player's bottom padding and
the spacer stacked and doubled the bottom inset. Skip the spacer in that
case so the bubble bottom matches the design.
@andremion andremion force-pushed the feature/and-1288-quoted-message-background branch from 4c34fbd to e26fc58 Compare July 7, 2026 10:28
Render the no-caption, caption, and recording-plus-custom-attachment reply
cases through MessageContainer, exercising the bubble bottom-spacer logic and
the viewModel-backed audio recording content that the Paparazzi snapshots
cannot reach.
@andremion andremion marked this pull request as ready for review July 7, 2026 10:50
@andremion andremion requested a review from a team as a code owner July 7, 2026 10:50

@gpunto gpunto 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.

Just a doubt if this change should be propagated to other attachment types

Comment on lines -106 to +134
val shouldBeFullSize = attachmentState.message.shouldBeDisplayedAsFullSizeAttachment()
val hasCaption = attachmentState.message.text.isNotEmpty()

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.

Does this mean that now voice recording attachments diverge from other attachment types? e.g. a file attachment will still be rendered with the background in the quoted message case, right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, on purpose, only the voice player drops the background when there's no caption; file and media replies keep theirs.
Here are the Figma specs https://www.figma.com/design/BJppI3JYlPy3XeEu5MZY1E/SDK-Components-Library?node-id=4011-115610&m=dev

@sonarqubecloud

sonarqubecloud Bot commented Jul 7, 2026

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

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

See analysis details on SonarQube Cloud

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr:improvement Improvement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants