From a75b906414b80c37e0059eed0f691443c610974c Mon Sep 17 00:00:00 2001 From: ganfra Date: Wed, 23 Nov 2022 14:48:26 +0100 Subject: [PATCH] RoomList: simple implementation of setRange --- .../x/features/roomlist/RoomListScreen.kt | 30 +++- .../x/features/roomlist/RoomListViewModel.kt | 7 + .../roomlist/components/RoomSummaryRow.kt | 137 ++++++++++++++++++ .../element/android/x/matrix/MatrixClient.kt | 13 +- .../x/matrix/room/RoomSummaryDataSource.kt | 21 ++- 5 files changed, 194 insertions(+), 14 deletions(-) create mode 100644 features/roomlist/src/main/java/io/element/android/x/features/roomlist/components/RoomSummaryRow.kt diff --git a/features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListScreen.kt b/features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListScreen.kt index 0340d8ccc0..93781efb19 100644 --- a/features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListScreen.kt +++ b/features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListScreen.kt @@ -13,7 +13,9 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.rememberTopAppBarState import androidx.compose.runtime.Composable +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.nestedScroll import androidx.compose.ui.tooling.preview.Preview @@ -25,7 +27,7 @@ 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.RoomItem +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.model.MatrixUser import io.element.android.x.features.roomlist.model.RoomListRoomSummary @@ -56,6 +58,7 @@ fun RoomListScreen( isLoginOut = logoutAction is Loading, filter = filter, onFilterChanged = viewModel::filterRoom, + onScrollOver = viewModel::updateVisibleRange ) } @@ -67,12 +70,31 @@ fun RoomListContent( filter: String, onFilterChanged: (String) -> Unit, onLogoutClicked: () -> Unit, + onScrollOver: (IntRange) -> Unit, isLoginOut: Boolean, ) { + + fun onRoomClicked(room: RoomListRoomSummary) { + onRoomClicked(room.roomId) + } + val appBarState = rememberTopAppBarState() val lazyListState = rememberLazyListState() + + val visibleRange by remember { + derivedStateOf { + val layoutInfo = lazyListState.layoutInfo + val firstItemIndex = layoutInfo.visibleItemsInfo.firstOrNull()?.index ?: 0 + val size = layoutInfo.visibleItemsInfo.size + firstItemIndex until firstItemIndex + size + } + } + if (!lazyListState.isScrollInProgress) { + onScrollOver(visibleRange) + } val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(appBarState) LogCompositions(tag = "RoomListScreen", msg = "Content") + Scaffold( modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), topBar = { @@ -91,9 +113,7 @@ fun RoomListContent( state = lazyListState, ) { items(roomSummaries) { room -> - RoomItem(room = room) { - onRoomClicked(it) - } + RoomSummaryRow(room = room, onClick = ::onRoomClicked) } } } @@ -120,6 +140,7 @@ private fun PreviewableRoomListContent() { filter = "filter", onFilterChanged = {}, isLoginOut = false, + onScrollOver = {} ) } } @@ -136,6 +157,7 @@ private fun PreviewableDarkRoomListContent() { filter = "filter", onFilterChanged = {}, isLoginOut = true, + onScrollOver = {} ) } } diff --git a/features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListViewModel.kt b/features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListViewModel.kt index 6aef08b98a..bd411e04c9 100644 --- a/features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListViewModel.kt +++ b/features/roomlist/src/main/java/io/element/android/x/features/roomlist/RoomListViewModel.kt @@ -66,6 +66,13 @@ class RoomListViewModel( } } + fun updateVisibleRange(range: IntRange) { + viewModelScope.launch { + if (range.isEmpty()) return@launch + client.roomSummaryDataSource().setSlidingSyncRange(range) + } + } + private fun handleInit() { suspend { val userAvatarUrl = client.loadUserAvatarURLString().getOrNull() diff --git a/features/roomlist/src/main/java/io/element/android/x/features/roomlist/components/RoomSummaryRow.kt b/features/roomlist/src/main/java/io/element/android/x/features/roomlist/components/RoomSummaryRow.kt new file mode 100644 index 0000000000..b1ef6f2ccf --- /dev/null +++ b/features/roomlist/src/main/java/io/element/android/x/features/roomlist/components/RoomSummaryRow.kt @@ -0,0 +1,137 @@ +package io.element.android.x.features.roomlist.components + +import Avatar +import androidx.compose.foundation.background +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.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.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.google.accompanist.placeholder.material.placeholder +import io.element.android.x.features.roomlist.model.RoomListRoomSummary + +private val minHeight = 72.dp + +@Composable +internal fun RoomSummaryRow( + modifier: Modifier = Modifier, + room: RoomListRoomSummary, + onClick: (RoomListRoomSummary) -> Unit +) { + + val clickModifier = if (room.isPlaceholder) { + Modifier + } else { + Modifier.clickable( + onClick = { onClick(room) }, + indication = rememberRipple(), + interactionSource = remember { MutableInteractionSource() } + ) + } + Box( + modifier = modifier + .fillMaxWidth() + .heightIn(min = minHeight) + .then(clickModifier) + ) { + DefaultRoomSummaryRow(modifier = modifier, room = room) + } + +} + +@Composable +internal fun DefaultRoomSummaryRow( + modifier: Modifier = Modifier, + room: RoomListRoomSummary, +) { + val placeholderShape = PlaceholderShape() + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp) + .height(IntrinsicSize.Min), + verticalAlignment = CenterVertically + ) { + Avatar( + room.avatarData, + modifier = Modifier.placeholder(room.isPlaceholder, shape = CircleShape) + ) + Column( + modifier = Modifier + .padding(start = 12.dp, end = 4.dp, top = 12.dp, bottom = 12.dp) + .alignByBaseline() + .weight(1f) + ) { + // Name + Text( + modifier = Modifier + .placeholder(room.isPlaceholder, shape = placeholderShape), + fontSize = 16.sp, + fontWeight = FontWeight.SemiBold, + text = room.name, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + // Last Message + Text( + modifier = Modifier.placeholder(room.isPlaceholder, shape = placeholderShape), + text = room.lastMessage?.toString().orEmpty(), + color = MaterialTheme.colorScheme.secondary, + fontSize = 14.sp, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + // Timestamp and Unread + Column( + modifier = Modifier + .alignByBaseline(), + ) { + Text( + modifier = Modifier.placeholder(room.isPlaceholder, shape = placeholderShape), + fontSize = 12.sp, + text = room.timestamp ?: "", + color = MaterialTheme.colorScheme.secondary, + ) + Spacer(modifier.size(4.dp)) + val unreadIndicatorColor = + if (room.hasUnread) MaterialTheme.colorScheme.primary else Color.Transparent + Box( + modifier = Modifier + .size(12.dp) + .clip(CircleShape) + .background(unreadIndicatorColor) + .align(Alignment.End), + ) + } + } +} + +@Composable +fun PlaceholderShape(): GenericShape { + return GenericShape { size, _ -> + val rect = Rect( + 0f, + size.height / 4, + size.width, + size.height - size.height / 4 + ) + addRect(rect) + } +} \ No newline at end of file diff --git a/libraries/matrix/src/main/java/io/element/android/x/matrix/MatrixClient.kt b/libraries/matrix/src/main/java/io/element/android/x/matrix/MatrixClient.kt index ff93387c76..24b7793721 100644 --- a/libraries/matrix/src/main/java/io/element/android/x/matrix/MatrixClient.kt +++ b/libraries/matrix/src/main/java/io/element/android/x/matrix/MatrixClient.kt @@ -44,19 +44,19 @@ class MatrixClient internal constructor( .requiredState( requiredState = listOf( RequiredState(key = "m.room.avatar", value = ""), - RequiredState(key = "m.room.name", value = ""), RequiredState(key = "m.room.encryption", value = ""), ) ) .name(name = "HomeScreenView") - .syncMode(mode = SlidingSyncMode.FULL_SYNC) + .syncMode(mode = SlidingSyncMode.SELECTIVE) + .addRange(0u, 10u) .build() private val slidingSync = client .slidingSync() .homeserver("https://slidingsync.lab.element.dev") .withCommonExtensions() - .coldCache("ElementX") + //.coldCache("ElementX") .addView(slidingSyncView) .build() @@ -66,7 +66,8 @@ class MatrixClient internal constructor( slidingSyncObserverProxy.updateSummaryFlow, slidingSync, slidingSyncView, - dispatchers + dispatchers, + ::onRestartSync ) private var slidingSyncObserverToken: StoppableSpawn? = null @@ -77,6 +78,10 @@ class MatrixClient internal constructor( client.setDelegate(clientDelegate) } + private fun onRestartSync(){ + slidingSyncObserverToken = slidingSync.sync() + } + fun getRoom(roomId: String): MatrixRoom? { val slidingSyncRoom = slidingSync.getRoom(roomId) ?: return null val room = slidingSyncRoom.fullRoom() ?: return null diff --git a/libraries/matrix/src/main/java/io/element/android/x/matrix/room/RoomSummaryDataSource.kt b/libraries/matrix/src/main/java/io/element/android/x/matrix/room/RoomSummaryDataSource.kt index 60799ad58a..91af2c5eca 100644 --- a/libraries/matrix/src/main/java/io/element/android/x/matrix/room/RoomSummaryDataSource.kt +++ b/libraries/matrix/src/main/java/io/element/android/x/matrix/room/RoomSummaryDataSource.kt @@ -12,6 +12,7 @@ import java.util.* interface RoomSummaryDataSource { fun roomSummaries(): Flow> + fun setSlidingSyncRange(range: IntRange) } internal class RustRoomSummaryDataSource( @@ -19,7 +20,8 @@ internal class RustRoomSummaryDataSource( private val slidingSync: SlidingSync, private val slidingSyncView: SlidingSyncView, private val coroutineDispatchers: CoroutineDispatchers, - private val roomSummaryDetailsFactory: RoomSummaryDetailsFactory = RoomSummaryDetailsFactory() + private val onRestartSync: () -> Unit, + private val roomSummaryDetailsFactory: RoomSummaryDetailsFactory = RoomSummaryDetailsFactory(), ) : RoomSummaryDataSource, Closeable { private val coroutineScope = CoroutineScope(SupervisorJob() + coroutineDispatchers.io) @@ -69,6 +71,12 @@ internal class RustRoomSummaryDataSource( return roomSummaries.sample(50) } + override fun setSlidingSyncRange(range: IntRange) { + Timber.v("setVisibleRange=$range") + slidingSyncView.setRange(range.first.toUInt(), range.last.toUInt()) + onRestartSync() + } + private suspend fun didReceiveSyncUpdate(summary: UpdateSummary) { Timber.v("UpdateRooms with identifiers: ${summary.rooms}") if (state.value != SlidingSyncState.LIVE) { @@ -140,11 +148,12 @@ internal class RustRoomSummaryDataSource( ) } - private suspend fun updateRoomSummaries(block: MutableList.() -> Unit) = withContext(coroutineDispatchers.diffUpdateDispatcher){ - val mutableRoomSummaries = roomSummaries.value.toMutableList() - block(mutableRoomSummaries) - roomSummaries.value = mutableRoomSummaries - } + private suspend fun updateRoomSummaries(block: MutableList.() -> Unit) = + withContext(coroutineDispatchers.diffUpdateDispatcher) { + val mutableRoomSummaries = roomSummaries.value.toMutableList() + block(mutableRoomSummaries) + roomSummaries.value = mutableRoomSummaries + } fun SlidingSyncViewRoomsListDiff.isInvalidation(): Boolean { return when (this) {