Skip to content

Use attachment url instead of index for determining the initial page in the media preview#6265

Open
gpunto wants to merge 2 commits intov7from
media-preview_replace-attachment-index-w-url
Open

Use attachment url instead of index for determining the initial page in the media preview#6265
gpunto wants to merge 2 commits intov7from
media-preview_replace-attachment-index-w-url

Conversation

@gpunto
Copy link
Contributor

@gpunto gpunto commented Mar 17, 2026

Goal

Use attachment url instead of index for determining the initial page in the media preview (addressing the discussion in #6247 (comment))

Implementation

  • Replaced the index with the preview url
  • Find the attachment in the list based on the url

🎨 UI Changes

None

Testing

Open the media gallery by clicking on an image/video attachment in a message and verify that the initially selected media is the one you clicked

Summary by CodeRabbit

Release Notes

  • Refactor
    • Refactored media gallery preview selection to use attachment URLs instead of positional indexing. This architectural improvement provides more reliable and consistent behavior when users preview media attachments from messages, ensuring the correct media displays first.

@gpunto gpunto added the pr:breaking-change Breaking change label Mar 17, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Mar 17, 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.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 17, 2026

SDK Size Comparison 📏

SDK Before After Difference Status
stream-chat-android-client 5.25 MB 5.70 MB 0.45 MB 🟡
stream-chat-android-ui-components 10.60 MB 11.00 MB 0.41 MB 🟡
stream-chat-android-compose 12.81 MB 12.05 MB -0.77 MB 🚀

@gpunto gpunto marked this pull request as ready for review March 17, 2026 16:05
@gpunto gpunto requested a review from a team as a code owner March 17, 2026 16:05
@coderabbitai
Copy link

coderabbitai bot commented Mar 17, 2026

Walkthrough

The PR refactors media attachment preview selection from index-based positioning to URL-based selection across multiple components, including activity intents, contracts, composables, and data structures. This replaces integer position parameters with string URL parameters throughout the media gallery preview flow.

Changes

Cohort / File(s) Summary
Public API Declaration Updates
stream-chat-android-compose/api/stream-chat-android-compose.api
Tracks public API changes: MediaAttachmentContent.component3 type changed from Int to String; getSelectedAttachmentUrl() accessor added; MediaGalleryPreviewInput.getInitialPosition() removed; constructor and method signatures updated to use selectedAttachmentUrl: String? instead of position-based parameters across multiple classes.
Data Structure & Click Handling
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/content/MediaAttachmentContent.kt
MediaAttachmentClickData refactored to carry selectedAttachmentUrl: String? instead of attachmentPosition: Int; deprecated public overload signature updated to accept URL parameter; click data construction uses named parameters.
Activity Intent & Configuration
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/MediaGalleryPreviewActivity.kt
getIntent() companion method signature changed to accept selectedAttachmentUrl: String? replacing attachmentPosition: Int; intent key renamed from KeyAttachmentPosition to KeySelectedAttachmentUrl; intent extra retrieval changed from getIntExtra() to getStringExtra(); documentation updated.
Contract Input Definition
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/MediaGalleryPreviewContract.kt
Input data class constructor parameter changed from initialPosition: Int = 0 to selectedAttachmentUrl: String? = null; createIntent() now passes URL instead of position; documentation updated.
Composable Screen Logic
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/MediaGalleryPreviewScreen.kt
Public overloads refactored to accept selectedAttachmentUrl: String? instead of initialPage: Int; pager starting page now resolved by matching attachment imagePreviewUrl against selected URL; import added for URL resolution; defaults to index 0 if URL not found.
Test Suite Updates
stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/attachments/preview/MediaGalleryPreviewScreenTest.kt
Test invocations updated from initialPage = 0 to selectedAttachmentUrl = null across offline, connected, with-options, and with-share scenarios.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Suggested labels

pr:improvement

Poem

🐰 A hop through the gallery, URL by URL,
No more counting positions, let previews unfurl!
With strings guiding sight instead of indices small,
The attachments now dance when we choose one of all. 🌸

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 52.94% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: replacing index-based attachment selection with URL-based selection for media preview initialization.
Description check ✅ Passed The description covers the Goal and Implementation sections from the template with clear explanations. However, several sections are missing or incomplete: no contributor or reviewer checklists, no testing patch provided, and no UI comparison despite the template requirements.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch media-preview_replace-attachment-index-w-url
📝 Coding Plan
  • Generate coding plan for human review comments

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.

Tip

You can make CodeRabbit's review stricter and more nitpicky using the `assertive` profile, if that's what you prefer.

Change the reviews.profile setting to assertive to make CodeRabbit's nitpick more issues in your PRs.

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 (1)
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/content/MediaAttachmentContent.kt (1)

121-149: ⚠️ Potential issue | 🟡 Minor

Fix the deprecation quick-fix for onItemClick.

The deprecated overload's ReplaceWith forwards onItemClick = onItemClick directly, but the replacement overload expects (MediaAttachmentClickData) -> Unit. IDE-assisted migration will generate non-compiling code unless the ReplaceWith adapts the old callback signature.

🛠️ Proposed fix
 `@Deprecated`(
     message = "Use the overload that takes onItemClick as a single parameter of type MediaAttachmentClickData.",
     replaceWith = ReplaceWith(
         "MediaAttachmentContent(" +
             "state = attachmentState, " +
             "modifier = modifier, " +
             "maximumNumberOfPreviewedItems = maximumNumberOfPreviewedItems, " +
             "skipEnrichUrl = skipEnrichUrl, " +
-            "onItemClick = onItemClick, " +
+            "onItemClick = { data -> onItemClick(" +
+                "data.mediaGalleryPreviewLauncher, " +
+                "data.message, " +
+                "data.selectedAttachmentUrl, " +
+                "data.videoThumbnailsEnabled, " +
+                "data.downloadAttachmentUriGenerator, " +
+                "data.downloadRequestInterceptor, " +
+                "data.streamCdnImageResizing, " +
+                "data.skipEnrichUrl" +
+            ") }, " +
             "itemOverlayContent = itemOverlayContent" +
             ")",
     ),
🤖 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/content/MediaAttachmentContent.kt`
around lines 121 - 149, The ReplaceWith for the deprecated
MediaAttachmentContent must wrap the old multi-parameter onItemClick into the
new single-parameter signature; update the ReplaceWith expression so it passes
onItemClick = { data: MediaAttachmentClickData -> onItemClick(
data.mediaGalleryPreviewLauncher, data.message, data.selectedAttachmentUrl,
data.videoThumbnailsEnabled, data.downloadAttachmentUriGenerator,
data.downloadRequestInterceptor, data.streamCdnImageResizing, data.skipEnrichUrl
) } (or equivalent lambda) when calling the new MediaAttachmentContent overload,
referencing MediaAttachmentContent, the deprecated onItemClick parameter, and
MediaAttachmentClickData so IDE quick-fix produces compilable code.
🧹 Nitpick comments (2)
stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/attachments/preview/MediaGalleryPreviewScreenTest.kt (1)

118-118: Add one snapshot that exercises URL-based selection.

All updated cases pass selectedAttachmentUrl = null, so they only cover the default first-page path. Please add a multi-attachment snapshot with a non-null URL pointing at a non-first item so this PR's actual resolver logic is covered.

As per coding guidelines, **/stream-chat-android-compose/**/*Test.kt: Add Paparazzi snapshots for Compose UI regressions and run verifyPaparazziDebug.

Also applies to: 135-135, 152-152, 169-169, 186-186

🤖 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/MediaGalleryPreviewScreenTest.kt`
at line 118, Add a new Paparazzi snapshot case in MediaGalleryPreviewScreenTest
that passes a non-null selectedAttachmentUrl that matches a URL of a non-first
attachment to exercise URL-based selection; locate the existing test function(s)
where selectedAttachmentUrl = null is passed (look for the test methods in
MediaGalleryPreviewScreenTest that call the composable under test) and add a
sibling test or expand one case to set selectedAttachmentUrl =
"<url-of-second-attachment>" (use the same attachment list used in the other
snapshots) so the resolver logic selects a non-first page, then run
verifyPaparazziDebug to ensure the snapshot is added.
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/MediaGalleryPreviewActivity.kt (1)

155-155: Normalize blank selected URLs to null before matching.

An empty string is unlikely to match any attachment URL but is still treated as a provided selector. Converting blanks to null keeps fallback behavior explicit.

Suggested diff
-        val selectedAttachmentUrl = intent?.getStringExtra(KeySelectedAttachmentUrl)
+        val selectedAttachmentUrl = intent?.getStringExtra(KeySelectedAttachmentUrl)?.takeIf { it.isNotBlank() }
...
-                putExtra(KeySelectedAttachmentUrl, selectedAttachmentUrl)
+                putExtra(KeySelectedAttachmentUrl, selectedAttachmentUrl?.takeIf { it.isNotBlank() })

Also applies to: 543-543

🤖 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/MediaGalleryPreviewActivity.kt`
at line 155, In MediaGalleryPreviewActivity, normalize the selectedAttachmentUrl
obtained via intent?.getStringExtra(KeySelectedAttachmentUrl) to null when it's
blank (empty or only whitespace) before any matching logic so an empty string
doesn't block fallback behavior; replace the direct assignment with a trimmed
check (if the retrieved string.isNullOrBlank() then null else the trimmed
string) and apply the same change to the other occurrence that reads
KeySelectedAttachmentUrl (the second usage noted in the file) so both matching
points treat blank selectors as absent.
🤖 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 583-594: Add migration notes to the changelog documenting the API
change from position-based parameters to the URL-based selectedAttachmentUrl:
explain that MediaAttachmentClickData no longer exposes
attachmentPosition/initialPosition and callers should pass selectedAttachmentUrl
(the absolute attachment URL to open) instead; update references for
MediaGalleryPreviewActivity, MediaGalleryPreviewContract, and
MediaGalleryPreviewScreen to show the new parameter name and expected semantics,
include a short example mapping (attachmentPosition/initialPosition ->
selectedAttachmentUrl) and note behavioral differences for callers relying on
index-based navigation in CHANGELOG.md or a dedicated migration section.
- Around line 583-594: The preview selection currently finds the clicked
attachment by using indexOfFirst on imagePreviewUrl which picks the first
matching URL and breaks when duplicate URLs exist; update the selection logic in
the preview-launching code (the place that reads getSelectedAttachmentUrl() and
getMessage() / message.attachments) to prefer a stable tiebreaker: first try to
match by the original attachment index passed through the component (retain and
use the attachment index from the click handler or an explicit attachmentId
field), and only fall back to matching by imagePreviewUrl when index/id is not
available; add unit tests that simulate a Message with duplicate imagePreviewUrl
entries to assert the correct preview index is chosen and note the change in the
CHANGELOG.

In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/MediaGalleryPreviewActivity.kt`:
- Line 514: Add a breaking-change note to CHANGELOG.md under the v7 section
documenting that MediaGalleryPreviewActivity.getIntent changed its selection
parameter from an index-based attachmentPosition:Int to a URL-based
selectedAttachmentUrl:String?; instruct users to migrate by passing the
attachment's preview URL instead of position (e.g., use attachment.imageUrl or
attachment.videoThumbnailUrl when constructing the new selectedAttachmentUrl)
and include an example mapping: attachmentPosition -> find the attachment at
that index and use its imageUrl/videoThumbnailUrl as selectedAttachmentUrl for
getIntent.

In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/MediaGalleryPreviewScreen.kt`:
- Around line 364-367: Replace the fragile indexOfFirst lookup for
startingPosition with a stable multi-key match: iterate filteredAttachments with
mapIndexed and pick the first entry where imagePreviewUrl ==
selectedAttachmentUrl AND a stable tiebreaker matches (preferably an attachment
identifier or the filtered index). Concretely, compute startingPosition by using
filteredAttachments.mapIndexed { index, att -> index to att } and find the pair
where att.imagePreviewUrl == selectedAttachmentUrl && (att.id ==
selectedAttachmentId || index == selectedAttachmentFilteredIndex), falling back
to 0 if none found; this uses filteredAttachments, selectedAttachmentUrl,
imagePreviewUrl, startingPosition (and add selectedAttachmentId or
selectedAttachmentFilteredIndex state if not present) to ensure deterministic
selection.

---

Outside diff comments:
In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/content/MediaAttachmentContent.kt`:
- Around line 121-149: The ReplaceWith for the deprecated MediaAttachmentContent
must wrap the old multi-parameter onItemClick into the new single-parameter
signature; update the ReplaceWith expression so it passes onItemClick = { data:
MediaAttachmentClickData -> onItemClick( data.mediaGalleryPreviewLauncher,
data.message, data.selectedAttachmentUrl, data.videoThumbnailsEnabled,
data.downloadAttachmentUriGenerator, data.downloadRequestInterceptor,
data.streamCdnImageResizing, data.skipEnrichUrl ) } (or equivalent lambda) when
calling the new MediaAttachmentContent overload, referencing
MediaAttachmentContent, the deprecated onItemClick parameter, and
MediaAttachmentClickData so IDE quick-fix produces compilable code.

---

Nitpick comments:
In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/MediaGalleryPreviewActivity.kt`:
- Line 155: In MediaGalleryPreviewActivity, normalize the selectedAttachmentUrl
obtained via intent?.getStringExtra(KeySelectedAttachmentUrl) to null when it's
blank (empty or only whitespace) before any matching logic so an empty string
doesn't block fallback behavior; replace the direct assignment with a trimmed
check (if the retrieved string.isNullOrBlank() then null else the trimmed
string) and apply the same change to the other occurrence that reads
KeySelectedAttachmentUrl (the second usage noted in the file) so both matching
points treat blank selectors as absent.

In
`@stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/attachments/preview/MediaGalleryPreviewScreenTest.kt`:
- Line 118: Add a new Paparazzi snapshot case in MediaGalleryPreviewScreenTest
that passes a non-null selectedAttachmentUrl that matches a URL of a non-first
attachment to exercise URL-based selection; locate the existing test function(s)
where selectedAttachmentUrl = null is passed (look for the test methods in
MediaGalleryPreviewScreenTest that call the composable under test) and add a
sibling test or expand one case to set selectedAttachmentUrl =
"<url-of-second-attachment>" (use the same attachment list used in the other
snapshots) so the resolver logic selects a non-first page, then run
verifyPaparazziDebug to ensure the snapshot is added.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: b77eb064-ecd6-4215-99cc-2dae67c1db30

📥 Commits

Reviewing files that changed from the base of the PR and between 9a9c4fb and a94e335.

📒 Files selected for processing (6)
  • 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/MediaAttachmentContent.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/MediaGalleryPreviewActivity.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/MediaGalleryPreviewContract.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/MediaGalleryPreviewScreen.kt
  • stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/attachments/preview/MediaGalleryPreviewScreenTest.kt

@gpunto gpunto force-pushed the media-preview_replace-attachment-index-w-url branch from a94e335 to 3cf65b5 Compare March 17, 2026 16:25
@gpunto gpunto force-pushed the media-preview_replace-attachment-index-w-url branch from 3cf65b5 to 1ae7e5e Compare March 17, 2026 16:26
@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
39.3% 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:breaking-change Breaking change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant