From 78601326b8d97a1197f1aece9862e6909e6a0642 Mon Sep 17 00:00:00 2001 From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Date: Thu, 16 Oct 2025 07:56:14 +0000 Subject: [PATCH] android, desktop: smoother scroll of member list achieved by async member image loading (#6365) --- .../chat/simplex/common/platform/Images.kt | 28 ++++++++++++++++++ .../views/chat/group/GroupChatInfoView.kt | 2 +- .../views/chat/group/GroupMemberInfoView.kt | 6 ++-- .../common/views/helpers/ChatInfoImage.kt | 29 ++++++++++++------- 4 files changed, 51 insertions(+), 14 deletions(-) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Images.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Images.kt index 19e40ab0a2..d7a241d5ee 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Images.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Images.kt @@ -1,7 +1,15 @@ package chat.simplex.common.platform +import androidx.compose.foundation.Image +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.produceState +import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.layout.ContentScale import boofcv.struct.image.GrayU8 +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext import java.io.ByteArrayOutputStream import java.io.InputStream import java.net.URI @@ -23,3 +31,23 @@ expect fun isImage(uri: URI): Boolean expect fun isAnimImage(uri: URI, drawable: Any?): Boolean expect fun loadImageBitmap(inputStream: InputStream): ImageBitmap + +@Composable +fun Base64AsyncImage( + base64ImageString: String, + contentDescription: String?, + contentScale: ContentScale, + modifier: Modifier = Modifier +) { + val imageBitmap by produceState(initialValue = null, base64ImageString) { + value = withContext(Dispatchers.IO) { base64ToBitmap(base64ImageString) } + } + imageBitmap?.let { + Image( + bitmap = it, + contentDescription = contentDescription, + contentScale = contentScale, + modifier = modifier + ) + } +} diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupChatInfoView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupChatInfoView.kt index 1ea3daeab1..46669ead70 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupChatInfoView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupChatInfoView.kt @@ -867,7 +867,7 @@ fun MemberRow(member: GroupMember, user: Boolean = false, infoPage: Boolean = tr verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(4.dp) ) { - MemberProfileImage(size = MEMBER_ROW_AVATAR_SIZE, member) + MemberProfileImage(size = MEMBER_ROW_AVATAR_SIZE, member, async = true) Spacer(Modifier.width(DEFAULT_PADDING_HALF)) Column { Row(verticalAlignment = Alignment.CenterVertically) { diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupMemberInfoView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupMemberInfoView.kt index b29374390e..7febf2a904 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupMemberInfoView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupMemberInfoView.kt @@ -714,14 +714,16 @@ fun MemberProfileImage( size: Dp, mem: GroupMember, color: Color = MaterialTheme.colors.secondaryVariant, - backgroundColor: Color? = null + backgroundColor: Color? = null, + async: Boolean = false ) { ProfileImage( size = size, image = mem.image, color = color, backgroundColor = backgroundColor, - blurred = mem.blocked + blurred = mem.blocked, + async = async ) } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/ChatInfoImage.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/ChatInfoImage.kt index 72ef8c623d..5f3a73e7ea 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/ChatInfoImage.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/ChatInfoImage.kt @@ -12,12 +12,9 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.* -import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.* import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.platform.InspectableValue import androidx.compose.ui.unit.* -import chat.simplex.common.model.BusinessChatType import dev.icerock.moko.resources.compose.painterResource import dev.icerock.moko.resources.compose.stringResource import chat.simplex.common.model.ChatInfo @@ -57,7 +54,8 @@ fun ProfileImage( icon: ImageResource = MR.images.ic_account_circle_filled, color: Color = MaterialTheme.colors.secondaryVariant, backgroundColor: Color? = null, - blurred: Boolean = false + blurred: Boolean = false, + async: Boolean = false ) { Box(Modifier.size(size)) { if (image == null) { @@ -85,13 +83,22 @@ fun ProfileImage( ) } } else { - val imageBitmap = base64ToBitmap(image) - Image( - imageBitmap, - stringResource(MR.strings.image_descr_profile_image), - contentScale = ContentScale.Crop, - modifier = ProfileIconModifier(size, blurred = blurred) - ) + if (async) { + Base64AsyncImage( + base64ImageString = image, + contentDescription = stringResource(MR.strings.image_descr_profile_image), + contentScale = ContentScale.Crop, + modifier = ProfileIconModifier(size, blurred = blurred) + ) + } else { + val imageBitmap = base64ToBitmap(image) + Image( + bitmap = imageBitmap, + contentDescription = stringResource(MR.strings.image_descr_profile_image), + contentScale = ContentScale.Crop, + modifier = ProfileIconModifier(size, blurred = blurred) + ) + } } } }