Room directory : fix pagination and add empty state.
This commit is contained in:
@@ -22,5 +22,4 @@ sealed interface RoomDirectoryEvents {
|
||||
data class Search(val query: String) : RoomDirectoryEvents
|
||||
data object LoadMore : RoomDirectoryEvents
|
||||
data class JoinRoom(val roomId: RoomId) : RoomDirectoryEvents
|
||||
data class SearchActiveChange(val isActive: Boolean) : RoomDirectoryEvents
|
||||
}
|
||||
|
||||
@@ -26,14 +26,11 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import io.element.android.features.roomdirectory.impl.root.model.RoomDescriptionUiModel
|
||||
import io.element.android.features.roomdirectory.impl.root.model.toUiModel
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryList
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryService
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -76,9 +73,6 @@ class RoomDirectoryPresenter @Inject constructor(
|
||||
is RoomDirectoryEvents.Search -> {
|
||||
searchQuery = event.query
|
||||
}
|
||||
is RoomDirectoryEvents.SearchActiveChange -> {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,23 +84,6 @@ class RoomDirectoryPresenter @Inject constructor(
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun searchResults(
|
||||
filteredRooms: ImmutableList<RoomDescriptionUiModel>,
|
||||
hasMoreToLoad: Boolean,
|
||||
isSearchActive: Boolean,
|
||||
): SearchBarResultState<ImmutableList<RoomDescriptionUiModel>> {
|
||||
return if (!isSearchActive) {
|
||||
SearchBarResultState.Initial()
|
||||
} else {
|
||||
if (filteredRooms.isEmpty() && !hasMoreToLoad) {
|
||||
SearchBarResultState.NoResultsFound()
|
||||
} else {
|
||||
SearchBarResultState.Results(filteredRooms)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RoomDirectoryList.collectItemsAsState() = remember {
|
||||
items.map { list ->
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package io.element.android.features.roomdirectory.impl.root
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
import androidx.compose.foundation.layout.Row
|
||||
@@ -26,6 +27,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
@@ -33,6 +35,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.TextFieldColors
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
@@ -49,6 +52,7 @@ import io.element.android.libraries.designsystem.components.button.BackButton
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.aliasScreenTitle
|
||||
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
|
||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||
import io.element.android.libraries.designsystem.theme.components.IconButton
|
||||
import io.element.android.libraries.designsystem.theme.components.Scaffold
|
||||
@@ -77,8 +81,8 @@ fun RoomDirectoryView(
|
||||
state.eventSink(RoomDirectoryEvents.JoinRoom(roomId))
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(padding)
|
||||
.consumeWindowInsets(padding)
|
||||
.padding(padding)
|
||||
.consumeWindowInsets(padding)
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -119,7 +123,10 @@ private fun RoomDirectoryContent(
|
||||
)
|
||||
RoomDirectoryRoomList(
|
||||
roomDescriptions = state.roomDescriptions,
|
||||
displayLoadMoreIndicator = state.displayLoadMoreIndicator,
|
||||
displayEmptyState = state.displayEmptyState,
|
||||
onResultClicked = onResultClicked,
|
||||
onReachedLoadMore = { state.eventSink(RoomDirectoryEvents.LoadMore) },
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -127,18 +134,52 @@ private fun RoomDirectoryContent(
|
||||
@Composable
|
||||
private fun RoomDirectoryRoomList(
|
||||
roomDescriptions: ImmutableList<RoomDescriptionUiModel>,
|
||||
displayLoadMoreIndicator: Boolean,
|
||||
displayEmptyState: Boolean,
|
||||
onResultClicked: (RoomId) -> Unit,
|
||||
onReachedLoadMore: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = modifier,
|
||||
) {
|
||||
LazyColumn(modifier = modifier) {
|
||||
items(roomDescriptions) { roomDescription ->
|
||||
RoomDirectoryRoomRow(
|
||||
roomDescription = roomDescription,
|
||||
onClick = onResultClicked,
|
||||
)
|
||||
}
|
||||
if (displayEmptyState) {
|
||||
item {
|
||||
Text(
|
||||
text = stringResource(id = CommonStrings.common_no_results),
|
||||
style = ElementTheme.typography.fontBodyLgRegular,
|
||||
color = ElementTheme.colors.textPlaceholder,
|
||||
modifier = Modifier.padding(16.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
if (displayLoadMoreIndicator) {
|
||||
item {
|
||||
LoadMoreIndicator(modifier = Modifier.fillMaxWidth())
|
||||
LaunchedEffect(Unit) {
|
||||
onReachedLoadMore()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LoadMoreIndicator(modifier: Modifier = Modifier) {
|
||||
Box(
|
||||
modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
.padding(8.dp),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
CircularProgressIndicator(
|
||||
strokeWidth = 2.dp,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,14 +244,14 @@ private fun RoomDirectoryRoomRow(
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { onClick(roomDescription.roomId) }
|
||||
.padding(
|
||||
top = 12.dp,
|
||||
bottom = 12.dp,
|
||||
start = 16.dp,
|
||||
)
|
||||
.height(IntrinsicSize.Min),
|
||||
.fillMaxWidth()
|
||||
.clickable { onClick(roomDescription.roomId) }
|
||||
.padding(
|
||||
top = 12.dp,
|
||||
bottom = 12.dp,
|
||||
start = 16.dp,
|
||||
)
|
||||
.height(IntrinsicSize.Min),
|
||||
) {
|
||||
Avatar(
|
||||
avatarData = roomDescription.avatarData,
|
||||
@@ -218,8 +259,8 @@ private fun RoomDirectoryRoomRow(
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(start = 16.dp)
|
||||
.weight(1f)
|
||||
.padding(start = 16.dp)
|
||||
) {
|
||||
Text(
|
||||
text = roomDescription.name,
|
||||
@@ -241,8 +282,8 @@ private fun RoomDirectoryRoomRow(
|
||||
text = stringResource(id = CommonStrings.action_join),
|
||||
color = ElementTheme.colors.textSuccessPrimary,
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterVertically)
|
||||
.padding(start = 4.dp, end = 12.dp)
|
||||
.align(Alignment.CenterVertically)
|
||||
.padding(start = 4.dp, end = 12.dp)
|
||||
)
|
||||
} else {
|
||||
Spacer(modifier = Modifier.width(24.dp))
|
||||
|
||||
@@ -19,8 +19,8 @@ package io.element.android.libraries.matrix.api.roomdirectory
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface RoomDirectoryList {
|
||||
suspend fun filter(filter: String?, batchSize: Int)
|
||||
suspend fun loadMore()
|
||||
suspend fun filter(filter: String?, batchSize: Int): Result<Unit>
|
||||
suspend fun loadMore(): Result<Unit>
|
||||
suspend fun hasMoreToLoad(): Boolean
|
||||
val items: Flow<List<RoomDescription>>
|
||||
}
|
||||
|
||||
@@ -42,27 +42,27 @@ class RoomDirectorySearchProcessor(
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun MutableList<RoomDescription>.applyUpdate(update: RoomDirectorySearchEntryUpdate) {
|
||||
private fun MutableList<RoomDescription>.applyUpdate(update: RoomDirectorySearchEntryUpdate) {
|
||||
when (update) {
|
||||
is RoomDirectorySearchEntryUpdate.Append -> {
|
||||
val roomSummaries = update.values.map(roomDescriptionMapper::map)
|
||||
addAll(roomSummaries)
|
||||
}
|
||||
is RoomDirectorySearchEntryUpdate.PushBack -> {
|
||||
val roomSummary = roomDescriptionMapper.map(update.value)
|
||||
add(roomSummary)
|
||||
val roomDescription = roomDescriptionMapper.map(update.value)
|
||||
add(roomDescription)
|
||||
}
|
||||
is RoomDirectorySearchEntryUpdate.PushFront -> {
|
||||
val roomSummary = roomDescriptionMapper.map(update.value)
|
||||
add(0, roomSummary)
|
||||
val roomDescription = roomDescriptionMapper.map(update.value)
|
||||
add(0, roomDescription)
|
||||
}
|
||||
is RoomDirectorySearchEntryUpdate.Set -> {
|
||||
val roomSummary = roomDescriptionMapper.map(update.value)
|
||||
this[update.index.toInt()] = roomSummary
|
||||
val roomDescription = roomDescriptionMapper.map(update.value)
|
||||
this[update.index.toInt()] = roomDescription
|
||||
}
|
||||
is RoomDirectorySearchEntryUpdate.Insert -> {
|
||||
val roomSummary = roomDescriptionMapper.map(update.value)
|
||||
add(update.index.toInt(), roomSummary)
|
||||
val roomDescription = roomDescriptionMapper.map(update.value)
|
||||
add(update.index.toInt(), roomDescription)
|
||||
}
|
||||
is RoomDirectorySearchEntryUpdate.Remove -> {
|
||||
removeAt(update.index.toInt())
|
||||
|
||||
@@ -18,6 +18,7 @@ package io.element.android.libraries.matrix.impl.roomdirectory
|
||||
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomDescription
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryList
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
@@ -32,12 +33,11 @@ import org.matrix.rustcomponents.sdk.RoomDirectorySearch as InnerRoomDirectorySe
|
||||
|
||||
class RustRoomDirectoryList(
|
||||
private val inner: InnerRoomDirectorySearch,
|
||||
private val sessionCoroutineScope: CoroutineScope,
|
||||
private val sessionDispatcher: CoroutineDispatcher,
|
||||
sessionCoroutineScope: CoroutineScope,
|
||||
sessionDispatcher: CoroutineDispatcher,
|
||||
) : RoomDirectoryList {
|
||||
|
||||
private val _items = MutableSharedFlow<List<RoomDescription>>()
|
||||
|
||||
private val _items = MutableSharedFlow<List<RoomDescription>>(replay = 1)
|
||||
private val processor = RoomDirectorySearchProcessor(_items, sessionDispatcher, RoomDescriptionMapper())
|
||||
|
||||
init {
|
||||
@@ -51,12 +51,26 @@ class RustRoomDirectoryList(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun filter(filter: String?, batchSize: Int) {
|
||||
inner.search(filter, batchSize.toUInt())
|
||||
override suspend fun filter(filter: String?, batchSize: Int): Result<Unit> {
|
||||
return try {
|
||||
inner.search(filter = filter, batchSize = batchSize.toUInt())
|
||||
Result.success(Unit)
|
||||
} catch (e: CancellationException) {
|
||||
throw e
|
||||
} catch (e: Exception) {
|
||||
Result.failure(e)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun loadMore() {
|
||||
inner.nextPage()
|
||||
override suspend fun loadMore(): Result<Unit> {
|
||||
return try {
|
||||
inner.nextPage()
|
||||
Result.success(Unit)
|
||||
} catch (e: CancellationException) {
|
||||
throw e
|
||||
} catch (e: Exception) {
|
||||
Result.failure(e)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun hasMoreToLoad(): Boolean {
|
||||
|
||||
Reference in New Issue
Block a user