Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[AND-318] Fix fading issue in media attachment content items #5631

Merged
merged 11 commits into from
Feb 19, 2025
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
Loading