Browse Source

RoomDirectory : continue implementing the search

pull/2620/head
ganfra 6 months ago
parent
commit
4c5ae6ae4b
  1. 71
      features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenter.kt
  2. 12
      features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryState.kt
  3. 22
      features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryStateProvider.kt
  4. 179
      features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryView.kt
  5. 81
      features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/datasource/RoomDirectoryDataSource.kt
  6. 19
      features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/model/RoomDescriptionUiModel.kt
  7. 2
      libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarSize.kt
  8. 8
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomdirectory/RoomDirectoryList.kt
  9. 2
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomdirectory/RoomDirectoryService.kt
  10. 6
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/di/SessionMatrixModule.kt
  11. 25
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RustRoomDirectoryList.kt
  12. 6
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RustRoomDirectoryService.kt
  13. 4
      libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomdirectory/FakeRoomDirectoryService.kt

71
features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryPresenter.kt

@ -21,73 +21,98 @@ import androidx.compose.runtime.LaunchedEffect @@ -21,73 +21,98 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.produceState
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.datasource.RoomDirectoryDataSource
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.core.RoomId
import kotlinx.coroutines.CoroutineScope
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
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import javax.inject.Inject
class RoomDirectoryPresenter @Inject constructor(
private val client: MatrixClient,
private val dataSource: RoomDirectoryDataSource,
private val roomDirectoryService: RoomDirectoryService,
) : Presenter<RoomDirectoryState> {
private val roomDirectoryList = roomDirectoryService.createRoomDirectoryList()
@Composable
override fun present(): RoomDirectoryState {
var searchQuery by rememberSaveable {
mutableStateOf("")
}
var isSearchActive by rememberSaveable {
mutableStateOf(false)
val allRooms by roomDirectoryList.collectItemsAsState()
val hasMoreToLoad by produceState(initialValue = true, allRooms) {
value = roomDirectoryList.hasMoreToLoad()
}
val roomSummaries by dataSource.all.collectAsState()
val coroutineScope = rememberCoroutineScope()
LaunchedEffect(searchQuery) {
dataSource.updateSearchQuery(searchQuery)
roomDirectoryList.filter(searchQuery, 20)
}
fun handleEvents(event: RoomDirectoryEvents) {
when (event) {
is RoomDirectoryEvents.JoinRoom -> {
coroutineScope.joinRoom(event.roomId)
//coroutineScope.joinRoom(event.roomId)
}
RoomDirectoryEvents.LoadMore -> {
coroutineScope.launch {
dataSource.loadMore()
roomDirectoryList.loadMore()
}
}
is RoomDirectoryEvents.Search -> {
searchQuery = event.query
}
is RoomDirectoryEvents.SearchActiveChange -> {
isSearchActive = event.isActive
if (!isSearchActive) {
searchQuery = ""
}
}
}
}
return RoomDirectoryState(
query = searchQuery,
isSearchActive = isSearchActive,
roomSummaries = roomSummaries,
searchResults = SearchBarResultState.Initial(),
roomDescriptions = allRooms,
displayLoadMoreIndicator = hasMoreToLoad,
eventSink = ::handleEvents
)
}
private fun CoroutineScope.joinRoom(roomId: RoomId) = launch {
client.getRoom(roomId)?.join()
@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 ->
list
.map { roomDescription -> roomDescription.toUiModel() }
.toImmutableList()
}.flowOn(Dispatchers.Default)
}.collectAsState(persistentListOf())
}

12
features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryState.kt

@ -16,14 +16,14 @@ @@ -16,14 +16,14 @@
package io.element.android.features.roomdirectory.impl.root
import io.element.android.features.roomdirectory.impl.root.model.RoomDirectoryRoomSummary
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
import io.element.android.features.roomdirectory.impl.root.model.RoomDescriptionUiModel
import kotlinx.collections.immutable.ImmutableList
data class RoomDirectoryState(
val query: String,
val roomSummaries: ImmutableList<RoomDirectoryRoomSummary>,
val searchResults: SearchBarResultState<ImmutableList<RoomDirectoryRoomSummary>>,
val isSearchActive: Boolean,
val roomDescriptions: ImmutableList<RoomDescriptionUiModel>,
val displayLoadMoreIndicator: Boolean,
val eventSink: (RoomDirectoryEvents) -> Unit
)
) {
val displayEmptyState = roomDescriptions.isEmpty() && !displayLoadMoreIndicator
}

22
features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryStateProvider.kt

@ -17,7 +17,7 @@ @@ -17,7 +17,7 @@
package io.element.android.features.roomdirectory.impl.root
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.features.roomdirectory.impl.root.model.RoomDirectoryRoomSummary
import io.element.android.features.roomdirectory.impl.root.model.RoomDescriptionUiModel
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
@ -31,8 +31,8 @@ open class RoomDirectorySearchStateProvider : PreviewParameterProvider<RoomDirec @@ -31,8 +31,8 @@ open class RoomDirectorySearchStateProvider : PreviewParameterProvider<RoomDirec
aRoomDirectoryState(),
aRoomDirectoryState(
query = "Element",
roomSummaries = persistentListOf(
RoomDirectoryRoomSummary(
roomDescriptions = persistentListOf(
RoomDescriptionUiModel(
roomId = RoomId("@exa:matrix.org"),
name = "Element X Android",
description = "Element X is a secure, private and decentralized messenger.",
@ -40,11 +40,11 @@ open class RoomDirectorySearchStateProvider : PreviewParameterProvider<RoomDirec @@ -40,11 +40,11 @@ open class RoomDirectorySearchStateProvider : PreviewParameterProvider<RoomDirec
id = "@exa:matrix.org",
name = "Element X Android",
url = null,
size = AvatarSize.RoomDirectorySearchItem
size = AvatarSize.RoomDirectoryItem
),
canBeJoined = true,
),
RoomDirectoryRoomSummary(
RoomDescriptionUiModel(
roomId = RoomId("@exi:matrix.org"),
name = "Element X iOS",
description = "Element X is a secure, private and decentralized messenger.",
@ -52,7 +52,7 @@ open class RoomDirectorySearchStateProvider : PreviewParameterProvider<RoomDirec @@ -52,7 +52,7 @@ open class RoomDirectorySearchStateProvider : PreviewParameterProvider<RoomDirec
id = "@exi:matrix.org",
name = "Element X iOS",
url = null,
size = AvatarSize.RoomDirectorySearchItem
size = AvatarSize.RoomDirectoryItem
),
canBeJoined = false,
)
@ -64,12 +64,12 @@ open class RoomDirectorySearchStateProvider : PreviewParameterProvider<RoomDirec @@ -64,12 +64,12 @@ open class RoomDirectorySearchStateProvider : PreviewParameterProvider<RoomDirec
fun aRoomDirectoryState(
query: String = "",
isSearchActive: Boolean = false,
roomSummaries: ImmutableList<RoomDirectoryRoomSummary> = persistentListOf(),
searchResults: SearchBarResultState<ImmutableList<RoomDirectoryRoomSummary>> = SearchBarResultState.Initial(),
displayLoadMoreIndicator: Boolean = false,
roomDescriptions: ImmutableList<RoomDescriptionUiModel> = persistentListOf(),
searchResults: SearchBarResultState<ImmutableList<RoomDescriptionUiModel>> = SearchBarResultState.Initial(),
) = RoomDirectoryState(
query = query,
isSearchActive = isSearchActive,
roomSummaries = roomSummaries,
searchResults = searchResults,
roomDescriptions = roomDescriptions,
displayLoadMoreIndicator = displayLoadMoreIndicator,
eventSink = {},
)

179
features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/RoomDirectoryView.kt

@ -17,37 +17,33 @@ @@ -17,37 +17,33 @@
package io.element.android.features.roomdirectory.impl.root
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxSize
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.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.text.KeyboardActions
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.draw.drawBehind
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme
import io.element.android.compound.tokens.generated.CompoundIcons
import io.element.android.features.roomdirectory.impl.root.model.RoomDirectoryRoomSummary
import io.element.android.features.roomdirectory.impl.root.model.RoomDescriptionUiModel
import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.preview.ElementPreview
@ -56,7 +52,6 @@ import io.element.android.libraries.designsystem.theme.aliasScreenTitle @@ -56,7 +52,6 @@ import io.element.android.libraries.designsystem.theme.aliasScreenTitle
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
import io.element.android.libraries.designsystem.theme.components.SearchBar
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.designsystem.theme.components.TextField
import io.element.android.libraries.designsystem.theme.components.TopAppBar
@ -109,71 +104,107 @@ private fun RoomDirectoryTopBar( @@ -109,71 +104,107 @@ private fun RoomDirectoryTopBar(
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun RoomDirectoryContent(
state: RoomDirectoryState,
onResultClicked: (RoomId) -> Unit,
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(16.dp),
) {
SearchBar(
Column(modifier = modifier) {
SearchTextField(
query = state.query,
onQueryChange = { query ->
state.eventSink(RoomDirectoryEvents.Search(query))
},
active = state.isSearchActive,
onActiveChange = {
state.eventSink(RoomDirectoryEvents.SearchActiveChange(it))
},
resultState = state.searchResults,
placeHolderTitle = stringResource(id = CommonStrings.action_search),
) { results ->
RoomDirectoryRoomList(
rooms = results,
onResultClicked = onResultClicked,
)
}
if (!state.isSearchActive) {
RoomDirectoryRoomList(
rooms = state.roomSummaries,
onResultClicked = onResultClicked,
)
}
onQueryChange = { state.eventSink(RoomDirectoryEvents.Search(it)) },
placeholder = stringResource(id = CommonStrings.action_search),
modifier = Modifier.fillMaxWidth(),
)
RoomDirectoryRoomList(
roomDescriptions = state.roomDescriptions,
onResultClicked = onResultClicked,
)
}
}
@Composable
private fun RoomDirectoryRoomList(
rooms: ImmutableList<RoomDirectoryRoomSummary>,
roomDescriptions: ImmutableList<RoomDescriptionUiModel>,
onResultClicked: (RoomId) -> Unit,
modifier: Modifier = Modifier,
) {
LazyColumn(
modifier = modifier,
) {
items(rooms) { room ->
items(roomDescriptions) { roomDescription ->
RoomDirectoryRoomRow(
room = room,
roomDescription = roomDescription,
onClick = onResultClicked,
)
}
}
}
@Composable
private fun SearchTextField(
query: String,
onQueryChange: (String) -> Unit,
placeholder: String,
modifier: Modifier = Modifier,
colors: TextFieldColors = TextFieldDefaults.colors(
focusedContainerColor = Color.Transparent,
unfocusedContainerColor = Color.Transparent,
unfocusedPlaceholderColor = ElementTheme.colors.textPlaceholder,
focusedPlaceholderColor = ElementTheme.colors.textPlaceholder,
focusedTextColor = ElementTheme.colors.textPrimary,
unfocusedTextColor = ElementTheme.colors.textPrimary,
focusedIndicatorColor = ElementTheme.colors.borderInteractiveSecondary,
unfocusedIndicatorColor = ElementTheme.colors.borderInteractiveSecondary,
),
) {
val focusManager = LocalFocusManager.current
TextField(
modifier = modifier,
textStyle = ElementTheme.typography.fontBodyLgRegular,
singleLine = true,
value = query,
onValueChange = onQueryChange,
keyboardActions = KeyboardActions(
onSearch = {
focusManager.clearFocus()
}
),
colors = colors,
placeholder = { Text(placeholder) },
trailingIcon = {
if (query.isNotEmpty()) {
IconButton(
onClick = {
onQueryChange("")
}
) {
Icon(
imageVector = CompoundIcons.Close(),
contentDescription = stringResource(CommonStrings.action_clear),
)
}
} else {
Icon(
imageVector = CompoundIcons.Search(),
contentDescription = stringResource(CommonStrings.action_search),
)
}
},
)
}
@Composable
private fun RoomDirectoryRoomRow(
room: RoomDirectoryRoomSummary,
roomDescription: RoomDescriptionUiModel,
onClick: (RoomId) -> Unit,
modifier: Modifier = Modifier,
) {
Row(
modifier = modifier
.fillMaxWidth()
.clickable { onClick(room.roomId) }
.clickable { onClick(roomDescription.roomId) }
.padding(
top = 12.dp,
bottom = 12.dp,
@ -182,7 +213,7 @@ private fun RoomDirectoryRoomRow( @@ -182,7 +213,7 @@ private fun RoomDirectoryRoomRow(
.height(IntrinsicSize.Min),
) {
Avatar(
avatarData = room.avatarData,
avatarData = roomDescription.avatarData,
modifier = Modifier.align(Alignment.CenterVertically)
)
Column(
@ -191,21 +222,21 @@ private fun RoomDirectoryRoomRow( @@ -191,21 +222,21 @@ private fun RoomDirectoryRoomRow(
.padding(start = 16.dp)
) {
Text(
text = room.name,
text = roomDescription.name,
maxLines = 1,
style = ElementTheme.typography.fontBodyLgRegular,
color = ElementTheme.colors.textPrimary,
overflow = TextOverflow.Ellipsis,
)
Text(
text = room.description,
text = roomDescription.description,
maxLines = 1,
style = ElementTheme.typography.fontBodyMdRegular,
color = ElementTheme.colors.textSecondary,
overflow = TextOverflow.Ellipsis,
)
}
if (room.canBeJoined) {
if (roomDescription.canBeJoined) {
Text(
text = stringResource(id = CommonStrings.action_join),
color = ElementTheme.colors.textSuccessPrimary,
@ -219,64 +250,6 @@ private fun RoomDirectoryRoomRow( @@ -219,64 +250,6 @@ private fun RoomDirectoryRoomRow(
}
}
@Composable
@OptIn(ExperimentalMaterial3Api::class)
private fun RoomDirectorySearchTopBar(
query: String,
onQueryChanged: (String) -> Unit,
onBackPressed: () -> Unit,
modifier: Modifier = Modifier,
) {
val borderColor = ElementTheme.colors.borderInteractivePrimary
val borderStroke = 1.dp
TopAppBar(
modifier = modifier.drawBehind {
drawLine(
color = borderColor,
start = Offset(0f, size.height),
end = Offset(size.width, size.height),
strokeWidth = borderStroke.value
)
},
title = {
val focusRequester = FocusRequester()
TextField(
modifier = Modifier
.fillMaxWidth()
.focusRequester(focusRequester),
value = query,
singleLine = true,
onValueChange = onQueryChanged,
colors = TextFieldDefaults.colors(
focusedContainerColor = Color.Transparent,
unfocusedContainerColor = Color.Transparent,
disabledContainerColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent,
errorIndicatorColor = Color.Transparent,
),
trailingIcon = {
if (query.isNotEmpty()) {
IconButton(onClick = {
onQueryChanged("")
}) {
Icon(
imageVector = CompoundIcons.Close(),
contentDescription = stringResource(CommonStrings.action_cancel),
)
}
}
}
)
LaunchedEffect(Unit) {
focusRequester.requestFocus()
}
},
navigationIcon = { BackButton(onClick = onBackPressed) },
)
}
@PreviewsDayNight
@Composable
fun RoomDirectorySearchViewLightPreview(@PreviewParameter(RoomDirectorySearchStateProvider::class) state: RoomDirectoryState) = ElementPreview {

81
features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/datasource/RoomDirectoryDataSource.kt

@ -1,81 +0,0 @@ @@ -1,81 +0,0 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.features.roomdirectory.impl.root.datasource
import io.element.android.features.roomdirectory.impl.root.model.RoomDirectoryRoomSummary
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.matrix.api.core.RoomId
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.flowOf
import javax.inject.Inject
class RoomDirectoryDataSource @Inject constructor() {
private val _searchResults = MutableStateFlow<ImmutableList<RoomDirectoryRoomSummary>>(persistentListOf())
suspend fun updateSearchQuery(searchQuery: String) {
//TODO branch to matrix sdk
if (searchQuery.isEmpty()) {
_searchResults.value = persistentListOf()
} else {
delay(100)
_searchResults.value = all.value.filter {
it.name.contains(searchQuery)
}.toImmutableList()
}
}
suspend fun loadMore() {
//TODO branch to matrix sdk
}
val all: StateFlow<ImmutableList<RoomDirectoryRoomSummary>> = MutableStateFlow(
persistentListOf(
RoomDirectoryRoomSummary(
roomId = RoomId("!exa:matrix.org"),
name = "Element X Android",
description = "Element X is a secure, private and decentralized messenger.",
avatarData = AvatarData(
id = "!exa:matrix.org",
name = "Element X Android",
url = null,
size = AvatarSize.RoomDirectorySearchItem
),
canBeJoined = true,
),
RoomDirectoryRoomSummary(
roomId = RoomId("!exi:matrix.org"),
name = "Element X iOS",
description = "Element X is a secure, private and decentralized messenger.",
avatarData = AvatarData(
id = "!exi:matrix.org",
name = "Element X iOS",
url = null,
size = AvatarSize.RoomDirectorySearchItem
),
canBeJoined = false,
)
)
)
val searchResults: StateFlow<ImmutableList<RoomDirectoryRoomSummary>> = _searchResults
}

19
features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/model/RoomDirectoryRoomSummary.kt → features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/model/RoomDescriptionUiModel.kt

@ -17,12 +17,29 @@ @@ -17,12 +17,29 @@
package io.element.android.features.roomdirectory.impl.root.model
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.roomdirectory.RoomDescription
data class RoomDirectoryRoomSummary(
data class RoomDescriptionUiModel(
val roomId: RoomId,
val name: String,
val description: String,
val avatarData: AvatarData,
val canBeJoined: Boolean,
)
fun RoomDescription.toUiModel(): RoomDescriptionUiModel {
return RoomDescriptionUiModel(
roomId = roomId,
name = name ?: "",
description = topic ?: "",
avatarData = AvatarData(
id = roomId.value,
name = name ?: "",
url = avatarUrl,
size = AvatarSize.RoomDirectoryItem,
),
canBeJoined = joinRule == RoomDescription.JoinRule.PUBLIC,
)
}

2
libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/AvatarSize.kt

@ -53,5 +53,5 @@ enum class AvatarSize(val dp: Dp) { @@ -53,5 +53,5 @@ enum class AvatarSize(val dp: Dp) {
CustomRoomNotificationSetting(36.dp),
RoomDirectorySearchItem(36.dp),
RoomDirectoryItem(36.dp),
}

8
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomdirectory/RoomDirectorySearch.kt → libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomdirectory/RoomDirectoryList.kt

@ -16,11 +16,11 @@ @@ -16,11 +16,11 @@
package io.element.android.libraries.matrix.api.roomdirectory
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.Flow
interface RoomDirectorySearch {
suspend fun updateQuery(query: String?, batchSize: Int)
interface RoomDirectoryList {
suspend fun filter(filter: String?, batchSize: Int)
suspend fun loadMore()
suspend fun hasMoreToLoad(): Boolean
val results: SharedFlow<List<RoomDescription>>
val items: Flow<List<RoomDescription>>
}

2
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomdirectory/RoomDirectoryService.kt

@ -17,5 +17,5 @@ @@ -17,5 +17,5 @@
package io.element.android.libraries.matrix.api.roomdirectory
interface RoomDirectoryService {
fun search(): RoomDirectorySearch
fun createRoomDirectoryList(): RoomDirectoryList
}

6
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/di/SessionMatrixModule.kt

@ -26,6 +26,7 @@ import io.element.android.libraries.matrix.api.encryption.EncryptionService @@ -26,6 +26,7 @@ import io.element.android.libraries.matrix.api.encryption.EncryptionService
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryService
import io.element.android.libraries.matrix.api.roomlist.RoomListService
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
import kotlinx.coroutines.CoroutineScope
@ -68,4 +69,9 @@ object SessionMatrixModule { @@ -68,4 +69,9 @@ object SessionMatrixModule {
fun provideSessionCoroutineScope(matrixClient: MatrixClient): CoroutineScope {
return matrixClient.sessionCoroutineScope
}
@Provides
fun providesRoomDirectoryService(matrixClient: MatrixClient): RoomDirectoryService {
return matrixClient.roomDirectoryService()
}
}

25
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RustRoomDirectorySearch.kt → libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RustRoomDirectoryList.kt

@ -17,26 +17,28 @@ @@ -17,26 +17,28 @@
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.RoomDirectorySearch
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryList
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlin.time.Duration.Companion.milliseconds
import org.matrix.rustcomponents.sdk.RoomDirectorySearch as InnerRoomDirectorySearch
class RustRoomDirectorySearch(
class RustRoomDirectoryList(
private val inner: InnerRoomDirectorySearch,
private val sessionCoroutineScope: CoroutineScope,
private val sessionDispatcher: CoroutineDispatcher,
) : RoomDirectorySearch {
) : RoomDirectoryList {
private val _results: MutableStateFlow<List<RoomDescription>> =
MutableStateFlow(emptyList())
private val _items = MutableSharedFlow<List<RoomDescription>>()
private val processor = RoomDirectorySearchProcessor(_results, sessionDispatcher, RoomDescriptionMapper())
private val processor = RoomDirectorySearchProcessor(_items, sessionDispatcher, RoomDescriptionMapper())
init {
sessionCoroutineScope.launch(sessionDispatcher) {
@ -49,8 +51,8 @@ class RustRoomDirectorySearch( @@ -49,8 +51,8 @@ class RustRoomDirectorySearch(
}
}
override suspend fun updateQuery(query: String?, batchSize: Int) {
inner.search(query, batchSize.toUInt())
override suspend fun filter(filter: String?, batchSize: Int) {
inner.search(filter, batchSize.toUInt())
}
override suspend fun loadMore() {
@ -61,5 +63,6 @@ class RustRoomDirectorySearch( @@ -61,5 +63,6 @@ class RustRoomDirectorySearch(
return !inner.isAtLastPage()
}
override val results: SharedFlow<List<RoomDescription>> = _results
@OptIn(FlowPreview::class)
override val items: Flow<List<RoomDescription>> = _items.debounce(200.milliseconds)
}

6
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RustRoomDirectoryService.kt

@ -16,7 +16,7 @@ @@ -16,7 +16,7 @@
package io.element.android.libraries.matrix.impl.roomdirectory
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectorySearch
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryList
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryService
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
@ -28,8 +28,8 @@ class RustRoomDirectoryService( @@ -28,8 +28,8 @@ class RustRoomDirectoryService(
private val sessionDispatcher: CoroutineDispatcher,
) : RoomDirectoryService {
override fun search(): RoomDirectorySearch {
return RustRoomDirectorySearch(client.roomDirectorySearch(), sessionCoroutineScope, sessionDispatcher)
override fun createRoomDirectoryList(): RoomDirectoryList {
return RustRoomDirectoryList(client.roomDirectorySearch(), sessionCoroutineScope, sessionDispatcher)
}
}

4
libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomdirectory/FakeRoomDirectoryService.kt

@ -16,11 +16,11 @@ @@ -16,11 +16,11 @@
package io.element.android.libraries.matrix.test.roomdirectory
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectorySearch
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryList
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryService
class FakeRoomDirectoryService : RoomDirectoryService {
override fun search(): RoomDirectorySearch {
override fun createRoomDirectoryList(): RoomDirectoryList {
TODO("Not yet implemented")
}
}

Loading…
Cancel
Save