Update file picker UI and refactor radio controls #6216
Update file picker UI and refactor radio controls #6216andremion wants to merge 5 commits intoredesign/update-stream-design-tokensfrom
Conversation
PR checklist ✅All required conditions are satisfied:
🎉 Great job! This PR is ready for review. |
|
WalkthroughThis PR introduces a new RadioButton composable, refactors file and attachment picker UI components with clickable headers and chevron icons, updates radio control color tokens, and modifies the public API surface of ComposableSingletons classes by renaming and adding lambda field accessors. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 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: 3
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/components/attachments/files/FilesPicker.kt (1)
127-167:⚠️ Potential issue | 🟠 MajorExpose selection semantics on the file row.
This row is the actual click target, but it doesn't expose the selection state or control type to assistive technologies. The trailing
RadioCheck/RadioButtonhaveonCheckedChange = null, making them non-interactive displays. Use.selectable()with the appropriateRoleto provide semantic information about whether the item is selected and whether it behaves as a checkbox or radio button.♿ Suggested fix
+import androidx.compose.foundation.selection.selectable +import androidx.compose.ui.semantics.Role + Row( Modifier .fillMaxWidth() - .clickable { onItemSelected(fileItem) } + .selectable( + selected = fileItem.isSelected, + role = if (allowMultipleSelection) Role.Checkbox else Role.RadioButton, + onClick = { onItemSelected(fileItem) }, + ) .padding(StreamTokens.spacingSm), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(StreamTokens.spacingSm), ) {🤖 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/attachments/files/FilesPicker.kt` around lines 127 - 167, The row currently uses .clickable and leaves the radio controls non-interactive; replace the Row's .clickable with Modifier.selectable(selected = fileItem.isSelected, onClick = { onItemSelected(fileItem) }, role = if (allowMultipleSelection) Role.Checkbox else Role.Radio) so accessibility exposes selection state and control type; keep the trailing RadioCheck/RadioButton as visual indicators but leave their onCheckedChange = null (read-only) since the selectable on the Row handles interaction.
🤖 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 3594-3595: Add a changelog entry under the UNRELEASED section's
"⚠️ Changed" for stream-chat-android-compose documenting that the
StreamDesign.Colors ABI changed due to the new controlRadioButtonIndicator token
(the public constructor/copy signature for StreamDesign.Colors was modified),
and note that apps that instantiate or copy StreamDesign.Colors directly will
need recompilation and may require updates to custom theme code; repeat the same
note for the related ABI changes you observed (the other StreamDesign.Colors
constructor/copy signature changes referenced at the other locations).
In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/attachments/files/FilesPicker.kt`:
- Around line 98-101: The chevron Icon in FilesPicker (the Icon call using
painterResource(id = R.drawable.stream_compose_ic_chevron_right)) is decorative
but currently has a non-null contentDescription; change its contentDescription
to null so the chevron is ignored by accessibility services (i.e., update the
Icon in FilesPicker.kt to use contentDescription = null).
In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/AttachmentFilePicker.kt`:
- Around line 219-223: Change the Icon in AttachmentFilePicker's chevron to use
contentDescription = null (replace the current contentDescription =
stringResource(id = contentDescriptionId)), then remove the now-unused
contentDescriptionId parameter from the RequestAdditionalAccessButton
declaration and its usages (all callers currently pass the same textId) and
update callers to stop supplying that argument; ensure
RequestAdditionalAccessButton and any downstream references compile after
removing the parameter.
---
Outside diff comments:
In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/attachments/files/FilesPicker.kt`:
- Around line 127-167: The row currently uses .clickable and leaves the radio
controls non-interactive; replace the Row's .clickable with
Modifier.selectable(selected = fileItem.isSelected, onClick = {
onItemSelected(fileItem) }, role = if (allowMultipleSelection) Role.Checkbox
else Role.Radio) so accessibility exposes selection state and control type; keep
the trailing RadioCheck/RadioButton as visual indicators but leave their
onCheckedChange = null (read-only) since the selectable on the Row handles
interaction.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 9067aa7a-a5be-45fa-84c1-4644dc704c82
⛔ Files ignored due to path filters (37)
stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.attachments.preview_MediaGalleryPreviewOptionsMenuTest_media_gallery_options_menu_for_other_user.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.attachments.preview_MediaGalleryPreviewOptionsMenuTest_media_gallery_options_menu_for_own_user.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.attachments.preview_MediaGalleryPreviewScreenTest_media_gallery_screen_with_options_menu.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.channel.attachments_ChannelFilesAttachmentsContentTest_content.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.channel.attachments_ChannelFilesAttachmentsContentTest_content_in_dark_mode.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.channel.attachments_ChannelFilesAttachmentsContentTest_loading_more.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.channel.attachments_ChannelFilesAttachmentsContentTest_loading_more_in_dark_mode.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.channel.attachments_ChannelMediaAttachmentsContentTest_loading_more_in_dark_mode.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.channel.info_ChannelInfoMemberInfoModalSheetTest_banned_member.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.channel.info_ChannelInfoMemberInfoModalSheetTest_banned_member_in_dark_mode.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.channel.info_ChannelInfoMemberInfoModalSheetTest_not_banned_member.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.channel.info_ChannelInfoMemberInfoModalSheetTest_not_banned_member_in_dark_mode.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.channel.info_DirectChannelInfoContentTest_content.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.channel.info_DirectChannelInfoContentTest_content_in_dark_mode.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.channel.info_DirectChannelInfoContentTest_loading_in_dark_mode.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.channel.info_GroupChannelInfoContentTest_collapsed_members.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.channel.info_GroupChannelInfoContentTest_collapsed_members_in_dark_mode.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.channel.info_GroupChannelInfoContentTest_expanded_members.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.channel.info_GroupChannelInfoContentTest_expanded_members_in_dark_mode.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.channel.info_GroupChannelInfoContentTest_loading_in_dark_mode.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.channels_ChannelListTest_loaded_channels.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.channels_ChannelListTest_loading_more_channels.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.channels_ChannelListTest_search_results.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.channels_ChannelsTest_loaded_channels.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.attachments.files_FilesPickerTest_multiple_selection.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.components.attachments.files_FilesPickerTest_single_selection.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.messages.attachments_AttachmentFilePickerTest_multiple_selection.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.messages.attachments_AttachmentFilePickerTest_single_selection.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.pinned_PinnedMessageListTest_loaded_pinned_messages.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.pinned_PinnedMessageListTest_loading_more_pinned_messages.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.threads_ThreadItemTest_threadItem.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.threads_ThreadListTest_loaded_threads.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.threads_ThreadListTest_loaded_threads_with_unread_banner.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.threads_ThreadListTest_loading_more_threads.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.threads_UnreadThreadsBannerTest_multiple_unread_threads.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.threads_UnreadThreadsBannerTest_no_unread_threads.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.threads_UnreadThreadsBannerTest_one_unread_thread.pngis excluded by!**/*.png
📒 Files selected for processing (8)
stream-chat-android-compose/api/stream-chat-android-compose.apistream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/StreamHorizontalDivider.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/attachments/files/FilesPicker.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/common/RadioControls.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/AttachmentFilePicker.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/theme/StreamDesign.ktstream-chat-android-compose/src/main/res/drawable/stream_compose_ic_chevron_right.xmlstream-chat-android-compose/src/main/res/drawable/stream_compose_ic_more_files.xml
💤 Files with no reviewable changes (1)
- stream-chat-android-compose/src/main/res/drawable/stream_compose_ic_more_files.xml
| public synthetic fun <init> (JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIIILkotlin/jvm/internal/DefaultConstructorMarker;)V | ||
| public synthetic fun <init> (JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJLkotlin/jvm/internal/DefaultConstructorMarker;)V |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Search for migration and changelog files
docs="$(fd -t f -i -E 'node_modules' -E '.git' 'migration|changelog' . || true)"
if [ -z "$docs" ]; then
echo "No migration or changelog files found in initial search."
# Try broader search
docs="$(find . -maxdepth 3 -type f \( -iname '*migration*' -o -iname '*changelog*' -o -iname 'CHANGES*' \) 2>/dev/null || true)"
fi
if [ -z "$docs" ]; then
echo "No migration or changelog files found after broad search."
exit 1
fi
echo "=== Files found ==="
echo "$docs"
echo ""
echo "=== Searching for relevant content ==="
echo "$docs" | xargs -r grep -l -i 'StreamDesign\|controlRadioButton\|RadioControls\|RadioCheck\|v7\|migration\|breaking' 2>/dev/null | head -20Repository: GetStream/stream-chat-android
Length of output: 183
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# First, get the size of CHANGELOG.md to decide how to read it
wc -l ./CHANGELOG.md
echo ""
echo "=== Searching for StreamDesign or controlRadioButton mentions ==="
rg -n -i 'StreamDesign|controlRadioButton|theme.*Colors|Colors.*constructor|breaking.*change|migration' ./CHANGELOG.md | head -50Repository: GetStream/stream-chat-android
Length of output: 12774
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Searching for v7 or recent theme changes in CHANGELOG ==="
rg -n -i '^#+\s+(v7|7\.|theme|color|radio)' ./CHANGELOG.md | head -20
echo ""
echo "=== Searching for StreamDesign or design tokens ==="
rg -n -i 'streamdesign|design.*token|theme.*color|colors.*constructor' ./CHANGELOG.md | head -20
echo ""
echo "=== Looking at the very top of CHANGELOG to see current version context ==="
head -50 ./CHANGELOG.mdRepository: GetStream/stream-chat-android
Length of output: 134
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Top of CHANGELOG.md ==="
head -100 ./CHANGELOG.md
echo ""
echo "=== Any mention of radio, indicator, or control ==="
rg -n -i 'radio|indicator|control.*button|button.*control' ./CHANGELOG.md | head -20Repository: GetStream/stream-chat-android
Length of output: 4908
Add changelog entry documenting the StreamDesign.Colors ABI change.
The new controlRadioButtonIndicator token changes the public StreamDesign.Colors constructor/copy ABI. This breaking change is not documented in the CHANGELOG. Add an entry to the "stream-chat-android-compose in the UNRELEASED section, documenting that StreamDesign.Colors constructor signature has changed and apps that instantiate or copy this type directly will need recompilation and may require custom theme code updates.
Same applies to related changes at lines 3670-3671 and 3791-3791.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@stream-chat-android-compose/api/stream-chat-android-compose.api` around lines
3594 - 3595, Add a changelog entry under the UNRELEASED section's "⚠️ Changed"
for stream-chat-android-compose documenting that the StreamDesign.Colors ABI
changed due to the new controlRadioButtonIndicator token (the public
constructor/copy signature for StreamDesign.Colors was modified), and note that
apps that instantiate or copy StreamDesign.Colors directly will need
recompilation and may require updates to custom theme code; repeat the same note
for the related ABI changes you observed (the other StreamDesign.Colors
constructor/copy signature changes referenced at the other locations).
| Icon( | ||
| painter = painterResource(id = R.drawable.stream_compose_ic_chevron_right), | ||
| contentDescription = stringResource(id = R.string.stream_compose_send_attachment), | ||
| tint = ChatTheme.colors.textSecondary, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Read the FilesPicker.kt file around lines 98-101
cat -n stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/attachments/files/FilesPicker.kt | sed -n '90,110p'Repository: GetStream/stream-chat-android
Length of output: 1013
🏁 Script executed:
# Check the broader context of this Icon - what is the parent composable and what does it do?
cat -n stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/attachments/files/FilesPicker.kt | sed -n '75,110p'Repository: GetStream/stream-chat-android
Length of output: 1534
🏁 Script executed:
# Check what the string resource "stream_compose_send_attachment" actually says
rg -t kotlin "stream_compose_send_attachment" stream-chat-android-compose/src/main/res/values/ | head -5Repository: GetStream/stream-chat-android
Length of output: 55
🏁 Script executed:
# Search for the string resource definition in all string files
rg "stream_compose_send_attachment" stream-chat-android-compose/src/main/res/Repository: GetStream/stream-chat-android
Length of output: 489
Set the chevron's contentDescription to null to mark it as decorative.
The Icon uses contentDescription = stringResource(id = R.string.stream_compose_send_attachment) ("Send attachment"), which is unrelated to the chevron's visual role. Since the parent Row's text already describes the action ("Recent files") and the icon is non-interactive decoration, keeping a non-null contentDescription will cause TalkBack to announce a misleading extra label. Use contentDescription = null instead.
🤖 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/attachments/files/FilesPicker.kt`
around lines 98 - 101, The chevron Icon in FilesPicker (the Icon call using
painterResource(id = R.drawable.stream_compose_ic_chevron_right)) is decorative
but currently has a non-null contentDescription; change its contentDescription
to null so the chevron is ignored by accessibility services (i.e., update the
Icon in FilesPicker.kt to use contentDescription = null).
| Icon( | ||
| painter = painterResource(id = R.drawable.stream_compose_ic_chevron_right), | ||
| contentDescription = stringResource(id = contentDescriptionId), | ||
| tint = ChatTheme.colors.textSecondary, | ||
| ) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's find and examine the AttachmentFilePicker.kt file
find . -name "AttachmentFilePicker.kt" -type fRepository: GetStream/stream-chat-android
Length of output: 198
🏁 Script executed:
# Read the file to understand the context around lines 219-223
cat -n stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/AttachmentFilePicker.kt | head -250 | tail -80Repository: GetStream/stream-chat-android
Length of output: 3323
🏁 Script executed:
# Let's also check the RequestAdditionalAccessButton function definition
rg -A 30 "fun RequestAdditionalAccessButton" stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/attachments/AttachmentFilePicker.ktRepository: GetStream/stream-chat-android
Length of output: 1086
Set the chevron Icon contentDescription to null.
The visible row text already announces the action, so the Icon repeating the same contentDescription creates a duplicate spoken announcement. Change line 221 to contentDescription = null, then remove the contentDescriptionId parameter from RequestAdditionalAccessButton since it's unused after this change (all callers pass the same value as textId anyway).
🤖 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/attachments/AttachmentFilePicker.kt`
around lines 219 - 223, Change the Icon in AttachmentFilePicker's chevron to use
contentDescription = null (replace the current contentDescription =
stringResource(id = contentDescriptionId)), then remove the now-unused
contentDescriptionId parameter from the RequestAdditionalAccessButton
declaration and its usages (all callers currently pass the same textId) and
update callers to stop supplying that argument; ensure
RequestAdditionalAccessButton and any downstream references compile after
removing the parameter.
SDK Size Comparison 📏
|


Goal
Apply the updated Stream Design System tokens to the radio/check controls and redesign the file picker UI to align with the new design spec.
Implementation
StreamDesign.Colors— updated colors from FigmaRadioControls.kt(previouslyRadioCheck.kt) — introduced aRadioButtoncomponent (filled dot indicator), extracted a privateRadioControlBasecomposable that owns the shared container logic (sizing, shape,4-state modifier, click handling), and merged both
RadioButtonandRadioCheckinto a single file.FilesPicker/AttachmentFilePicker— the trailing icon is replaced with a chevron;RadioButtonis used forsingle-selection mode and
RadioCheckfor multi-selection mode;StreamHorizontalDividerseparates items.StreamHorizontalDivider— default thickness updated from 0.5dp to 1dp.stream_compose_ic_more_filesdrawable withstream_compose_ic_chevron_right.🎨 UI Changes
Please check the snapshot files
Testing
Summary by CodeRabbit
Release Notes
New Features
UI Improvements
Theme Updates