RoomList: avoid recomposition
This commit is contained in:
@@ -17,8 +17,10 @@ import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Velocity
|
||||
import com.airbnb.mvrx.Loading
|
||||
import com.airbnb.mvrx.Success
|
||||
import com.airbnb.mvrx.compose.collectAsState
|
||||
@@ -27,8 +29,8 @@ import io.element.android.x.core.compose.LogCompositions
|
||||
import io.element.android.x.designsystem.ElementXTheme
|
||||
import io.element.android.x.designsystem.components.ProgressDialog
|
||||
import io.element.android.x.designsystem.components.avatar.AvatarData
|
||||
import io.element.android.x.features.roomlist.components.RoomSummaryRow
|
||||
import io.element.android.x.features.roomlist.components.RoomListTopBar
|
||||
import io.element.android.x.features.roomlist.components.RoomSummaryRow
|
||||
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
|
||||
@@ -89,12 +91,18 @@ fun RoomListContent(
|
||||
firstItemIndex until firstItemIndex + size
|
||||
}
|
||||
}
|
||||
if (!lazyListState.isScrollInProgress) {
|
||||
onScrollOver(visibleRange)
|
||||
}
|
||||
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(appBarState)
|
||||
LogCompositions(tag = "RoomListScreen", msg = "Content")
|
||||
|
||||
val nestedScrollConnection = remember {
|
||||
object : NestedScrollConnection {
|
||||
override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
|
||||
onScrollOver(visibleRange)
|
||||
return super.onPostFling(consumed, available)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
@@ -109,7 +117,9 @@ fun RoomListContent(
|
||||
content = { padding ->
|
||||
Column(modifier = Modifier.padding(padding)) {
|
||||
LazyColumn(
|
||||
modifier = Modifier.weight(1f),
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.nestedScroll(nestedScrollConnection),
|
||||
state = lazyListState,
|
||||
) {
|
||||
items(roomSummaries) { room ->
|
||||
|
||||
@@ -6,8 +6,8 @@ import io.element.android.x.designsystem.components.avatar.AvatarData
|
||||
import io.element.android.x.designsystem.components.avatar.AvatarSize
|
||||
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.RoomListRoomSummaryPlaceholders
|
||||
import io.element.android.x.features.roomlist.model.RoomListViewState
|
||||
import io.element.android.x.features.roomlist.model.createFakePlaceHolders
|
||||
import io.element.android.x.matrix.MatrixClient
|
||||
import io.element.android.x.matrix.MatrixInstance
|
||||
import io.element.android.x.matrix.media.MediaResolver
|
||||
@@ -114,7 +114,7 @@ class RoomListViewModel(
|
||||
// Note: this second case will prevent to handle correctly the empty case
|
||||
(it is Success && it().isEmpty() && filter.isEmpty()) -> {
|
||||
// Show fake placeholders to avoid having empty screen
|
||||
Loading(createFakePlaceHolders())
|
||||
Loading(RoomListRoomSummaryPlaceholders.createFakeList(size = 16))
|
||||
}
|
||||
else -> {
|
||||
it
|
||||
@@ -129,7 +129,7 @@ class RoomListViewModel(
|
||||
): List<RoomListRoomSummary> {
|
||||
return roomSummaries.parallelMap { roomSummary ->
|
||||
when (roomSummary) {
|
||||
is RoomSummary.Empty -> RoomListRoomSummary.placeholder(roomSummary.identifier)
|
||||
is RoomSummary.Empty -> RoomListRoomSummaryPlaceholders.create(roomSummary.identifier)
|
||||
is RoomSummary.Filled -> {
|
||||
val avatarData = loadAvatarData(
|
||||
roomSummary.details.name,
|
||||
|
||||
@@ -6,20 +6,26 @@ import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.GenericShape
|
||||
import androidx.compose.material.ripple.rememberRipple
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Alignment.Companion.CenterVertically
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.geometry.Rect
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Outline
|
||||
import androidx.compose.ui.graphics.Path
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.Density
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.google.accompanist.placeholder.material.placeholder
|
||||
@@ -49,18 +55,15 @@ internal fun RoomSummaryRow(
|
||||
.heightIn(min = minHeight)
|
||||
.then(clickModifier)
|
||||
) {
|
||||
DefaultRoomSummaryRow(modifier = modifier, room = room)
|
||||
DefaultRoomSummaryRow(room = room)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun DefaultRoomSummaryRow(
|
||||
modifier: Modifier = Modifier,
|
||||
room: RoomListRoomSummary,
|
||||
) {
|
||||
val placeholderShape = PlaceholderShape()
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -81,7 +84,7 @@ internal fun DefaultRoomSummaryRow(
|
||||
// Name
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.placeholder(room.isPlaceholder, shape = placeholderShape),
|
||||
.placeholder(room.isPlaceholder, shape = TextPlaceholderShape),
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
text = room.name,
|
||||
@@ -90,7 +93,7 @@ internal fun DefaultRoomSummaryRow(
|
||||
)
|
||||
// Last Message
|
||||
Text(
|
||||
modifier = Modifier.placeholder(room.isPlaceholder, shape = placeholderShape),
|
||||
modifier = Modifier.placeholder(room.isPlaceholder, shape = TextPlaceholderShape),
|
||||
text = room.lastMessage?.toString().orEmpty(),
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
fontSize = 14.sp,
|
||||
@@ -104,12 +107,12 @@ internal fun DefaultRoomSummaryRow(
|
||||
.alignByBaseline(),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.placeholder(room.isPlaceholder, shape = placeholderShape),
|
||||
modifier = Modifier.placeholder(room.isPlaceholder, shape = TextPlaceholderShape),
|
||||
fontSize = 12.sp,
|
||||
text = room.timestamp ?: "",
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
)
|
||||
Spacer(modifier.size(4.dp))
|
||||
Spacer(Modifier.size(4.dp))
|
||||
val unreadIndicatorColor =
|
||||
if (room.hasUnread) MaterialTheme.colorScheme.primary else Color.Transparent
|
||||
Box(
|
||||
@@ -123,15 +126,25 @@ internal fun DefaultRoomSummaryRow(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PlaceholderShape(): GenericShape {
|
||||
return GenericShape { size, _ ->
|
||||
val rect = Rect(
|
||||
0f,
|
||||
size.height / 4,
|
||||
size.width,
|
||||
size.height - size.height / 4
|
||||
)
|
||||
addRect(rect)
|
||||
val TextPlaceholderShape = PercentRectangleSizeShape(0.5f)
|
||||
|
||||
class PercentRectangleSizeShape(private val percent: Float) : Shape {
|
||||
override fun createOutline(
|
||||
size: Size,
|
||||
layoutDirection: LayoutDirection,
|
||||
density: Density
|
||||
): Outline {
|
||||
val halfPercent = percent / 2f
|
||||
val path = Path().apply {
|
||||
val rect = Rect(
|
||||
0f,
|
||||
size.height * halfPercent,
|
||||
size.width,
|
||||
size.height - (size.height * halfPercent)
|
||||
)
|
||||
addRect(rect)
|
||||
close()
|
||||
}
|
||||
return Outline.Generic(path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package io.element.android.x.features.roomlist.model
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import io.element.android.x.designsystem.components.avatar.AvatarData
|
||||
import io.element.android.x.matrix.core.RoomId
|
||||
|
||||
@Stable
|
||||
data class RoomListRoomSummary(
|
||||
val id: String,
|
||||
val roomId: RoomId = RoomId(id),
|
||||
@@ -11,19 +13,4 @@ data class RoomListRoomSummary(
|
||||
val timestamp: String? = null,
|
||||
val lastMessage: CharSequence? = null,
|
||||
val avatarData: AvatarData = AvatarData(),
|
||||
val isPlaceholder: Boolean = false,
|
||||
) {
|
||||
|
||||
companion object {
|
||||
fun placeholder(id: String): RoomListRoomSummary {
|
||||
return RoomListRoomSummary(
|
||||
id = id,
|
||||
isPlaceholder = true,
|
||||
name = "Short name",
|
||||
timestamp = "hh:mm",
|
||||
lastMessage = "Last message for placeholder",
|
||||
avatarData = AvatarData("S")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
val isPlaceholder: Boolean = false,)
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package io.element.android.x.features.roomlist.model
|
||||
|
||||
import io.element.android.x.designsystem.components.avatar.AvatarData
|
||||
|
||||
|
||||
object RoomListRoomSummaryPlaceholders {
|
||||
|
||||
fun create(id: String): RoomListRoomSummary {
|
||||
return RoomListRoomSummary(
|
||||
id = id,
|
||||
isPlaceholder = true,
|
||||
name = "Short name",
|
||||
timestamp = "hh:mm",
|
||||
lastMessage = "Last message for placeholder",
|
||||
avatarData = AvatarData("S")
|
||||
)
|
||||
}
|
||||
|
||||
fun createFakeList(size: Int): List<RoomListRoomSummary> {
|
||||
return mutableListOf<RoomListRoomSummary>().apply {
|
||||
for (i in 0..size) {
|
||||
add(create("\$fakeRoom$i"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
package io.element.android.x.features.roomlist.model
|
||||
|
||||
|
||||
/**
|
||||
* Create a list of 16 RoomListRoomSummary placeholders
|
||||
*/
|
||||
fun createFakePlaceHolders(): List<RoomListRoomSummary> {
|
||||
return mutableListOf<RoomListRoomSummary>().apply {
|
||||
for (i in 0..15) {
|
||||
add(RoomListRoomSummary.placeholder("\$fakeRoom$i"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,6 @@ internal fun stubbedRoomSummaries(): List<RoomListRoomSummary> {
|
||||
avatarData = AvatarData("Z"),
|
||||
id = "roomId2"
|
||||
),
|
||||
RoomListRoomSummary.placeholder("roomId2")
|
||||
RoomListRoomSummaryPlaceholders.create("roomId2")
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user