Conversation
PR checklist ✅All required conditions are satisfied:
🎉 Great job! This PR is ready for review. |
SDK Size Comparison 📏
|
|
WalkthroughThis pull request integrates video playback capabilities into the media gallery preview system. Changes introduce a lifecycle-managed Player state, video playback controls, immersive mode toggling via media taps, and UI updates across the gallery components. A ModalBottomSheet replaces the previous overlay in options menu, and external component libraries are utilized for button controls. Changes
Sequence DiagramsequenceDiagram
participant UI as MediaGalleryPreviewScreen
participant PSM as rememberMediaGalleryPlayerState
participant PS as MediaGalleryPlayerState
participant GME as GalleryMediaEffect
participant Pager as MediaGalleryPager
participant VPC as VideoPlaybackControls
participant Player as Player
UI->>PSM: Initialize with onPlaybackError callback
PSM->>PS: Create state holder
PSM->>Player: Create & bind to lifecycle
PSM-->>UI: Return playerState
UI->>GME: Pass playerState, currentPage, attachments
GME->>PS: Query current player
GME->>Player: Pause previous attachment
GME->>PS: Select attachment at currentPage
alt Video attachment
GME->>Player: Prepare MediaItem from URL
GME->>Player: Seek to savedPosition
GME->>Player: Start playback
end
UI->>Pager: Pass player & onMediaClick
Pager->>VPC: Mount with player instance
VPC->>Player: Attach listener for state changes
VPC->>Player: Poll position while playing
UI->>VPC: User interacts (play/pause, seek, speed)
VPC->>Player: Execute command (play, seek, setPlaybackParameters)
Player-->>VPC: Listener callback with state update
VPC-->>UI: Render updated controls
Pager->>UI: User taps media (onMediaClick)
UI->>UI: Toggle immersive mode
UI->>Pager: Update header/footer visibility
Estimated Code Review Effort🎯 4 (Complex) | ⏱️ ~60 minutes 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)
📝 Coding Plan
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 Tip CodeRabbit can approve the review once all CodeRabbit's comments are resolved.Enable the |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/internal/StreamMediaPlayerContent.kt (1)
223-229:⚠️ Potential issue | 🟠 MajorDon't hard-disable the only controller on this player view.
StreamMediaPlayerContentis still used directly bystream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/MediaPreviewActivity.kt:64-84, and that screen does not render the new overlay controls. On Line 228, forcinguseController = falseremoves pause/seek UI there, while the existingshowController()call becomes a no-op.🎛️ Suggested shape
internal fun createPlayerView( context: Context, player: Player, + useController: Boolean = true, ): PlayerView { val playerView = LayoutInflater.from(context) .inflate(R.layout.stream_compose_player_view, null) as PlayerView return playerView.apply { this.player = player - useController = false + this.useController = useController setShowBuffering(PlayerView.SHOW_BUFFERING_NEVER) } }Then keep the default in
StreamMediaPlayerContent, and passuseController = falseonly from the redesigned gallery path.🤖 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/attachments/preview/internal/StreamMediaPlayerContent.kt` around lines 223 - 229, createPlayerView currently forces useController = false which breaks controls for callers like MediaPreviewActivity and makes showController() a no-op; remove the hard-disable (delete or stop setting useController in createPlayerView inside StreamMediaPlayerContent) so the PlayerView uses its default controller behavior, and instead set useController = false only from the redesigned gallery code path that requires it (i.e., update the caller that intentionally wants no controller to explicitly set playerView.useController = false after createPlayerView).stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/channel/attachments/ChannelMediaAttachmentsPreviewScreen.kt (1)
171-178:⚠️ Potential issue | 🟠 MajorThis pager wiring is only half of the new gallery flow.
By passing
player = playerState.playerhere you opt intoMediaGalleryVideoPage, which now uses a controllerless player view. Because this screen still keeps the old bottom bar and does not passonMediaClick, videos here end up with no replacement playback controls and media taps cannot trigger immersive mode.🤖 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/channel/attachments/ChannelMediaAttachmentsPreviewScreen.kt` around lines 171 - 178, The pager is being forced into the new controllerless video page by passing playerState.player to MediaGalleryPager, which prevents the old bottom bar controls and onMediaClick handling; remove the player = playerState.player argument (or pass null) so MediaGalleryPager/MediaGalleryVideoPage falls back to the controller-backed view, and add/forward an onMediaClick callback from ChannelMediaAttachmentsPreviewScreen into MediaGalleryPager (and ensure the existing bottom bar code still renders) so taps trigger immersive mode and playback controls are present.stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/internal/MediaGalleryPage.kt (1)
204-223:⚠️ Potential issue | 🔴 CriticalImport
withTimeoutOrNullbefore landing this.Line 208 calls
withTimeoutOrNull, but this file never importskotlinx.coroutines.withTimeoutOrNull, so the new tap/double-tap block will not compile.🔧 Proposed fix
import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.withTimeoutOrNull import kotlin.math.abs🤖 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/attachments/preview/internal/MediaGalleryPage.kt` around lines 204 - 223, The new double-tap logic uses withTimeoutOrNull but the file lacks the kotlinx.coroutines import, so add an import for kotlinx.coroutines.withTimeoutOrNull at the top of the file; locate the pointerInput block (contains coroutineScope, awaitEachGesture, awaitFirstDown, DoubleTapTimeoutMs and withTimeoutOrNull) and add the missing import so the withTimeoutOrNull call compiles.stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/MediaGalleryPreviewScreen.kt (1)
147-155:⚠️ Potential issue | 🟡 MinorInconsistent header modifier between stateful and stateless overloads.
The stateful overload uses
Modifier.fillMaxWidth()(line 149) while the stateless overload usesModifier.fillMaxWidth().height(56.dp)(lines 296-298). This inconsistency means the header will have different default heights depending on which overload is used.Consider aligning both defaults:
Proposed fix
header: `@Composable` (attachments: List<Attachment>, currentPage: Int) -> Unit = { _, _ -> MediaGalleryPreviewHeader( - modifier = Modifier.fillMaxWidth(), + modifier = Modifier + .fillMaxWidth() + .height(56.dp), message = viewModel.message, connectionState = viewModel.connectionState, onLeadingContentClick = onHeaderLeadingContentClick, onTrailingContentClick = onHeaderTrailingContentClick, ) },🤖 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/attachments/preview/MediaGalleryPreviewScreen.kt` around lines 147 - 155, The stateful overload's header parameter uses Modifier.fillMaxWidth() while the stateless overload sets Modifier.fillMaxWidth().height(56.dp), causing different default header heights; update the stateful overload's header default (the header: `@Composable` (attachments: List<Attachment>, currentPage: Int) -> Unit lambda that calls MediaGalleryPreviewHeader) to use the same modifier as the stateless version (add .height(56.dp) to Modifier.fillMaxWidth()) so both overloads render the header at the same default height, ensuring consistency for MediaGalleryPreviewHeader across both implementations.
🧹 Nitpick comments (6)
stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/attachments/preview/MediaGalleryPreviewActivityTest.kt (3)
142-156: Test lacks behavior assertion.Similar to the delete test,
should save on save option clickonly exercises the click without verifying save behavior. Thescenariovariable is unused. Consider adding mock verification or renaming to clarify the test's actual scope.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/attachments/preview/MediaGalleryPreviewActivityTest.kt` around lines 142 - 156, The test should assert that the save action occurs rather than only performing the click: update the test `should save on save option click` to either (A) verify the save flow by mocking or spying the component that handles saves (e.g., inject a mock save handler into `MediaGalleryPreviewActivity` or observe a result/Intent from `ActivityScenario`), and assert that the handler was called or the expected Intent/result was produced after clicking the "Save media" option, or (B) if you intentionally only exercise UI interaction, rename the test to reflect that it only performs the click; use the existing `createIntent(message)`/`PreviewMessageData.messageWithUserAndAttachment` and `ActivityScenario.launchActivityForResult<MediaGalleryPreviewActivity>(intent)`/`composeTestRule` to obtain and assert the expected behavior.
124-140: Test lacks behavior assertion.The test name
should delete on delete option clicksuggests it verifies delete behavior, but the test only exercises the click without asserting any outcome. Thescenariovariable is unused. Consider either:
- Adding an assertion (e.g., verify delete API was called via mock verification)
- Renaming to clarify intent (e.g.,
should not crash on delete option click)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/attachments/preview/MediaGalleryPreviewActivityTest.kt` around lines 124 - 140, The test function `should delete on delete option click` in MediaGalleryPreviewActivityTest currently only performs a click and doesn't assert behavior; update the test to either verify the delete flow was triggered (for example, mock and verify the chat client's delete method or repository method was called after clicking the "Delete" option—use the existing mocks like `mockClientState`/client mock or whatever deletion API mock exists in the test harness and assert it was invoked with `PreviewMessageData.messageWithUserAndAttachment`), or rename the test to something like `should not crash on delete option click` if no behavior should be asserted; modify `should delete on delete option click` (or add verification) and use the `scenario`/mocks to perform a verify assertion.
158-168: Inconsistent interaction pattern.The
should share on share option clicktest still usesperformClick()directly (line 166) while all the other option click tests were migrated to the semantic action pattern. Consider aligning for consistency, or document why the share button differs (e.g., it's a toolbar icon vs. bottom sheet item).♻️ Suggested alignment (if applicable)
If the Share button should follow the same pattern:
- composeTestRule.onNodeWithContentDescription("Share").performClick() + composeTestRule.onNodeWithContentDescription("Share") + .performSemanticsAction(SemanticsActions.OnClick)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/attachments/preview/MediaGalleryPreviewActivityTest.kt` around lines 158 - 168, The test MediaGalleryPreviewActivityTest.should share on share option click uses composeTestRule.onNodeWithContentDescription("Share").performClick(), which is inconsistent with the other option-click tests that use the semantic-action pattern; replace the direct performClick call with the same semantic-action invocation used elsewhere (i.e., invoke the node's semantic action for click) so the test aligns with the other option click tests, or if the Share control is intentionally different (toolbar icon vs bottom-sheet item), add a short comment in the test explaining why performClick is kept.stream-chat-android-compose/api/stream-chat-android-compose.api (1)
864-865: Document thisMediaGalleryPreviewScreenAPI break in the v7 migration notes.These overloads change the public parameter model and the exposed slot callback shape, so custom preview integrations will need source updates. If this is not already covered elsewhere in the PR, please add an entry to
MIGRATION_TO_V7.mdor the changelog rather than introducing compatibility shims.Based on learnings, PRs against major version branches (e.g.,
v7) are allowed to introduce breaking public API changes, and maintainers prefer documenting migration paths inMIGRATION_TO_V7.md/CHANGELOGfor function signature changes.🤖 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 864 - 865, The public overloads of MediaGalleryPreviewScreen changed parameter types and callback shapes; document this breaking change in the v7 migration notes by adding an entry to MIGRATION_TO_V7.md (or the CHANGELOG) explaining the new signatures (both MediaGalleryPreviewScreen overloads), what changed (model-based parameters vs viewmodel-based parameters and updated FunctionX slot types), and provide a minimal migration snippet showing how callers should adapt custom preview integrations to the new parameter list and callback shapes; reference the exact symbol name MediaGalleryPreviewScreen and list both overload variants so users can find and update usages.stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/MediaGalleryPreviewScreen.kt (2)
722-725: Consider updating content description to match the new icon.The icon changed from a close (X) to a back arrow, but the content description still references "cancel" (
R.string.stream_compose_cancel). For accessibility consistency, consider using a "back" or "navigate back" description.🤖 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/attachments/preview/MediaGalleryPreviewScreen.kt` around lines 722 - 725, The Icon in MediaGalleryPreviewScreen.kt now uses R.drawable.stream_compose_ic_arrow_back but still sets contentDescription via stringResource(R.string.stream_compose_cancel); update the contentDescription to a more accurate string (e.g., R.string.stream_compose_back or R.string.stream_compose_navigate_back) and replace the reference in the Icon's contentDescription call so accessibility announces "back" instead of "cancel".
594-603: Consider showing a placeholder when player is null for video attachments.When
playeris null (e.g., during initialization), video pages render nothing. This could result in a blank page momentarily. Consider showing the thumbnail as a placeholder until the player is ready.Suggested improvement
attachment.isVideo() -> { - player?.let { + if (player != null) { MediaGalleryVideoPage( modifier = Modifier.fillMaxSize(), - player = it, + player = player, thumbnailUrl = attachment.thumbUrl, onClick = onMediaClick, ) + } else { + // Show thumbnail placeholder while player initializes + MediaGalleryImagePage( + attachment = attachment, + pagerState = pagerState, + page = page, + onTap = onMediaClick, + ) } }🤖 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/attachments/preview/MediaGalleryPreviewScreen.kt` around lines 594 - 603, The video branch currently returns nothing when player is null which causes blank pages; update the attachment.isVideo() branch to render a thumbnail placeholder while the player initializes by either (a) passing a nullable player into MediaGalleryVideoPage and have it show the thumbnailUrl when player == null, or (b) render a small MediaGalleryVideoPlaceholder composable that uses attachment.thumbUrl and onMediaClick until player is available, then swap to MediaGalleryVideoPage with the ready player; reference attachment.isVideo(), player, MediaGalleryVideoPage, thumbnailUrl/thumbUrl, and onMediaClick to locate and implement the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/internal/MediaGalleryPage.kt`:
- Around line 204-223: The new double-tap logic uses withTimeoutOrNull but the
file lacks the kotlinx.coroutines import, so add an import for
kotlinx.coroutines.withTimeoutOrNull at the top of the file; locate the
pointerInput block (contains coroutineScope, awaitEachGesture, awaitFirstDown,
DoubleTapTimeoutMs and withTimeoutOrNull) and add the missing import so the
withTimeoutOrNull call compiles.
In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/internal/StreamMediaPlayerContent.kt`:
- Around line 223-229: createPlayerView currently forces useController = false
which breaks controls for callers like MediaPreviewActivity and makes
showController() a no-op; remove the hard-disable (delete or stop setting
useController in createPlayerView inside StreamMediaPlayerContent) so the
PlayerView uses its default controller behavior, and instead set useController =
false only from the redesigned gallery code path that requires it (i.e., update
the caller that intentionally wants no controller to explicitly set
playerView.useController = false after createPlayerView).
In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/MediaGalleryPreviewScreen.kt`:
- Around line 147-155: The stateful overload's header parameter uses
Modifier.fillMaxWidth() while the stateless overload sets
Modifier.fillMaxWidth().height(56.dp), causing different default header heights;
update the stateful overload's header default (the header: `@Composable`
(attachments: List<Attachment>, currentPage: Int) -> Unit lambda that calls
MediaGalleryPreviewHeader) to use the same modifier as the stateless version
(add .height(56.dp) to Modifier.fillMaxWidth()) so both overloads render the
header at the same default height, ensuring consistency for
MediaGalleryPreviewHeader across both implementations.
In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/channel/attachments/ChannelMediaAttachmentsPreviewScreen.kt`:
- Around line 171-178: The pager is being forced into the new controllerless
video page by passing playerState.player to MediaGalleryPager, which prevents
the old bottom bar controls and onMediaClick handling; remove the player =
playerState.player argument (or pass null) so
MediaGalleryPager/MediaGalleryVideoPage falls back to the controller-backed
view, and add/forward an onMediaClick callback from
ChannelMediaAttachmentsPreviewScreen into MediaGalleryPager (and ensure the
existing bottom bar code still renders) so taps trigger immersive mode and
playback controls are present.
---
Nitpick comments:
In `@stream-chat-android-compose/api/stream-chat-android-compose.api`:
- Around line 864-865: The public overloads of MediaGalleryPreviewScreen changed
parameter types and callback shapes; document this breaking change in the v7
migration notes by adding an entry to MIGRATION_TO_V7.md (or the CHANGELOG)
explaining the new signatures (both MediaGalleryPreviewScreen overloads), what
changed (model-based parameters vs viewmodel-based parameters and updated
FunctionX slot types), and provide a minimal migration snippet showing how
callers should adapt custom preview integrations to the new parameter list and
callback shapes; reference the exact symbol name MediaGalleryPreviewScreen and
list both overload variants so users can find and update usages.
In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/MediaGalleryPreviewScreen.kt`:
- Around line 722-725: The Icon in MediaGalleryPreviewScreen.kt now uses
R.drawable.stream_compose_ic_arrow_back but still sets contentDescription via
stringResource(R.string.stream_compose_cancel); update the contentDescription to
a more accurate string (e.g., R.string.stream_compose_back or
R.string.stream_compose_navigate_back) and replace the reference in the Icon's
contentDescription call so accessibility announces "back" instead of "cancel".
- Around line 594-603: The video branch currently returns nothing when player is
null which causes blank pages; update the attachment.isVideo() branch to render
a thumbnail placeholder while the player initializes by either (a) passing a
nullable player into MediaGalleryVideoPage and have it show the thumbnailUrl
when player == null, or (b) render a small MediaGalleryVideoPlaceholder
composable that uses attachment.thumbUrl and onMediaClick until player is
available, then swap to MediaGalleryVideoPage with the ready player; reference
attachment.isVideo(), player, MediaGalleryVideoPage, thumbnailUrl/thumbUrl, and
onMediaClick to locate and implement the change.
In
`@stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/attachments/preview/MediaGalleryPreviewActivityTest.kt`:
- Around line 142-156: The test should assert that the save action occurs rather
than only performing the click: update the test `should save on save option
click` to either (A) verify the save flow by mocking or spying the component
that handles saves (e.g., inject a mock save handler into
`MediaGalleryPreviewActivity` or observe a result/Intent from
`ActivityScenario`), and assert that the handler was called or the expected
Intent/result was produced after clicking the "Save media" option, or (B) if you
intentionally only exercise UI interaction, rename the test to reflect that it
only performs the click; use the existing
`createIntent(message)`/`PreviewMessageData.messageWithUserAndAttachment` and
`ActivityScenario.launchActivityForResult<MediaGalleryPreviewActivity>(intent)`/`composeTestRule`
to obtain and assert the expected behavior.
- Around line 124-140: The test function `should delete on delete option click`
in MediaGalleryPreviewActivityTest currently only performs a click and doesn't
assert behavior; update the test to either verify the delete flow was triggered
(for example, mock and verify the chat client's delete method or repository
method was called after clicking the "Delete" option—use the existing mocks like
`mockClientState`/client mock or whatever deletion API mock exists in the test
harness and assert it was invoked with
`PreviewMessageData.messageWithUserAndAttachment`), or rename the test to
something like `should not crash on delete option click` if no behavior should
be asserted; modify `should delete on delete option click` (or add verification)
and use the `scenario`/mocks to perform a verify assertion.
- Around line 158-168: The test MediaGalleryPreviewActivityTest.should share on
share option click uses
composeTestRule.onNodeWithContentDescription("Share").performClick(), which is
inconsistent with the other option-click tests that use the semantic-action
pattern; replace the direct performClick call with the same semantic-action
invocation used elsewhere (i.e., invoke the node's semantic action for click) so
the test aligns with the other option click tests, or if the Share control is
intentionally different (toolbar icon vs bottom-sheet item), add a short comment
in the test explaining why performClick is kept.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 9519d05c-80e6-46e8-92d8-85a853212021
⛔ Files ignored due to path filters (11)
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_header_connecting.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.attachments.preview_MediaGalleryPreviewScreenTest_media_gallery_header_message_without_id.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.attachments.preview_MediaGalleryPreviewScreenTest_media_gallery_header_offline.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.attachments.preview_MediaGalleryPreviewScreenTest_media_gallery_header_online.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.attachments.preview_MediaGalleryPreviewScreenTest_media_gallery_screen_connected.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.attachments.preview_MediaGalleryPreviewScreenTest_media_gallery_screen_offline.pngis excluded by!**/*.pngstream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.attachments.preview_MediaGalleryPreviewScreenTest_media_gallery_screen_with_gallery_bottom_sheet.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.attachments.preview_MediaGalleryPreviewScreenTest_media_gallery_screen_with_share_large_file_prompt.pngis excluded by!**/*.png
📒 Files selected for processing (11)
stream-chat-android-compose/api/stream-chat-android-compose.apistream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/content/AudioRecordAttachmentContent.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/MediaGalleryPreviewOptionsMenu.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/MediaGalleryPreviewScreen.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/internal/MediaGalleryPage.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/internal/MediaGalleryPlayerState.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/internal/StreamMediaPlayerContent.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/internal/VideoPlaybackControls.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/channel/attachments/ChannelMediaAttachmentsPreviewScreen.ktstream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/button/SpeedButton.ktstream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/attachments/preview/MediaGalleryPreviewActivityTest.kt


Goal
Redesign the media gallery preview screen
Implementation
VideoPlaybackControlsoverlayMediaGalleryPlayerStateto encapsulate player lifecycle and state managementSpeedButtoncomponent from the audio recording speed control🎨 UI Changes
Testing
Summary by CodeRabbit
Release Notes
New Features
Tests