android, desktop: smoother scroll of member list achieved by async member image loading (#6365)

This commit is contained in:
spaced4ndy
2025-10-16 07:56:14 +00:00
committed by GitHub
parent ff1054dab7
commit 78601326b8
4 changed files with 51 additions and 14 deletions
@@ -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<ImageBitmap?>(initialValue = null, base64ImageString) {
value = withContext(Dispatchers.IO) { base64ToBitmap(base64ImageString) }
}
imageBitmap?.let {
Image(
bitmap = it,
contentDescription = contentDescription,
contentScale = contentScale,
modifier = modifier
)
}
}
@@ -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) {
@@ -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
)
}
@@ -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)
)
}
}
}
}