RoomList: avoid recomposition on avatar and add placeholder

This commit is contained in:
ganfra
2022-11-03 10:58:53 +01:00
parent 66dc61cc3e
commit c66b7a699c
9 changed files with 145 additions and 54 deletions

View File

@@ -28,6 +28,7 @@ import com.airbnb.mvrx.Success
import com.airbnb.mvrx.compose.collectAsState
import com.airbnb.mvrx.compose.mavericksViewModel
import io.element.android.x.core.data.LogCompositions
import io.element.android.x.designsystem.components.avatar.AvatarData
import io.element.android.x.features.roomlist.model.MatrixUser
import io.element.android.x.features.roomlist.model.RoomListRoomSummary
import io.element.android.x.features.roomlist.model.RoomListViewState
@@ -49,7 +50,7 @@ fun RoomListScreen(
val matrixUser by viewModel.collectAsState(RoomListViewState::user)
RoomListContent(
roomSummaries = roomSummaries().orEmpty(),
matrixUser = matrixUser,
matrixUser = matrixUser(),
onRoomClicked = onRoomClicked,
onLogoutClicked = viewModel::logout
)
@@ -58,7 +59,7 @@ fun RoomListScreen(
@Composable
fun RoomListContent(
roomSummaries: List<RoomListRoomSummary>,
matrixUser: MatrixUser,
matrixUser: MatrixUser?,
onRoomClicked: (RoomId) -> Unit,
onLogoutClicked: () -> Unit,
) {
@@ -84,15 +85,16 @@ fun RoomListContent(
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun RoomListTopBar(matrixUser: MatrixUser, onLogoutClicked: () -> Unit) {
fun RoomListTopBar(matrixUser: MatrixUser?, onLogoutClicked: () -> Unit) {
LogCompositions(tag = "RoomListScreen", msg = "TopBar")
if (matrixUser == null) return
TopAppBar(
title = {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
Avatar(data = matrixUser.avatarData, size = 32.dp)
Avatar(matrixUser.avatarData)
Spacer(modifier = Modifier.width(8.dp))
Text("${matrixUser.username}")
}
@@ -131,7 +133,7 @@ private fun RoomItem(
.padding(horizontal = 16.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Avatar(data = room.avatarData)
Avatar(room.avatarData)
Column(
modifier = Modifier
.padding(12.dp)
@@ -182,13 +184,13 @@ private fun PreviewableRoomListContent() {
hasUnread = true,
timestamp = "14:18",
lastMessage = "A message",
avatarData = null,
avatarData = AvatarData("R"),
id = "roomId"
)
)
RoomListContent(
roomSummaries = roomSummaries,
matrixUser = MatrixUser("User#1"),
matrixUser = MatrixUser("User#1", avatarData = AvatarData("U")),
onRoomClicked = {},
onLogoutClicked = {}
)

View File

@@ -5,6 +5,7 @@ import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.MavericksViewModel
import com.airbnb.mvrx.Success
import io.element.android.x.core.data.parallelMap
import io.element.android.x.designsystem.components.avatar.AvatarData
import io.element.android.x.features.roomlist.model.MatrixUser
import io.element.android.x.features.roomlist.model.RoomListRoomSummary
import io.element.android.x.features.roomlist.model.RoomListViewState
@@ -41,17 +42,18 @@ class RoomListViewModel(initialState: RoomListViewState) :
viewModelScope.launch {
val client = getClient()
client.startSync()
val userAvatarUrl = client.loadUserAvatarURLString().getOrNull()
val userDisplayName = client.loadUserDisplayName().getOrNull()
val avatarData = loadAvatarData(client, userAvatarUrl)
setState {
copy(
user = MatrixUser(
username = userDisplayName,
avatarUrl = userAvatarUrl,
avatarData = avatarData,
)
suspend {
val userAvatarUrl = client.loadUserAvatarURLString().getOrNull()
val userDisplayName = client.loadUserDisplayName().getOrNull()
val avatarData =
loadAvatarData(client, userDisplayName ?: client.userId().value, userAvatarUrl)
MatrixUser(
username = userDisplayName ?: client.userId().value,
avatarUrl = userAvatarUrl,
avatarData = avatarData,
)
}.execute {
copy(user = it)
}
client.roomSummaryDataSource().roomSummaries()
.map { roomSummaries ->
@@ -75,7 +77,11 @@ class RoomListViewModel(initialState: RoomListViewState) :
isPlaceholder = true
)
is RoomSummary.Filled -> {
val avatarData = loadAvatarData(client, roomSummary.details.avatarURLString)
val avatarData = loadAvatarData(
client,
roomSummary.details.name,
roomSummary.details.avatarURLString
)
RoomListRoomSummary(
id = roomSummary.identifier(),
name = roomSummary.details.name,
@@ -89,7 +95,12 @@ class RoomListViewModel(initialState: RoomListViewState) :
}
}
private suspend fun loadAvatarData(client: MatrixClient, url: String?, size: Long = 48): ByteArray? {
private suspend fun loadAvatarData(
client: MatrixClient,
name: String,
url: String?,
size: Long = 48
): AvatarData {
val mediaContent = url?.let {
val mediaSource = mediaSourceFromUrl(it)
client.loadMediaThumbnailForSource(mediaSource, size, size)
@@ -97,7 +108,9 @@ class RoomListViewModel(initialState: RoomListViewState) :
return mediaContent?.fold(
{ it },
{ null }
)
).let { model ->
AvatarData(name.first().toString(), model, size.toInt())
}
}
private fun handleLogout() {

View File

@@ -1,10 +1,11 @@
package io.element.android.x.features.roomlist.model
import androidx.compose.runtime.Stable
import io.element.android.x.designsystem.components.avatar.AvatarData
@Stable
data class MatrixUser(
val username: String? = null,
val avatarUrl: String? = null,
val avatarData: ByteArray? = null,
val avatarData: AvatarData = AvatarData(),
)

View File

@@ -1,5 +1,6 @@
package io.element.android.x.features.roomlist.model
import io.element.android.x.designsystem.components.avatar.AvatarData
import io.element.android.x.matrix.core.RoomId
data class RoomListRoomSummary(
@@ -9,6 +10,6 @@ data class RoomListRoomSummary(
val hasUnread: Boolean = false,
val timestamp: String? = null,
val lastMessage: CharSequence? = null,
val avatarData: ByteArray? = null,
val avatarData: AvatarData = AvatarData(),
val isPlaceholder: Boolean = false,
)

View File

@@ -7,7 +7,7 @@ import io.element.android.x.features.roomlist.model.MatrixUser
import io.element.android.x.matrix.room.RoomSummary
data class RoomListViewState(
val user: MatrixUser = MatrixUser(),
val user: Async<MatrixUser> = Uninitialized,
val rooms: Async<List<RoomListRoomSummary>> = Uninitialized,
val canLoadMore: Boolean = false,
val logoutAction: Async<Unit> = Uninitialized,