Skip to content

Commit

Permalink
[PBE-4105] Implement StreamImage and migrate to use it internally (#5299
Browse files Browse the repository at this point in the history
)

* Add landscapist dependencies

* Implement StreamNetworkImage composables and deprecate rememberStreamImagePainter

* Migrate Avatar to use StreamNetworkImage

* Migrate to use StreamImage composable

* Add color resources for shimmering and update ktlint rules

* Update ktlint rules

* Migrate AsyncImage to CoilImage

* Update CHANGELOG.md
  • Loading branch information
skydoves authored Jun 19, 2024
1 parent 25b960f commit 6387016
Show file tree
Hide file tree
Showing 26 changed files with 463 additions and 329 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,11 @@
- Added `MessageOptionItemVisibility` class that controls menu item visibility in the selected message options menu.
- Added new `ChatTheme.messageOptionItemVisibility` property of type `MessageOptionItemVisibility`.
- Added docs sections that describe these changes.
- Added `StreamImage` Composable functions to load internal images, such as profile images, attatchments, and other media files.

### ⚠️ Changed
- Used the new `ChatTheme.messageOptionItemVisibility` property in `defaultMessageOptionsState()` in combination with own capabilities to control menu option item visibility.
- Now, Compose components use `StreamImage`, which delegates [Landscapist-Coil](https://github.com/skydoves/landscapist) for loading internal images.

### ❌ Removed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ object Versions {
internal const val KOTLIN = "1.9.22"
internal const val KOTLIN_BINARY_VALIDATOR = "0.13.2"
internal const val KSP = "1.9.22-1.0.17"
internal const val LANDSCAPIST = "2.3.4"
internal const val LEAK_CANARY = "2.4"
internal const val MATERIAL_COMPONENTS = "1.8.0"
internal const val MACRO_BENCHMARK = "1.2.0"
Expand Down Expand Up @@ -131,6 +132,9 @@ object Dependencies {
const val composeActivity = "androidx.activity:activity-compose:${Versions.ANDROIDX_ACTIVITY_COMPOSE}"
const val composeViewModel = "androidx.lifecycle:lifecycle-viewmodel-compose:${Versions.ANDROIDX_LIFECYCLE}"
const val composeStableMarker = "com.github.skydoves:compose-stable-marker:${Versions.COMPOSE_STABLE_MARKER}"
const val composeLandscapistCoil = "com.github.skydoves:landscapist-coil:${Versions.LANDSCAPIST}"
const val composeLandscapistPlaceholder = "com.github.skydoves:landscapist-placeholder:${Versions.LANDSCAPIST}"
const val composeLandscapistAnimation = "com.github.skydoves:landscapist-animation:${Versions.LANDSCAPIST}"
const val composeLifecycle = "androidx.lifecycle:lifecycle-runtime-compose:${Versions.ANDROIDX_LIFECYCLE}"
const val coroutinesAndroid = "org.jetbrains.kotlinx:kotlinx-coroutines-android:${Versions.COROUTINES}"
const val coroutinesCore = "org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.COROUTINES}"
Expand Down
2 changes: 1 addition & 1 deletion stream-chat-android-compose-sample/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ dependencies {
implementation Dependencies.composeAccompanistPager

// Coil
implementation Dependencies.composeCoil
implementation Dependencies.composeLandscapistCoil

// Firebase
implementation Dependencies.firebaseAnalytics
Expand Down
46 changes: 40 additions & 6 deletions stream-chat-android-compose/api/stream-chat-android-compose.api
Original file line number Diff line number Diff line change
Expand Up @@ -390,11 +390,22 @@ 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/Function2;
}

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 @@ -543,10 +554,18 @@ 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/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 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/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 class io/getstream/chat/android/compose/ui/attachments/preview/ComposableSingletons$MediaPreviewActivityKt {
Expand Down Expand Up @@ -1755,8 +1774,8 @@ public final class io/getstream/chat/android/compose/ui/theme/ReactionOptionsThe
public final class io/getstream/chat/android/compose/ui/theme/StreamColors {
public static final field $stable I
public static final field Companion Lio/getstream/chat/android/compose/ui/theme/StreamColors$Companion;
public synthetic fun <init> (JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIILkotlin/jvm/internal/DefaultConstructorMarker;)V
public synthetic fun <init> (JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJLkotlin/jvm/internal/DefaultConstructorMarker;)V
public synthetic fun <init> (JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIILkotlin/jvm/internal/DefaultConstructorMarker;)V
public synthetic fun <init> (JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJLkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1-0d7_KjU ()J
public final fun component10-0d7_KjU ()J
public final fun component11-0d7_KjU ()J
Expand Down Expand Up @@ -1784,14 +1803,16 @@ public final class io/getstream/chat/android/compose/ui/theme/StreamColors {
public final fun component31-0d7_KjU ()J
public final fun component32-0d7_KjU ()J
public final fun component33-0d7_KjU ()J
public final fun component34-0d7_KjU ()J
public final fun component35-0d7_KjU ()J
public final fun component4-0d7_KjU ()J
public final fun component5-0d7_KjU ()J
public final fun component6-0d7_KjU ()J
public final fun component7-0d7_KjU ()J
public final fun component8-0d7_KjU ()J
public final fun component9-0d7_KjU ()J
public final fun copy-ky7mje0 (JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ)Lio/getstream/chat/android/compose/ui/theme/StreamColors;
public static synthetic fun copy-ky7mje0$default (Lio/getstream/chat/android/compose/ui/theme/StreamColors;JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIILjava/lang/Object;)Lio/getstream/chat/android/compose/ui/theme/StreamColors;
public final fun copy-IWRRXjM (JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ)Lio/getstream/chat/android/compose/ui/theme/StreamColors;
public static synthetic fun copy-IWRRXjM$default (Lio/getstream/chat/android/compose/ui/theme/StreamColors;JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJIILjava/lang/Object;)Lio/getstream/chat/android/compose/ui/theme/StreamColors;
public fun equals (Ljava/lang/Object;)Z
public final fun getAppBackground-0d7_KjU ()J
public final fun getBarsBackground-0d7_KjU ()J
Expand All @@ -1806,6 +1827,8 @@ public final class io/getstream/chat/android/compose/ui/theme/StreamColors {
public final fun getInfoAccent-0d7_KjU ()J
public final fun getInputBackground-0d7_KjU ()J
public final fun getLinkBackground-0d7_KjU ()J
public final fun getMediaShimmerBase-0d7_KjU ()J
public final fun getMediaShimmerHighlights-0d7_KjU ()J
public final fun getOtherMessageQuotedBackground-0d7_KjU ()J
public final fun getOtherMessageQuotedText-0d7_KjU ()J
public final fun getOtherMessageText-0d7_KjU ()J
Expand Down Expand Up @@ -2058,7 +2081,18 @@ public final class io/getstream/chat/android/compose/ui/util/ChannelUtilsKt {
public static final fun isOneToOne (Lio/getstream/chat/android/models/Channel;Lio/getstream/chat/android/models/User;)Z
}

public final class io/getstream/chat/android/compose/ui/util/ComposableSingletons$ImageUtilsKt {
public static final field INSTANCE Lio/getstream/chat/android/compose/ui/util/ComposableSingletons$ImageUtilsKt;
public static field lambda-1 Lkotlin/jvm/functions/Function3;
public static field lambda-2 Lkotlin/jvm/functions/Function3;
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 class io/getstream/chat/android/compose/ui/util/ImageUtilsKt {
public static final fun StreamImage (Lkotlin/jvm/functions/Function0;Landroidx/compose/ui/Modifier;Lcom/skydoves/landscapist/components/ImageComponent;Lcom/skydoves/landscapist/ImageOptions;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/graphics/painter/Painter;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function5;Lkotlin/jvm/functions/Function4;Landroidx/compose/runtime/Composer;II)V
public static final fun StreamImage (Lkotlin/jvm/functions/Function0;Landroidx/compose/ui/Modifier;Lcom/skydoves/landscapist/components/ImageComponent;Lkotlin/jvm/functions/Function0;Lcom/skydoves/landscapist/ImageOptions;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/graphics/painter/Painter;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function5;Lkotlin/jvm/functions/Function4;Landroidx/compose/runtime/Composer;II)V
public static final fun mirrorRtl (Landroidx/compose/ui/Modifier;Landroidx/compose/ui/unit/LayoutDirection;)Landroidx/compose/ui/Modifier;
public static final fun rememberStreamImagePainter-MqR-F_0 (Lcoil/request/ImageRequest;Landroidx/compose/ui/graphics/painter/Painter;Landroidx/compose/ui/graphics/painter/Painter;Landroidx/compose/ui/graphics/painter/Painter;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/layout/ContentScale;ILandroidx/compose/runtime/Composer;II)Lcoil/compose/AsyncImagePainter;
public static final fun rememberStreamImagePainter-MqR-F_0 (Ljava/lang/Object;Landroidx/compose/ui/graphics/painter/Painter;Landroidx/compose/ui/graphics/painter/Painter;Landroidx/compose/ui/graphics/painter/Painter;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Landroidx/compose/ui/layout/ContentScale;ILandroidx/compose/runtime/Composer;II)Lcoil/compose/AsyncImagePainter;
Expand Down
3 changes: 3 additions & 0 deletions stream-chat-android-compose/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ dependencies {

// Coil
implementation Dependencies.composeCoil
implementation Dependencies.composeLandscapistCoil
implementation Dependencies.composeLandscapistPlaceholder
implementation Dependencies.composeLandscapistAnimation
implementation Dependencies.coilGif
implementation Dependencies.coilVideo

Expand Down
2 changes: 2 additions & 0 deletions stream-chat-android-compose/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,15 @@
<ID>MagicNumber:MediaAttachmentFactory.kt$0.25f</ID>
<ID>MagicNumber:MediaAttachmentQuotedContent.kt$0.8f</ID>
<ID>MagicNumber:MediaGalleryPreviewActivity.kt$MediaGalleryPreviewActivity$0.2f</ID>
<ID>MagicNumber:MediaGalleryPreviewActivity.kt$MediaGalleryPreviewActivity$0.4f</ID>
<ID>MagicNumber:MediaGalleryPreviewActivity.kt$MediaGalleryPreviewActivity$8f</ID>
<ID>MagicNumber:MediaPreviewPlaceHolder.kt$0.4f</ID>
<ID>MagicNumber:MessageComposer.kt$1000</ID>
<ID>MagicNumber:MessageComposer.kt$60_000</ID>
<ID>MagicNumber:Messages.kt$3</ID>
<ID>MagicNumber:Messages.kt$5</ID>
<ID>MagicNumber:SearchInput.kt$8f</ID>
<ID>MagicNumber:MediaAttachmentContent.kt$0.4f</ID>
<ID>MagicNumber:TypingIndicatorAnimatedDot.kt$0.5f</ID>
<ID>MaxLineLength:AttachmentsPickerTabFactories.kt$AttachmentsPickerTabFactories$*</ID>
<ID>MaxLineLength:ChatTheme.kt$error("No attachments picker tab factories provided! Make sure to wrap all usages of Stream components in a ChatTheme.")</ID>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package io.getstream.chat.android.compose.ui.attachments.content

import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.interaction.MutableInteractionSource
Expand Down Expand Up @@ -45,14 +44,15 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.skydoves.landscapist.ImageOptions
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.messages.attachments.AttachmentState
import io.getstream.chat.android.compose.ui.attachments.preview.handler.AttachmentPreviewHandler
import io.getstream.chat.android.compose.ui.theme.ChatTheme
import io.getstream.chat.android.compose.ui.util.MimeTypeIconProvider
import io.getstream.chat.android.compose.ui.util.rememberStreamImagePainter
import io.getstream.chat.android.compose.ui.util.StreamImage
import io.getstream.chat.android.compose.util.attachmentDownloadState
import io.getstream.chat.android.compose.util.onDownloadHandleRequest
import io.getstream.chat.android.models.Attachment
Expand Down Expand Up @@ -212,34 +212,43 @@ public fun FileAttachmentImage(attachment: Attachment) {
val isImage = attachment.isImage()
val isVideoWithThumbnails = attachment.isVideo() && ChatTheme.videoThumbnailsEnabled

val painter = when {
val data = when {
isImage -> {
val dataToLoad =
attachment.imagePreviewUrl?.applyStreamCdnImageResizingIfEnabled(ChatTheme.streamCdnImageResizing)
?: attachment.upload

rememberStreamImagePainter(dataToLoad)
dataToLoad
}

isVideoWithThumbnails -> {
val dataToLoad = attachment.thumbUrl?.applyStreamCdnImageResizingIfEnabled(ChatTheme.streamCdnImageResizing)
?: attachment.upload

rememberStreamImagePainter(dataToLoad)
dataToLoad
}
else -> painterResource(id = MimeTypeIconProvider.getIconRes(attachment.mimeType))

else -> MimeTypeIconProvider.getIconRes(attachment.mimeType)
}

val shape = if (isImage || isVideoWithThumbnails) ChatTheme.shapes.imageThumbnail else null

val imageModifier = Modifier.size(height = 40.dp, width = 35.dp).let { baseModifier ->
if (shape != null) baseModifier.clip(shape) else baseModifier
}
val imageModifier = Modifier
.size(height = 40.dp, width = 35.dp)
.let { baseModifier ->
if (shape != null) baseModifier.clip(shape) else baseModifier
}

Image(
StreamImage(
modifier = imageModifier,
painter = painter,
contentDescription = null,
contentScale = if (isImage || isVideoWithThumbnails) ContentScale.Crop else ContentScale.Fit,
data = { data },
imageOptions = ImageOptions(
contentScale = if (isImage || isVideoWithThumbnails) {
ContentScale.Crop
} else {
ContentScale.Fit
},
),
)
}

Expand All @@ -254,7 +263,5 @@ internal fun onFileAttachmentContentItemClick(
previewHandlers: List<AttachmentPreviewHandler>,
attachment: Attachment,
) {
previewHandlers
.firstOrNull { it.canHandle(attachment) }
?.handleAttachmentPreview(attachment)
previewHandlers.firstOrNull { it.canHandle(attachment) }?.handleAttachmentPreview(attachment)
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,15 @@

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

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import com.skydoves.landscapist.ImageOptions
import io.getstream.chat.android.compose.ui.theme.ChatTheme
import io.getstream.chat.android.compose.ui.util.MimeTypeIconProvider
import io.getstream.chat.android.compose.ui.util.rememberStreamImagePainter
import io.getstream.chat.android.compose.ui.util.StreamImage
import io.getstream.chat.android.models.Attachment
import io.getstream.chat.android.ui.common.images.resizing.applyStreamCdnImageResizingIfEnabled
import io.getstream.chat.android.ui.common.utils.extensions.imagePreviewUrl
Expand All @@ -42,26 +41,25 @@ public fun FileAttachmentQuotedContent(
) {
val isImage = attachment.type == "image"

val painter = if (isImage) {
val data = if (isImage) {
val dataToLoad =
attachment.imagePreviewUrl?.applyStreamCdnImageResizingIfEnabled(ChatTheme.streamCdnImageResizing)
?: attachment.upload

rememberStreamImagePainter(dataToLoad)
dataToLoad
} else {
painterResource(id = MimeTypeIconProvider.getIconRes(attachment.mimeType))
MimeTypeIconProvider.getIconRes(attachment.mimeType)
}

val startPadding = ChatTheme.dimens.quotedMessageAttachmentStartPadding
val verticalPadding = ChatTheme.dimens.quotedMessageAttachmentTopPadding
val size = ChatTheme.dimens.quotedMessageAttachmentPreviewSize

Image(
StreamImage(
modifier = modifier
.padding(start = startPadding, top = verticalPadding, bottom = verticalPadding)
.size(size),
painter = painter,
contentDescription = null,
contentScale = if (isImage) ContentScale.Crop else ContentScale.Fit,
data = { data },
imageOptions = ImageOptions(contentScale = if (isImage) ContentScale.Crop else ContentScale.Fit),
)
}
Loading

0 comments on commit 6387016

Please sign in to comment.