Skip to content

compose: Derive Giphy ratio from the downloaded GIF when dimensions are missing#6551

Open
gpunto wants to merge 1 commit into
v6from
feature/giphy-ratio-from-download
Open

compose: Derive Giphy ratio from the downloaded GIF when dimensions are missing#6551
gpunto wants to merge 1 commit into
v6from
feature/giphy-ratio-from-download

Conversation

@gpunto

@gpunto gpunto commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Goal

When a Giphy attachment has no dimensions metadata (e.g. GIFs synced from another platform), GiphyAttachmentContent rendered a hardcoded container size with ContentScale.Crop, cropping portrait and landscape GIFs. Derive the container aspect ratio from the downloaded GIF instead.

Closes AND-1296

Implementation

  • When a Giphy attachment has no dimensions (giphyInfo == null), measure the downloaded GIF's intrinsic size via Coil and size the container to that aspect ratio, clamped to the configured max dimensions.
  • While the GIF loads, use a 1:1 container so both portrait and landscape are covered.
  • Only this no-dimensions path changes; Giphy attachments that carry dimensions render as before.

UI Changes

Before After
Screenshot_20260707_105827 Screenshot_20260707_111156

Testing

  • Updated the giphy attachment content Paparazzi snapshot and verifyPaparazziDebug passes.
  • Spotless and Detekt pass on the module.
  • Manually verified in the compose sample: Giphy attachments without dimensions render at their real aspect ratio instead of a fixed-size box.

Summary by CodeRabbit

  • New Features
    • Improved Giphy attachment previews by sizing fallback images more accurately when extra metadata isn’t available.
    • Added a smoother loading experience for fallback previews, including a placeholder until the image is ready.

@gpunto gpunto added the pr:bug Bug fix label Jul 7, 2026
@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 ignored for dependabot PRs).

🎉 Great job! This PR is ready for review.

@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.26 MB 5.32 MB 0.05 MB 🟢
stream-chat-android-offline 5.49 MB 5.54 MB 0.04 MB 🟢
stream-chat-android-ui-components 10.64 MB 10.75 MB 0.11 MB 🟢
stream-chat-android-compose 12.87 MB 12.96 MB 0.09 MB 🟢

@sonarqubecloud

sonarqubecloud Bot commented Jul 7, 2026

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

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

See analysis details on SonarQube Cloud

@gpunto gpunto marked this pull request as ready for review July 7, 2026 10:48
@gpunto gpunto requested a review from a team as a code owner July 7, 2026 10:48
@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

GiphyAttachmentContent is updated to compute fallback display dimensions using the downloaded image's intrinsic aspect ratio. A new downloadedRatio state tracks the loaded fallback image's size, driving conditional rendering with a shimmer placeholder versus the normal Giphy image path.

Changes

Giphy Fallback Aspect-Ratio Sizing

Layer / File(s) Summary
State and dimension calculation
stream-chat-android-compose/.../GiphyAttachmentContent.kt
Adds a remembered downloadedRatio state and reworks giphyDimensions to derive fallback size from that ratio, or a square when unavailable.
Conditional fallback/normal image rendering
stream-chat-android-compose/.../GiphyAttachmentContent.kt
Replaces the single StreamAsyncImage call with a flow that loads the fallback preview URL, extracts intrinsic size via LaunchedEffect, shows ShimmerProgressIndicator until ready, and updates downloadedRatio, while rendering the normal Giphy image directly when giphyInfo is present.

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

Possibly related PRs

Suggested reviewers: andremion

Poem

A gif with no info, oh what a plight,
So I measured its shape to get the size right,
A shimmer while waiting, soft and serene,
Then pixels appear on the chat screen,
Hop hop, ratios locked — a tidy scene! 🐇✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: deriving Giphy ratios from downloaded GIFs when metadata is missing.
Description check ✅ Passed The description covers the goal, implementation, screenshots, and testing, so it is mostly complete against the template.
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/giphy-ratio-from-download

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

🧹 Nitpick comments (1)
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/content/GiphyAttachmentContent.kt (1)

262-266: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider SideEffect instead of LaunchedEffect for a synchronous state update.

The block performs a plain, non-suspending state assignment; SideEffect avoids spinning up a coroutine for this and better conveys intent.

♻️ Suggested refactor
-                val intrinsicSize = painter?.intrinsicSize
-                LaunchedEffect(intrinsicSize) {
-                    if (intrinsicSize != null && intrinsicSize.isSpecified && intrinsicSize.height > 0f) {
-                        downloadedRatio = intrinsicSize.width / intrinsicSize.height
-                    }
-                }
+                val intrinsicSize = painter?.intrinsicSize
+                SideEffect {
+                    if (intrinsicSize != null && intrinsicSize.isSpecified && intrinsicSize.height > 0f) {
+                        downloadedRatio = intrinsicSize.width / intrinsicSize.height
+                    }
+                }
🤖 Prompt for 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.

In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/content/GiphyAttachmentContent.kt`
around lines 262 - 266, Replace the `LaunchedEffect(intrinsicSize)` block in
`GiphyAttachmentContent` with `SideEffect` because the body only performs a
synchronous `downloadedRatio` assignment and does not need a coroutine. Keep the
same `intrinsicSize` guard and update logic, but use `SideEffect` so the intent
of a non-suspending state update is clearer.
🤖 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/attachments/content/GiphyAttachmentContent.kt`:
- Around line 254-277: The Giphy fallback in GiphyAttachmentContent currently
only checks whether the painter is null, so AsyncImagePainter.State.Error is
treated like a loading state and can leave the shimmer up indefinitely. Update
the StreamAsyncImage content branch to inspect imageState directly, handle the
error state separately with a dedicated placeholder, and keep the existing
loaded and loading behavior in the fallback path.

---

Nitpick comments:
In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/content/GiphyAttachmentContent.kt`:
- Around line 262-266: Replace the `LaunchedEffect(intrinsicSize)` block in
`GiphyAttachmentContent` with `SideEffect` because the body only performs a
synchronous `downloadedRatio` assignment and does not need a coroutine. Keep the
same `intrinsicSize` guard and update logic, but use `SideEffect` so the intent
of a non-suspending state update is clearer.
🪄 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: 57c424b3-3c90-4e88-8a0d-bd85c3bdbe1c

📥 Commits

Reviewing files that changed from the base of the PR and between a4dbb5b and 0bc96f6.

⛔ Files ignored due to path filters (1)
  • stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.attachments.content_AttachmentsContentTest_giphy_attachment_content.png is excluded by !**/*.png
📒 Files selected for processing (1)
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/content/GiphyAttachmentContent.kt

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

Labels

pr:bug Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant