Skip to content

Commit

Permalink
[AND-318] Fix fading issue in media attachment content items (#5631)
Browse files Browse the repository at this point in the history
* Introduce ShimmerProgressIndicator

* Use StreamAsyncImage in MediaAttachmentContentItem

* Use StreamAsyncImage in MediaGalleryPreviewActivity

* Add a ripple effect to the gallery item

* Use StreamAsyncImage in GiphyAttachmentContent

* Remove landscapist placeholder dependency from the SDK

* apiDump

* CHANGELOG

* spotless
  • Loading branch information
andremion authored Feb 19, 2025
1 parent cffb101 commit f4a2d18
Show file tree
Hide file tree
Showing 12 changed files with 302 additions and 208 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
## stream-chat-android-compose
### 🐞 Fixed
- Crash when recording audio on a message reply. [#5642](https://github.com/GetStream/stream-chat-android/pull/5642)
- Fix fading issue in media attachment content items. [#5631](https://github.com/GetStream/stream-chat-android/pull/5631)

### ⬆️ Improved
- Autofocus the input fields in the poll creation screen. [#5629](https://github.com/GetStream/stream-chat-android/pull/5629)
Expand Down
23 changes: 2 additions & 21 deletions stream-chat-android-compose/api/stream-chat-android-compose.api
Original file line number Diff line number Diff line change
Expand Up @@ -408,22 +408,11 @@ public final class io/getstream/chat/android/compose/ui/attachments/content/Comp
public final fun getLambda-1$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function3;
}

public final class io/getstream/chat/android/compose/ui/attachments/content/ComposableSingletons$GiphyAttachmentContentKt {
public static final field INSTANCE Lio/getstream/chat/android/compose/ui/attachments/content/ComposableSingletons$GiphyAttachmentContentKt;
public static field lambda-1 Lkotlin/jvm/functions/Function3;
public fun <init> ()V
public final fun getLambda-1$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function3;
}

public final class io/getstream/chat/android/compose/ui/attachments/content/ComposableSingletons$MediaAttachmentContentKt {
public static final field INSTANCE Lio/getstream/chat/android/compose/ui/attachments/content/ComposableSingletons$MediaAttachmentContentKt;
public static field lambda-1 Lkotlin/jvm/functions/Function3;
public static field lambda-2 Lkotlin/jvm/functions/Function3;
public static field lambda-3 Lkotlin/jvm/functions/Function4;
public fun <init> ()V
public final fun getLambda-1$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function3;
public final fun getLambda-2$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function3;
public final fun getLambda-3$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function4;
}

public final class io/getstream/chat/android/compose/ui/attachments/content/ComposableSingletons$MediaAttachmentPreviewContentKt {
Expand Down Expand Up @@ -577,18 +566,10 @@ public final class io/getstream/chat/android/compose/ui/attachments/factory/Uplo
public final class io/getstream/chat/android/compose/ui/attachments/preview/ComposableSingletons$MediaGalleryPreviewActivityKt {
public static final field INSTANCE Lio/getstream/chat/android/compose/ui/attachments/preview/ComposableSingletons$MediaGalleryPreviewActivityKt;
public static field lambda-1 Lkotlin/jvm/functions/Function2;
public static field lambda-2 Lkotlin/jvm/functions/Function3;
public static field lambda-3 Lkotlin/jvm/functions/Function4;
public static field lambda-4 Lkotlin/jvm/functions/Function2;
public static field lambda-5 Lkotlin/jvm/functions/Function3;
public static field lambda-6 Lkotlin/jvm/functions/Function4;
public static field lambda-2 Lkotlin/jvm/functions/Function2;
public fun <init> ()V
public final fun getLambda-1$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
public final fun getLambda-2$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function3;
public final fun getLambda-3$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function4;
public final fun getLambda-4$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
public final fun getLambda-5$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function3;
public final fun getLambda-6$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function4;
public final fun getLambda-2$stream_chat_android_compose_release ()Lkotlin/jvm/functions/Function2;
}

public final class io/getstream/chat/android/compose/ui/attachments/preview/ComposableSingletons$MediaPreviewActivityKt {
Expand Down
2 changes: 1 addition & 1 deletion stream-chat-android-compose/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,13 @@ dependencies {
// Coil
implementation(libs.coil.compose)
implementation(libs.skydoves.landscapist.coil)
implementation(libs.skydoves.landscapist.placeholder)
implementation(libs.skydoves.landscapist.animation)
implementation(libs.coil.gif)
implementation(libs.coil.video)

// UI
implementation(libs.reorderable)
implementation(libs.shimmer.compose)

// Tests
testImplementation(project(":stream-chat-android-test"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,14 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import com.skydoves.landscapist.ImageOptions
import com.skydoves.landscapist.components.rememberImageComponent
import com.skydoves.landscapist.placeholder.shimmer.Shimmer
import com.skydoves.landscapist.placeholder.shimmer.ShimmerPlugin
import coil.compose.AsyncImagePainter
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.StreamDimens
import io.getstream.chat.android.compose.ui.util.StreamImage
import io.getstream.chat.android.compose.ui.util.StreamAsyncImage
import io.getstream.chat.android.models.Attachment
import io.getstream.chat.android.ui.common.utils.GiphyInfoType
import io.getstream.chat.android.ui.common.utils.GiphySizingMode
Expand Down Expand Up @@ -159,19 +157,22 @@ public fun GiphyAttachmentContent(
onLongClick = { onLongItemClick(message) },
),
) {
StreamImage(
modifier = Modifier.fillMaxSize(),
data = { giphyInfo?.url },
component = rememberImageComponent {
+ShimmerPlugin(
Shimmer.Resonate(
baseColor = ChatTheme.colors.mediaShimmerBase,
highlightColor = ChatTheme.colors.mediaShimmerHighlights,
),
StreamAsyncImage(
data = giphyInfo?.url,
) { state ->
if (state !is AsyncImagePainter.State.Success) {
ShimmerProgressIndicator(
modifier = Modifier.fillMaxSize(),
)
},
imageOptions = ImageOptions(contentScale = contentScale),
)
} else {
Image(
modifier = Modifier.fillMaxSize(),
painter = state.painter,
contentDescription = null,
contentScale = contentScale,
)
}
}

Image(
modifier = Modifier
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@

package io.getstream.chat.android.compose.ui.attachments.content

import android.annotation.SuppressLint
import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.compose.animation.Crossfade
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
Expand All @@ -44,6 +44,7 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
Expand All @@ -57,25 +58,20 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.Dp
import coil.compose.AsyncImagePainter
import coil.request.ImageRequest
import com.skydoves.landscapist.ImageOptions
import com.skydoves.landscapist.animation.crossfade.CrossfadePlugin
import com.skydoves.landscapist.coil.CoilImageState
import com.skydoves.landscapist.coil.rememberCoilImageState
import com.skydoves.landscapist.components.rememberImageComponent
import com.skydoves.landscapist.placeholder.shimmer.Shimmer
import com.skydoves.landscapist.placeholder.shimmer.ShimmerPlugin
import io.getstream.chat.android.client.ChatClient
import io.getstream.chat.android.client.utils.attachment.isImage
import io.getstream.chat.android.client.utils.attachment.isVideo
import io.getstream.chat.android.compose.R
import io.getstream.chat.android.compose.state.mediagallerypreview.MediaGalleryPreviewResult
import io.getstream.chat.android.compose.state.messages.attachments.AttachmentState
import io.getstream.chat.android.compose.ui.attachments.preview.MediaGalleryPreviewContract
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.util.ImageRequestTimeoutHandler
import io.getstream.chat.android.compose.ui.util.RetryHash
import io.getstream.chat.android.compose.ui.util.StreamImage
import io.getstream.chat.android.compose.ui.util.onImageNeedsToReload
import io.getstream.chat.android.compose.ui.util.StreamAsyncImage
import io.getstream.chat.android.models.Attachment
import io.getstream.chat.android.models.AttachmentType
import io.getstream.chat.android.models.Message
Expand Down Expand Up @@ -385,7 +381,6 @@ internal fun RowScope.MultipleMediaAttachments(
* @param overlayContent Represents the content overlaid above attachment previews.
* Usually used to display a play button over video previews.
*/
@SuppressLint("UnrememberedMutableInteractionSource")
@Suppress("LongParameterList", "LongMethod")
@OptIn(ExperimentalFoundationApi::class)
@Composable
Expand Down Expand Up @@ -430,26 +425,25 @@ internal fun MediaAttachmentContentItem(
}

val context = LocalContext.current
val model = remember(retryHash) {
val imageRequest = remember(retryHash) {
ImageRequest.Builder(context)
.data(data)
.setParameter(key = RetryHash, value = retryHash)
.build()
}

var imageState by rememberCoilImageState()
var imageState by remember { mutableStateOf<AsyncImagePainter.State>(AsyncImagePainter.State.Empty) }

val mixedMediaPreviewLauncher = rememberLauncherForActivityResult(
contract = MediaGalleryPreviewContract(),
onResult = { result -> onMediaGalleryPreviewResult(result) },
)

// Used to refresh the request for the current page
// if it has previously failed.
onImageNeedsToReload(
// Used to refresh the request for the current page if it has previously failed.
ImageRequestTimeoutHandler(
data = data,
connectionState = connectionState,
coilImageState = imageState,
imageState = imageState,
) {
retryHash++
}
Expand All @@ -468,7 +462,7 @@ internal fun MediaAttachmentContentItem(
.fillMaxWidth()
.testTag("Stream_MediaContent_$testTag")
.combinedClickable(
interactionSource = MutableInteractionSource(),
interactionSource = remember { MutableInteractionSource() },
indication = ripple(),
onClick = {
if (message.syncStatus == SyncStatus.COMPLETED) {
Expand Down Expand Up @@ -497,36 +491,51 @@ internal fun MediaAttachmentContentItem(
ChatTheme.colors.videoBackgroundMessageList
}

StreamImage(
StreamAsyncImage(
imageRequest = imageRequest,
modifier = modifier
.fillMaxSize()
.background(backgroundColor),
imageRequest = { model },
onImageStateChanged = { imageState = it },
component = rememberImageComponent {
+ShimmerPlugin(
Shimmer.Resonate(
baseColor = ChatTheme.colors.mediaShimmerBase,
highlightColor = ChatTheme.colors.mediaShimmerHighlights,
),
)
+CrossfadePlugin()
},
failure = {
Icon(
tint = ChatTheme.colors.disabled,
modifier = Modifier.fillMaxSize(0.4f),
painter = painterResource(
id = R.drawable.stream_compose_ic_image_picker,
),
contentDescription = null,
)
},
imageOptions = ImageOptions(contentScale = ContentScale.Crop),
)
contentScale = ContentScale.Crop,
) { asyncImageState ->
imageState = asyncImageState

Crossfade(targetState = asyncImageState) { state ->
when (state) {
is AsyncImagePainter.State.Empty,
is AsyncImagePainter.State.Loading,
-> ShimmerProgressIndicator(
modifier = Modifier.fillMaxSize(),
)

if (imageState !is CoilImageState.Loading) {
overlayContent(attachment.type)
is AsyncImagePainter.State.Success -> {
Image(
modifier = Modifier.fillMaxSize(),
painter = state.painter,
contentDescription = null,
contentScale = ContentScale.Crop,
)
overlayContent(attachment.type)
}

is AsyncImagePainter.State.Error -> {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center,
) {
Icon(
tint = ChatTheme.colors.disabled,
modifier = Modifier.fillMaxSize(0.4f),
painter = painterResource(R.drawable.stream_compose_ic_image_picker),
contentDescription = stringResource(
id = R.string.stream_ui_message_list_attachment_load_failed,
),
)
overlayContent(attachment.type)
}
}
}
}
}
}
}
Expand Down
Loading

0 comments on commit f4a2d18

Please sign in to comment.