From e5bf8826bedc0c78632b05fea2344e18dc83ac06 Mon Sep 17 00:00:00 2001 From: Gian <47775302+gpunto@users.noreply.github.com> Date: Tue, 7 Jul 2026 12:28:48 +0200 Subject: [PATCH 1/5] compose: Derive Giphy ratio from the downloaded GIF when dimensions are missing --- .../content/GiphyAttachmentContent.kt | 61 +++++++++++++++--- ...tsContentTest_giphy_attachment_content.png | Bin 4617 -> 4523 bytes ...t_giphy_attachment_content_blank_title.png | Bin 4617 -> 4523 bytes ...phy_attachment_content_non-interactive.png | Bin 4617 -> 4523 bytes ...mentsContentTest_giphy_message_content.png | Bin 19340 -> 19346 bytes 5 files changed, 52 insertions(+), 9 deletions(-) diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/content/GiphyAttachmentContent.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/content/GiphyAttachmentContent.kt index 864a3d746c4..a4734a76007 100644 --- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/content/GiphyAttachmentContent.kt +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/content/GiphyAttachmentContent.kt @@ -33,10 +33,15 @@ import androidx.compose.material3.Text import androidx.compose.material3.ripple import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.geometry.isSpecified import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.layout.ContentScale @@ -57,6 +62,7 @@ import coil3.compose.LocalAsyncImagePreviewHandler import io.getstream.chat.android.client.utils.attachment.isGiphy import io.getstream.chat.android.compose.R import io.getstream.chat.android.compose.state.messages.attachments.AttachmentState +import io.getstream.chat.android.compose.ui.components.ShimmerProgressIndicator import io.getstream.chat.android.compose.ui.theme.ChatTheme import io.getstream.chat.android.compose.ui.theme.MessageStyling import io.getstream.chat.android.compose.ui.theme.StreamTokens @@ -120,7 +126,9 @@ public fun GiphyAttachmentContent( val giphyInfo = attachment.giphyInfo(giphyInfoType) - val giphyDimensions: DpSize = calculateSize(giphyInfo, giphySizingMode) + var downloadedRatio by remember(key1 = previewUrl) { mutableStateOf(null) } + + val giphyDimensions: DpSize = calculateSize(giphyInfo, giphySizingMode, downloadedRatio) val shouldBeFullSize = message.shouldBeDisplayedAsFullSizeAttachment() val giphyTitle = attachment.title?.takeIf(String::isNotBlank) @@ -170,12 +178,38 @@ public fun GiphyAttachmentContent( ) }, ) { - StreamAsyncImage( - data = giphyInfo?.url ?: attachment.giphyFallbackPreviewUrl, - modifier = Modifier.fillMaxSize(), - contentDescription = giphyTitle, - contentScale = contentScale, - ) + if (giphyInfo == null) { + StreamAsyncImage( + data = attachment.giphyFallbackPreviewUrl, + modifier = Modifier.fillMaxSize(), + contentScale = contentScale, + ) { imageState -> + val painter = imageState.painter + val intrinsicSize = painter?.intrinsicSize + LaunchedEffect(intrinsicSize) { + if (intrinsicSize != null && intrinsicSize.isSpecified && intrinsicSize.height > 0f) { + downloadedRatio = intrinsicSize.width / intrinsicSize.height + } + } + if (painter == null) { + ShimmerProgressIndicator(modifier = Modifier.matchParentSize()) + } else { + Image( + painter = painter, + contentDescription = giphyTitle, + modifier = Modifier.matchParentSize(), + contentScale = contentScale, + ) + } + } + } else { + StreamAsyncImage( + data = giphyInfo.url, + modifier = Modifier.fillMaxSize(), + contentDescription = giphyTitle, + contentScale = contentScale, + ) + } GiphyLabel( Modifier @@ -190,6 +224,7 @@ public fun GiphyAttachmentContent( private fun calculateSize( giphyInfo: GiphyInfo?, giphySizingMode: GiphySizingMode, + downloadedRatio: Float?, ): DpSize { val density = LocalDensity.current @@ -199,7 +234,7 @@ private fun calculateSize( val width = 250.dp val height = 200.dp - val giphyDimensions: DpSize = remember(giphyInfo, density, maxWidth, width, maxHeight, height) { + val giphyDimensions: DpSize = remember(giphyInfo, downloadedRatio, density, maxWidth, width, maxHeight, height) { if (giphyInfo != null) { with(density) { val giphyWidth = giphyInfo.width.toDp() @@ -227,8 +262,16 @@ private fun calculateSize( ) } } + } else if (downloadedRatio == null) { + val side = minOf(maxWidth, maxHeight) + DpSize(side, side) } else { - DpSize(maxWidth, maxHeight) + calculateResultingDimensions( + maxWidth = maxWidth, + maxHeight = maxHeight, + giphyWidth = downloadedRatio.dp, + giphyHeight = 1.dp, + ) } } return giphyDimensions diff --git a/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.attachments.content_AttachmentsContentTest_giphy_attachment_content.png b/stream-chat-android-compose/src/test/snapshots/images/io.getstream.chat.android.compose.ui.attachments.content_AttachmentsContentTest_giphy_attachment_content.png index b35920eae4c0594380b6f4a5e092e6d10fbd60bd..44b95e84c05c66e0cf2faa1b22ebe03a4c976790 100644 GIT binary patch literal 4523 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 literal 4617 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 literal 4617 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 literal 4617 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

TL%33rn#!l<$Y(;%_ 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 From 0df5fceeea9e275cc0c8ba8171776b8209f2077e Mon Sep 17 00:00:00 2001 From: Gian <47775302+gpunto@users.noreply.github.com> Date: Tue, 7 Jul 2026 14:46:21 +0200 Subject: [PATCH 2/5] compose: Prevent the Giphy label from wrapping on narrow attachments --- .../compose/ui/attachments/content/GiphyAttachmentContent.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/content/GiphyAttachmentContent.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/content/GiphyAttachmentContent.kt index a4734a76007..ea0b8b210c3 100644 --- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/content/GiphyAttachmentContent.kt +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/content/GiphyAttachmentContent.kt @@ -302,6 +302,8 @@ private fun GiphyLabel(modifier: Modifier) { text = stringResource(R.string.stream_compose_giphy_label), style = ChatTheme.typography.metadataEmphasis, color = colors.badgeTextOnAccent, + maxLines = 1, + softWrap = false, ) } } From 431aee78eec81a04fb839b7556e36043a9bd92cf Mon Sep 17 00:00:00 2001 From: Gian <47775302+gpunto@users.noreply.github.com> Date: Wed, 8 Jul 2026 13:36:16 +0200 Subject: [PATCH 3/5] compose: Track Giphy ratio by the loaded URL and reuse StreamAsyncImage loading --- .../content/GiphyAttachmentContent.kt | 44 ++++++------------- .../android/compose/ui/util/ImageUtils.kt | 8 ++++ 2 files changed, 21 insertions(+), 31 deletions(-) diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/content/GiphyAttachmentContent.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/content/GiphyAttachmentContent.kt index ea0b8b210c3..ced9f81b422 100644 --- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/content/GiphyAttachmentContent.kt +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/content/GiphyAttachmentContent.kt @@ -33,7 +33,6 @@ import androidx.compose.material3.Text import androidx.compose.material3.ripple import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -62,7 +61,6 @@ import coil3.compose.LocalAsyncImagePreviewHandler import io.getstream.chat.android.client.utils.attachment.isGiphy import io.getstream.chat.android.compose.R import io.getstream.chat.android.compose.state.messages.attachments.AttachmentState -import io.getstream.chat.android.compose.ui.components.ShimmerProgressIndicator import io.getstream.chat.android.compose.ui.theme.ChatTheme import io.getstream.chat.android.compose.ui.theme.MessageStyling import io.getstream.chat.android.compose.ui.theme.StreamTokens @@ -126,7 +124,7 @@ public fun GiphyAttachmentContent( val giphyInfo = attachment.giphyInfo(giphyInfoType) - var downloadedRatio by remember(key1 = previewUrl) { mutableStateOf(null) } + var downloadedRatio by remember(key1 = attachment.giphyFallbackPreviewUrl) { mutableStateOf(null) } val giphyDimensions: DpSize = calculateSize(giphyInfo, giphySizingMode, downloadedRatio) @@ -178,38 +176,22 @@ public fun GiphyAttachmentContent( ) }, ) { - if (giphyInfo == null) { - StreamAsyncImage( - data = attachment.giphyFallbackPreviewUrl, - modifier = Modifier.fillMaxSize(), - contentScale = contentScale, - ) { imageState -> - val painter = imageState.painter - val intrinsicSize = painter?.intrinsicSize - LaunchedEffect(intrinsicSize) { + StreamAsyncImage( + data = giphyInfo?.url ?: attachment.giphyFallbackPreviewUrl, + contentDescription = giphyTitle, + modifier = Modifier.fillMaxSize(), + contentScale = contentScale, + onState = if (giphyInfo == null) { + { imageState -> + val intrinsicSize = imageState.painter?.intrinsicSize if (intrinsicSize != null && intrinsicSize.isSpecified && intrinsicSize.height > 0f) { downloadedRatio = intrinsicSize.width / intrinsicSize.height } } - if (painter == null) { - ShimmerProgressIndicator(modifier = Modifier.matchParentSize()) - } else { - Image( - painter = painter, - contentDescription = giphyTitle, - modifier = Modifier.matchParentSize(), - contentScale = contentScale, - ) - } - } - } else { - StreamAsyncImage( - data = giphyInfo.url, - modifier = Modifier.fillMaxSize(), - contentDescription = giphyTitle, - contentScale = contentScale, - ) - } + } else { + null + }, + ) GiphyLabel( Modifier diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/ImageUtils.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/ImageUtils.kt index 2b4f3ab5e02..047851c964b 100644 --- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/ImageUtils.kt +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/util/ImageUtils.kt @@ -55,6 +55,7 @@ private const val MaxRetries = 3 * @param contentDescription The description to use for the image. * @param modifier Modifier for styling. * @param contentScale The scale to be used for the content. Default is [ContentScale.Fit]. + * @param onState Optional listener invoked when the image loading state changes. */ @Composable internal fun StreamAsyncImage( @@ -62,6 +63,7 @@ internal fun StreamAsyncImage( contentDescription: String?, modifier: Modifier = Modifier, contentScale: ContentScale = ContentScale.Fit, + onState: ((AsyncImagePainter.State) -> Unit)? = null, ) { StreamAsyncImage( imageRequest = ImageRequest.Builder(LocalContext.current) @@ -70,6 +72,7 @@ internal fun StreamAsyncImage( modifier = modifier, contentDescription = contentDescription, contentScale = contentScale, + onState = onState, ) } @@ -80,6 +83,7 @@ internal fun StreamAsyncImage( * @param contentDescription The description to use for the image. * @param modifier Modifier for styling. * @param contentScale The scale to be used for the content. Default is [ContentScale.Fit]. + * @param onState Optional listener invoked when the image loading state changes. */ @Composable internal fun StreamAsyncImage( @@ -87,12 +91,16 @@ internal fun StreamAsyncImage( contentDescription: String?, modifier: Modifier = Modifier, contentScale: ContentScale = ContentScale.Fit, + onState: ((AsyncImagePainter.State) -> Unit)? = null, ) { StreamAsyncImage( imageRequest = imageRequest, modifier = modifier, contentScale = contentScale, ) { state -> + if (onState != null) { + LaunchedEffect(state) { onState(state) } + } val painter = state.painter if (painter == null) { ShimmerProgressIndicator( From 16946aedfba8c3ba9f937b3dfb84d61f5900d481 Mon Sep 17 00:00:00 2001 From: Gian <47775302+gpunto@users.noreply.github.com> Date: Thu, 9 Jul 2026 12:14:22 +0200 Subject: [PATCH 4/5] compose: Unit-test Giphy sizing math --- .../content/GiphyAttachmentContent.kt | 2 +- .../content/GiphyAttachmentContentTest.kt | 67 +++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/attachments/content/GiphyAttachmentContentTest.kt diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/content/GiphyAttachmentContent.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/content/GiphyAttachmentContent.kt index ced9f81b422..ce6bb699346 100644 --- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/content/GiphyAttachmentContent.kt +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/content/GiphyAttachmentContent.kt @@ -305,7 +305,7 @@ private fun GiphyLabel(modifier: Modifier) { * * @return The resulting resized dimensions. */ -private fun calculateResultingDimensions( +internal fun calculateResultingDimensions( maxWidth: Dp, maxHeight: Dp, giphyWidth: Dp, diff --git a/stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/attachments/content/GiphyAttachmentContentTest.kt b/stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/attachments/content/GiphyAttachmentContentTest.kt new file mode 100644 index 00000000000..0fb15c51f44 --- /dev/null +++ b/stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/attachments/content/GiphyAttachmentContentTest.kt @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2014-2026 Stream.io Inc. All rights reserved. + * + * Licensed under the Stream License; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/GetStream/stream-chat-android/blob/main/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.getstream.chat.android.compose.ui.attachments.content + +import androidx.compose.ui.unit.dp +import org.junit.Assert.assertEquals +import org.junit.Test + +internal class GiphyAttachmentContentTest { + + @Test + fun `landscape ratio is constrained by the max width`() { + val result = calculateResultingDimensions( + maxWidth = 250.dp, + maxHeight = 200.dp, + giphyWidth = 400.dp, + giphyHeight = 200.dp, + ) + + assertEquals(250f, result.width.value, DELTA) + assertEquals(125f, result.height.value, DELTA) + } + + @Test + fun `portrait ratio is constrained by the max height`() { + val result = calculateResultingDimensions( + maxWidth = 250.dp, + maxHeight = 200.dp, + giphyWidth = 200.dp, + giphyHeight = 400.dp, + ) + + assertEquals(100f, result.width.value, DELTA) + assertEquals(200f, result.height.value, DELTA) + } + + @Test + fun `square ratio is constrained by the smaller max dimension`() { + val result = calculateResultingDimensions( + maxWidth = 250.dp, + maxHeight = 200.dp, + giphyWidth = 300.dp, + giphyHeight = 300.dp, + ) + + assertEquals(200f, result.width.value, DELTA) + assertEquals(200f, result.height.value, DELTA) + } + + private companion object { + private const val DELTA = 0.01f + } +} From e7e57db119c7aa47dcfdb5dc17d6deb39d537842 Mon Sep 17 00:00:00 2001 From: Gian <47775302+gpunto@users.noreply.github.com> Date: Thu, 9 Jul 2026 12:34:24 +0200 Subject: [PATCH 5/5] Add VisibleForTesting annotation to calculateResultingDimensions --- .../compose/ui/attachments/content/GiphyAttachmentContent.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/content/GiphyAttachmentContent.kt b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/content/GiphyAttachmentContent.kt index ce6bb699346..5df94f0de37 100644 --- a/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/content/GiphyAttachmentContent.kt +++ b/stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/content/GiphyAttachmentContent.kt @@ -17,6 +17,7 @@ package io.getstream.chat.android.compose.ui.attachments.content import android.content.Context +import androidx.annotation.VisibleForTesting import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image import androidx.compose.foundation.background @@ -305,6 +306,7 @@ private fun GiphyLabel(modifier: Modifier) { * * @return The resulting resized dimensions. */ +@VisibleForTesting internal fun calculateResultingDimensions( maxWidth: Dp, maxHeight: Dp,