RoomList: simple implementation of setRange
This commit is contained in:
@@ -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 = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -12,6 +12,7 @@ import java.util.*
|
||||
|
||||
interface RoomSummaryDataSource {
|
||||
fun roomSummaries(): Flow<List<RoomSummary>>
|
||||
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<RoomSummary>.() -> Unit) = withContext(coroutineDispatchers.diffUpdateDispatcher){
|
||||
val mutableRoomSummaries = roomSummaries.value.toMutableList()
|
||||
block(mutableRoomSummaries)
|
||||
roomSummaries.value = mutableRoomSummaries
|
||||
}
|
||||
private suspend fun updateRoomSummaries(block: MutableList<RoomSummary>.() -> Unit) =
|
||||
withContext(coroutineDispatchers.diffUpdateDispatcher) {
|
||||
val mutableRoomSummaries = roomSummaries.value.toMutableList()
|
||||
block(mutableRoomSummaries)
|
||||
roomSummaries.value = mutableRoomSummaries
|
||||
}
|
||||
|
||||
fun SlidingSyncViewRoomsListDiff.isInvalidation(): Boolean {
|
||||
return when (this) {
|
||||
|
||||
Reference in New Issue
Block a user