Merge pull request #1865 from vector-im/feature/fga/room_list_dynamic_api
RoomList : rework a bit the api and make usage of entriesWithDynamicAdapter
This commit is contained in:
commit
6e4635b4cd
17 changed files with 317 additions and 213 deletions
|
|
@ -57,7 +57,7 @@ class InviteListPresenter @Inject constructor(
|
||||||
override fun present(): InviteListState {
|
override fun present(): InviteListState {
|
||||||
val invites by client
|
val invites by client
|
||||||
.roomListService
|
.roomListService
|
||||||
.invites()
|
.invites
|
||||||
.summaries
|
.summaries
|
||||||
.collectAsState()
|
.collectAsState()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ class ForwardMessagesPresenter @AssistedInject constructor(
|
||||||
var results: SearchBarResultState<ImmutableList<RoomSummaryDetails>> by remember { mutableStateOf(SearchBarResultState.NotSearching()) }
|
var results: SearchBarResultState<ImmutableList<RoomSummaryDetails>> by remember { mutableStateOf(SearchBarResultState.NotSearching()) }
|
||||||
val forwardingActionState: MutableState<Async<ImmutableList<RoomId>>> = remember { mutableStateOf(Async.Uninitialized) }
|
val forwardingActionState: MutableState<Async<ImmutableList<RoomId>>> = remember { mutableStateOf(Async.Uninitialized) }
|
||||||
|
|
||||||
val summaries by client.roomListService.allRooms().summaries.collectAsState()
|
val summaries by client.roomListService.allRooms.summaries.collectAsState()
|
||||||
|
|
||||||
LaunchedEffect(query, summaries) {
|
LaunchedEffect(query, summaries) {
|
||||||
val filteredSummaries = summaries.filterIsInstance<RoomSummary.Filled>()
|
val filteredSummaries = summaries.filterIsInstance<RoomSummary.Filled>()
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ class EditDefaultNotificationSettingPresenter @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CoroutineScope.observeRoomSummaries(roomsWithUserDefinedMode: MutableState<List<RoomSummary.Filled>>) {
|
private fun CoroutineScope.observeRoomSummaries(roomsWithUserDefinedMode: MutableState<List<RoomSummary.Filled>>) {
|
||||||
roomListService.allRooms()
|
roomListService.allRooms
|
||||||
.summaries
|
.summaries
|
||||||
.onEach {
|
.onEach {
|
||||||
updateRoomsWithUserDefinedMode(it, roomsWithUserDefinedMode)
|
updateRoomsWithUserDefinedMode(it, roomsWithUserDefinedMode)
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ class DefaultInviteStateDataSource @Inject constructor(
|
||||||
override fun inviteState(): InvitesState {
|
override fun inviteState(): InvitesState {
|
||||||
val invites by client
|
val invites by client
|
||||||
.roomListService
|
.roomListService
|
||||||
.invites()
|
.invites
|
||||||
.summaries
|
.summaries
|
||||||
.collectAsState()
|
.collectAsState()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ class RoomListDataSource @Inject constructor(
|
||||||
|
|
||||||
fun launchIn(coroutineScope: CoroutineScope) {
|
fun launchIn(coroutineScope: CoroutineScope) {
|
||||||
roomListService
|
roomListService
|
||||||
.allRooms()
|
.allRooms
|
||||||
.summaries
|
.summaries
|
||||||
.onEach { roomSummaries ->
|
.onEach { roomSummaries ->
|
||||||
replaceWith(roomSummaries)
|
replaceWith(roomSummaries)
|
||||||
|
|
@ -106,7 +106,7 @@ class RoomListDataSource @Inject constructor(
|
||||||
notificationSettingsService.notificationSettingsChangeFlow
|
notificationSettingsService.notificationSettingsChangeFlow
|
||||||
.debounce(0.5.seconds)
|
.debounce(0.5.seconds)
|
||||||
.onEach {
|
.onEach {
|
||||||
roomListService.rebuildRoomSummaries()
|
roomListService.allRooms.rebuildSummaries()
|
||||||
}
|
}
|
||||||
.launchIn(appScope)
|
.launchIn(appScope)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 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.libraries.matrix.api.roomlist
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RoomList with dynamic filtering and loading.
|
||||||
|
* This is useful for large lists of rooms.
|
||||||
|
* It lets load rooms on demand and filter them.
|
||||||
|
*/
|
||||||
|
interface DynamicRoomList : RoomList {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val DEFAULT_PAGE_SIZE = 20
|
||||||
|
const val DEFAULT_PAGES_TO_LOAD = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed interface Filter {
|
||||||
|
/**
|
||||||
|
* No filter applied.
|
||||||
|
*/
|
||||||
|
data object All : Filter
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter all rooms.
|
||||||
|
*/
|
||||||
|
data object None : Filter
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter rooms by normalized room name.
|
||||||
|
*/
|
||||||
|
data class NormalizedMatchRoomName(val pattern: String) : Filter
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load more rooms into the list if possible.
|
||||||
|
*/
|
||||||
|
suspend fun loadMore()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the list to its initial size.
|
||||||
|
*/
|
||||||
|
suspend fun reset()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the filter to apply to the list.
|
||||||
|
* @param filter the filter to apply.
|
||||||
|
*/
|
||||||
|
suspend fun updateFilter(filter: Filter)
|
||||||
|
}
|
||||||
|
|
@ -28,6 +28,7 @@ import kotlin.time.Duration
|
||||||
* Can be retrieved from [RoomListService] methods.
|
* Can be retrieved from [RoomListService] methods.
|
||||||
*/
|
*/
|
||||||
interface RoomList {
|
interface RoomList {
|
||||||
|
|
||||||
sealed interface LoadingState {
|
sealed interface LoadingState {
|
||||||
data object NotLoaded : LoadingState
|
data object NotLoaded : LoadingState
|
||||||
data class Loaded(val numberOfRooms: Int) : LoadingState
|
data class Loaded(val numberOfRooms: Int) : LoadingState
|
||||||
|
|
@ -43,6 +44,12 @@ interface RoomList {
|
||||||
* This is useful to know if a specific set of rooms is loaded or not.
|
* This is useful to know if a specific set of rooms is loaded or not.
|
||||||
*/
|
*/
|
||||||
val loadingState: StateFlow<LoadingState>
|
val loadingState: StateFlow<LoadingState>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force a refresh of the room summaries.
|
||||||
|
* Might be useful for some situations where we are not notified of changes.
|
||||||
|
*/
|
||||||
|
suspend fun rebuildSummaries()
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun RoomList.awaitLoaded(timeout: Duration = Duration.INFINITE) {
|
suspend fun RoomList.awaitLoaded(timeout: Duration = Duration.INFINITE) {
|
||||||
|
|
|
||||||
|
|
@ -41,12 +41,12 @@ interface RoomListService {
|
||||||
* returns a [RoomList] object of all rooms we want to display.
|
* returns a [RoomList] object of all rooms we want to display.
|
||||||
* This will exclude some rooms like the invites, or spaces.
|
* This will exclude some rooms like the invites, or spaces.
|
||||||
*/
|
*/
|
||||||
fun allRooms(): RoomList
|
val allRooms: RoomList
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns a [RoomList] object of all invites.
|
* returns a [RoomList] object of all invites.
|
||||||
*/
|
*/
|
||||||
fun invites(): RoomList
|
val invites: RoomList
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will set the visible range of all rooms.
|
* Will set the visible range of all rooms.
|
||||||
|
|
@ -54,11 +54,6 @@ interface RoomListService {
|
||||||
*/
|
*/
|
||||||
fun updateAllRoomsVisibleRange(range: IntRange)
|
fun updateAllRoomsVisibleRange(range: IntRange)
|
||||||
|
|
||||||
/**
|
|
||||||
* Rebuild the room summaries, required when we know some data may have changed. (E.g. room notification settings)
|
|
||||||
*/
|
|
||||||
fun rebuildRoomSummaries()
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The sync indicator as a flow.
|
* The sync indicator as a flow.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ import io.element.android.libraries.matrix.impl.room.MatrixRoomInfoMapper
|
||||||
import io.element.android.libraries.matrix.impl.room.RoomContentForwarder
|
import io.element.android.libraries.matrix.impl.room.RoomContentForwarder
|
||||||
import io.element.android.libraries.matrix.impl.room.RoomSyncSubscriber
|
import io.element.android.libraries.matrix.impl.room.RoomSyncSubscriber
|
||||||
import io.element.android.libraries.matrix.impl.room.RustMatrixRoom
|
import io.element.android.libraries.matrix.impl.room.RustMatrixRoom
|
||||||
|
import io.element.android.libraries.matrix.impl.roomlist.RoomListFactory
|
||||||
import io.element.android.libraries.matrix.impl.roomlist.RustRoomListService
|
import io.element.android.libraries.matrix.impl.roomlist.RustRoomListService
|
||||||
import io.element.android.libraries.matrix.impl.roomlist.roomOrNull
|
import io.element.android.libraries.matrix.impl.roomlist.roomOrNull
|
||||||
import io.element.android.libraries.matrix.impl.sync.RustSyncService
|
import io.element.android.libraries.matrix.impl.sync.RustSyncService
|
||||||
|
|
@ -171,7 +172,11 @@ class RustMatrixClient constructor(
|
||||||
RustRoomListService(
|
RustRoomListService(
|
||||||
innerRoomListService = innerRoomListService,
|
innerRoomListService = innerRoomListService,
|
||||||
sessionCoroutineScope = sessionCoroutineScope,
|
sessionCoroutineScope = sessionCoroutineScope,
|
||||||
dispatcher = sessionDispatcher,
|
roomListFactory = RoomListFactory(
|
||||||
|
innerRoomListService = innerRoomListService,
|
||||||
|
coroutineScope = sessionCoroutineScope,
|
||||||
|
dispatcher = sessionDispatcher,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
override val roomListService: RoomListService
|
override val roomListService: RoomListService
|
||||||
|
|
@ -200,7 +205,7 @@ class RustMatrixClient constructor(
|
||||||
var cachedPairOfRoom = pairOfRoom(roomId)
|
var cachedPairOfRoom = pairOfRoom(roomId)
|
||||||
if (cachedPairOfRoom == null) {
|
if (cachedPairOfRoom == null) {
|
||||||
//... otherwise, lets wait for the SS to load all rooms and check again.
|
//... otherwise, lets wait for the SS to load all rooms and check again.
|
||||||
roomListService.allRooms().awaitLoaded()
|
roomListService.allRooms.awaitLoaded()
|
||||||
cachedPairOfRoom = pairOfRoom(roomId)
|
cachedPairOfRoom = pairOfRoom(roomId)
|
||||||
}
|
}
|
||||||
cachedPairOfRoom?.let { (roomListItem, fullRoom) ->
|
cachedPairOfRoom?.let { (roomListItem, fullRoom) ->
|
||||||
|
|
@ -274,7 +279,7 @@ class RustMatrixClient constructor(
|
||||||
|
|
||||||
// Wait to receive the room back from the sync
|
// Wait to receive the room back from the sync
|
||||||
withTimeout(30_000L) {
|
withTimeout(30_000L) {
|
||||||
roomListService.allRooms().summaries
|
roomListService.allRooms.summaries
|
||||||
.filter { roomSummaries ->
|
.filter { roomSummaries ->
|
||||||
roomSummaries.map { it.identifier() }.contains(roomId.value)
|
roomSummaries.map { it.identifier() }.contains(roomId.value)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,14 +16,10 @@
|
||||||
|
|
||||||
package io.element.android.libraries.matrix.impl.roomlist
|
package io.element.android.libraries.matrix.impl.roomlist
|
||||||
|
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomList
|
import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
|
||||||
|
|
||||||
/**
|
internal sealed interface RoomListDynamicEvents {
|
||||||
* Simple implementation of [RoomList] where state flows are provided through constructor.
|
data object Reset : RoomListDynamicEvents
|
||||||
*/
|
data object LoadMore : RoomListDynamicEvents
|
||||||
class RustRoomList(
|
data class SetFilter(val filter: RoomListEntriesDynamicFilterKind) : RoomListDynamicEvents
|
||||||
override val summaries: StateFlow<List<RoomSummary>>,
|
}
|
||||||
override val loadingState: StateFlow<RoomList.LoadingState>
|
|
||||||
) : RoomList
|
|
||||||
|
|
@ -17,15 +17,20 @@
|
||||||
package io.element.android.libraries.matrix.impl.roomlist
|
package io.element.android.libraries.matrix.impl.roomlist
|
||||||
|
|
||||||
import io.element.android.libraries.core.data.tryOrNull
|
import io.element.android.libraries.core.data.tryOrNull
|
||||||
|
import io.element.android.libraries.matrix.impl.util.cancelAndDestroy
|
||||||
import io.element.android.libraries.matrix.impl.util.mxCallbackFlow
|
import io.element.android.libraries.matrix.impl.util.mxCallbackFlow
|
||||||
import kotlinx.coroutines.channels.Channel
|
import kotlinx.coroutines.channels.Channel
|
||||||
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
import kotlinx.coroutines.channels.trySendBlocking
|
import kotlinx.coroutines.channels.trySendBlocking
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.buffer
|
import kotlinx.coroutines.flow.buffer
|
||||||
|
import kotlinx.coroutines.flow.callbackFlow
|
||||||
import kotlinx.coroutines.flow.catch
|
import kotlinx.coroutines.flow.catch
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind
|
||||||
import org.matrix.rustcomponents.sdk.RoomListEntriesListener
|
import org.matrix.rustcomponents.sdk.RoomListEntriesListener
|
||||||
import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate
|
import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate
|
||||||
import org.matrix.rustcomponents.sdk.RoomListEntry
|
|
||||||
import org.matrix.rustcomponents.sdk.RoomListInterface
|
import org.matrix.rustcomponents.sdk.RoomListInterface
|
||||||
import org.matrix.rustcomponents.sdk.RoomListItem
|
import org.matrix.rustcomponents.sdk.RoomListItem
|
||||||
import org.matrix.rustcomponents.sdk.RoomListLoadingState
|
import org.matrix.rustcomponents.sdk.RoomListLoadingState
|
||||||
|
|
@ -58,25 +63,45 @@ fun RoomListInterface.loadingStateFlow(): Flow<RoomListLoadingState> =
|
||||||
Timber.d(it, "loadingStateFlow() failed")
|
Timber.d(it, "loadingStateFlow() failed")
|
||||||
}.buffer(Channel.UNLIMITED)
|
}.buffer(Channel.UNLIMITED)
|
||||||
|
|
||||||
fun RoomListInterface.entriesFlow(onInitialList: suspend (List<RoomListEntry>) -> Unit): Flow<List<RoomListEntriesUpdate>> =
|
internal fun RoomListInterface.entriesFlow(
|
||||||
mxCallbackFlow {
|
pageSize: Int,
|
||||||
|
numberOfPages: Int,
|
||||||
|
roomListDynamicEvents: Flow<RoomListDynamicEvents>,
|
||||||
|
initialFilterKind: RoomListEntriesDynamicFilterKind
|
||||||
|
): Flow<List<RoomListEntriesUpdate>> =
|
||||||
|
callbackFlow {
|
||||||
val listener = object : RoomListEntriesListener {
|
val listener = object : RoomListEntriesListener {
|
||||||
override fun onUpdate(roomEntriesUpdate: List<RoomListEntriesUpdate>) {
|
override fun onUpdate(roomEntriesUpdate: List<RoomListEntriesUpdate>) {
|
||||||
trySendBlocking(roomEntriesUpdate)
|
trySendBlocking(roomEntriesUpdate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val result = entries(listener)
|
val result = entriesWithDynamicAdapters(pageSize.toUInt(), listener)
|
||||||
try {
|
val controller = result.controller
|
||||||
onInitialList(result.entries)
|
controller.setFilter(initialFilterKind)
|
||||||
} catch (exception: Exception) {
|
roomListDynamicEvents.onEach { controllerEvents ->
|
||||||
Timber.d("entriesFlow() onInitialList failed.")
|
when (controllerEvents) {
|
||||||
|
is RoomListDynamicEvents.SetFilter -> {
|
||||||
|
controller.setFilter(controllerEvents.filter)
|
||||||
|
}
|
||||||
|
is RoomListDynamicEvents.LoadMore -> {
|
||||||
|
repeat(numberOfPages) {
|
||||||
|
controller.addOnePage()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is RoomListDynamicEvents.Reset -> {
|
||||||
|
controller.resetToOnePage()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.launchIn(this)
|
||||||
|
awaitClose {
|
||||||
|
result.entriesStream.cancelAndDestroy()
|
||||||
|
result.destroy()
|
||||||
}
|
}
|
||||||
result.entriesStream
|
|
||||||
}.catch {
|
}.catch {
|
||||||
Timber.d(it, "entriesFlow() failed")
|
Timber.d(it, "entriesFlow() failed")
|
||||||
}.buffer(Channel.UNLIMITED)
|
}.buffer(Channel.UNLIMITED)
|
||||||
|
|
||||||
fun RoomListServiceInterface.stateFlow(): Flow<RoomListServiceState> =
|
internal fun RoomListServiceInterface.stateFlow(): Flow<RoomListServiceState> =
|
||||||
mxCallbackFlow {
|
mxCallbackFlow {
|
||||||
val listener = object : RoomListServiceStateListener {
|
val listener = object : RoomListServiceStateListener {
|
||||||
override fun onUpdate(state: RoomListServiceState) {
|
override fun onUpdate(state: RoomListServiceState) {
|
||||||
|
|
@ -88,7 +113,7 @@ fun RoomListServiceInterface.stateFlow(): Flow<RoomListServiceState> =
|
||||||
}
|
}
|
||||||
}.buffer(Channel.UNLIMITED)
|
}.buffer(Channel.UNLIMITED)
|
||||||
|
|
||||||
fun RoomListServiceInterface.syncIndicator(): Flow<RoomListServiceSyncIndicator> =
|
internal fun RoomListServiceInterface.syncIndicator(): Flow<RoomListServiceSyncIndicator> =
|
||||||
mxCallbackFlow {
|
mxCallbackFlow {
|
||||||
val listener = object : RoomListServiceSyncIndicatorListener {
|
val listener = object : RoomListServiceSyncIndicatorListener {
|
||||||
override fun onUpdate(syncIndicator: RoomListServiceSyncIndicator) {
|
override fun onUpdate(syncIndicator: RoomListServiceSyncIndicator) {
|
||||||
|
|
@ -104,7 +129,7 @@ fun RoomListServiceInterface.syncIndicator(): Flow<RoomListServiceSyncIndicator>
|
||||||
}
|
}
|
||||||
}.buffer(Channel.UNLIMITED)
|
}.buffer(Channel.UNLIMITED)
|
||||||
|
|
||||||
fun RoomListServiceInterface.roomOrNull(roomId: String): RoomListItem? {
|
internal fun RoomListServiceInterface.roomOrNull(roomId: String): RoomListItem? {
|
||||||
return try {
|
return try {
|
||||||
room(roomId)
|
room(roomId)
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,151 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 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.libraries.matrix.impl.roomlist
|
||||||
|
|
||||||
|
import io.element.android.libraries.matrix.api.roomlist.DynamicRoomList
|
||||||
|
import io.element.android.libraries.matrix.api.roomlist.RoomList
|
||||||
|
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind
|
||||||
|
import org.matrix.rustcomponents.sdk.RoomListLoadingState
|
||||||
|
import org.matrix.rustcomponents.sdk.RoomList as InnerRoomList
|
||||||
|
import org.matrix.rustcomponents.sdk.RoomListService as InnerRoomListService
|
||||||
|
|
||||||
|
internal class RoomListFactory(
|
||||||
|
private val innerRoomListService: InnerRoomListService,
|
||||||
|
private val coroutineScope: CoroutineScope,
|
||||||
|
private val dispatcher: CoroutineDispatcher,
|
||||||
|
private val roomSummaryDetailsFactory: RoomSummaryDetailsFactory = RoomSummaryDetailsFactory(),
|
||||||
|
) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a room list that will load all rooms in a single page.
|
||||||
|
* It mimics the usage of the old api.
|
||||||
|
*/
|
||||||
|
fun createRoomList(
|
||||||
|
innerProvider: suspend () -> InnerRoomList
|
||||||
|
): RoomList {
|
||||||
|
return createRustRoomList(
|
||||||
|
pageSize = Int.MAX_VALUE,
|
||||||
|
numberOfPages = 1,
|
||||||
|
initialFilterKind = RoomListEntriesDynamicFilterKind.All,
|
||||||
|
innerRoomListProvider = innerProvider
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a room list that can be used to load more rooms and filter them dynamically.
|
||||||
|
*/
|
||||||
|
fun createDynamicRoomList(
|
||||||
|
pageSize: Int = DynamicRoomList.DEFAULT_PAGE_SIZE,
|
||||||
|
pagesToLoad: Int = DynamicRoomList.DEFAULT_PAGES_TO_LOAD,
|
||||||
|
initialFilter: DynamicRoomList.Filter = DynamicRoomList.Filter.None,
|
||||||
|
innerProvider: suspend () -> InnerRoomList
|
||||||
|
): DynamicRoomList {
|
||||||
|
return createRustRoomList(
|
||||||
|
pageSize = pageSize,
|
||||||
|
numberOfPages = pagesToLoad,
|
||||||
|
initialFilterKind = initialFilter.toRustFilter(),
|
||||||
|
innerRoomListProvider = innerProvider
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createRustRoomList(
|
||||||
|
pageSize: Int,
|
||||||
|
numberOfPages: Int,
|
||||||
|
initialFilterKind: RoomListEntriesDynamicFilterKind,
|
||||||
|
innerRoomListProvider: suspend () -> InnerRoomList
|
||||||
|
): RustDynamicRoomList {
|
||||||
|
val loadingStateFlow: MutableStateFlow<RoomList.LoadingState> = MutableStateFlow(RoomList.LoadingState.NotLoaded)
|
||||||
|
val summariesFlow = MutableStateFlow<List<RoomSummary>>(emptyList())
|
||||||
|
val processor = RoomSummaryListProcessor(summariesFlow, innerRoomListService, dispatcher, roomSummaryDetailsFactory)
|
||||||
|
val dynamicEvents = MutableSharedFlow<RoomListDynamicEvents>()
|
||||||
|
|
||||||
|
var innerRoomList: InnerRoomList? = null
|
||||||
|
coroutineScope.launch(dispatcher) {
|
||||||
|
innerRoomList = innerRoomListProvider()
|
||||||
|
innerRoomList?.let { innerRoomList ->
|
||||||
|
innerRoomList.entriesFlow(
|
||||||
|
pageSize = pageSize,
|
||||||
|
numberOfPages = numberOfPages,
|
||||||
|
initialFilterKind = initialFilterKind,
|
||||||
|
roomListDynamicEvents = dynamicEvents
|
||||||
|
).onEach { update ->
|
||||||
|
processor.postUpdate(update)
|
||||||
|
}.launchIn(this)
|
||||||
|
|
||||||
|
innerRoomList.loadingStateFlow()
|
||||||
|
.map { it.toLoadingState() }
|
||||||
|
.onEach {
|
||||||
|
loadingStateFlow.value = it
|
||||||
|
}
|
||||||
|
.launchIn(this)
|
||||||
|
}
|
||||||
|
}.invokeOnCompletion {
|
||||||
|
innerRoomList?.destroy()
|
||||||
|
}
|
||||||
|
return RustDynamicRoomList(summariesFlow, loadingStateFlow, dynamicEvents, processor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RustDynamicRoomList(
|
||||||
|
override val summaries: MutableStateFlow<List<RoomSummary>>,
|
||||||
|
override val loadingState: MutableStateFlow<RoomList.LoadingState>,
|
||||||
|
private val dynamicEvents: MutableSharedFlow<RoomListDynamicEvents>,
|
||||||
|
private val processor: RoomSummaryListProcessor,
|
||||||
|
) : DynamicRoomList {
|
||||||
|
|
||||||
|
override suspend fun rebuildSummaries() {
|
||||||
|
processor.rebuildRoomSummaries()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun updateFilter(filter: DynamicRoomList.Filter) {
|
||||||
|
val filterEvent = RoomListDynamicEvents.SetFilter(filter.toRustFilter())
|
||||||
|
dynamicEvents.emit(filterEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun loadMore() {
|
||||||
|
dynamicEvents.emit(RoomListDynamicEvents.LoadMore)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun reset() {
|
||||||
|
dynamicEvents.emit(RoomListDynamicEvents.Reset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun RoomListLoadingState.toLoadingState(): RoomList.LoadingState {
|
||||||
|
return when (this) {
|
||||||
|
is RoomListLoadingState.Loaded -> RoomList.LoadingState.Loaded(maximumNumberOfRooms?.toInt() ?: 0)
|
||||||
|
RoomListLoadingState.NotLoaded -> RoomList.LoadingState.NotLoaded
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun DynamicRoomList.Filter.toRustFilter(): RoomListEntriesDynamicFilterKind {
|
||||||
|
return when (this) {
|
||||||
|
DynamicRoomList.Filter.All -> RoomListEntriesDynamicFilterKind.All
|
||||||
|
is DynamicRoomList.Filter.NormalizedMatchRoomName -> RoomListEntriesDynamicFilterKind.NormalizedMatchRoomName(this.pattern)
|
||||||
|
DynamicRoomList.Filter.None -> RoomListEntriesDynamicFilterKind.None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -16,9 +16,7 @@
|
||||||
|
|
||||||
package io.element.android.libraries.matrix.impl.roomlist
|
package io.element.android.libraries.matrix.impl.roomlist
|
||||||
|
|
||||||
import io.element.android.libraries.core.coroutine.parallelMap
|
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||||
import kotlinx.coroutines.CompletableDeferred
|
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
|
@ -39,21 +37,9 @@ class RoomSummaryListProcessor(
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val roomSummariesByIdentifier = HashMap<String, RoomSummary>()
|
private val roomSummariesByIdentifier = HashMap<String, RoomSummary>()
|
||||||
private val initLatch = CompletableDeferred<Unit>()
|
|
||||||
private val mutex = Mutex()
|
private val mutex = Mutex()
|
||||||
|
|
||||||
suspend fun postEntries(entries: List<RoomListEntry>) {
|
|
||||||
updateRoomSummaries {
|
|
||||||
Timber.v("Update rooms from postEntries (with ${entries.size} items) on ${Thread.currentThread()}")
|
|
||||||
val roomSummaries = entries.parallelMap(::buildSummaryForRoomListEntry)
|
|
||||||
addAll(roomSummaries)
|
|
||||||
}
|
|
||||||
initLatch.complete(Unit)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun postUpdate(updates: List<RoomListEntriesUpdate>) {
|
suspend fun postUpdate(updates: List<RoomListEntriesUpdate>) {
|
||||||
// Makes sure to process first entries before update.
|
|
||||||
initLatch.await()
|
|
||||||
updateRoomSummaries {
|
updateRoomSummaries {
|
||||||
Timber.v("Update rooms from postUpdates (with ${updates.size} items) on ${Thread.currentThread()}")
|
Timber.v("Update rooms from postUpdates (with ${updates.size} items) on ${Thread.currentThread()}")
|
||||||
updates.forEach { update ->
|
updates.forEach { update ->
|
||||||
|
|
@ -65,7 +51,7 @@ class RoomSummaryListProcessor(
|
||||||
suspend fun rebuildRoomSummaries() {
|
suspend fun rebuildRoomSummaries() {
|
||||||
updateRoomSummaries {
|
updateRoomSummaries {
|
||||||
forEachIndexed { i, summary ->
|
forEachIndexed { i, summary ->
|
||||||
this[i] = when(summary) {
|
this[i] = when (summary) {
|
||||||
is RoomSummary.Empty -> summary
|
is RoomSummary.Empty -> summary
|
||||||
is RoomSummary.Filled -> buildAndCacheRoomSummaryForIdentifier(summary.identifier())
|
is RoomSummary.Filled -> buildAndCacheRoomSummaryForIdentifier(summary.identifier())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,79 +18,34 @@ package io.element.android.libraries.matrix.impl.roomlist
|
||||||
|
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomList
|
import io.element.android.libraries.matrix.api.roomlist.RoomList
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.first
|
|
||||||
import kotlinx.coroutines.flow.launchIn
|
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate
|
|
||||||
import org.matrix.rustcomponents.sdk.RoomListException
|
import org.matrix.rustcomponents.sdk.RoomListException
|
||||||
import org.matrix.rustcomponents.sdk.RoomListInput
|
import org.matrix.rustcomponents.sdk.RoomListInput
|
||||||
import org.matrix.rustcomponents.sdk.RoomListLoadingState
|
|
||||||
import org.matrix.rustcomponents.sdk.RoomListRange
|
import org.matrix.rustcomponents.sdk.RoomListRange
|
||||||
import org.matrix.rustcomponents.sdk.RoomListServiceState
|
import org.matrix.rustcomponents.sdk.RoomListServiceState
|
||||||
import org.matrix.rustcomponents.sdk.RoomListServiceSyncIndicator
|
import org.matrix.rustcomponents.sdk.RoomListServiceSyncIndicator
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import org.matrix.rustcomponents.sdk.RoomListService as InnerRustRoomListService
|
import org.matrix.rustcomponents.sdk.RoomListService as InnerRustRoomListService
|
||||||
|
|
||||||
class RustRoomListService(
|
internal class RustRoomListService(
|
||||||
private val innerRoomListService: InnerRustRoomListService,
|
private val innerRoomListService: InnerRustRoomListService,
|
||||||
private val sessionCoroutineScope: CoroutineScope,
|
private val sessionCoroutineScope: CoroutineScope,
|
||||||
private val dispatcher: CoroutineDispatcher,
|
private val roomListFactory: RoomListFactory,
|
||||||
roomSummaryDetailsFactory: RoomSummaryDetailsFactory = RoomSummaryDetailsFactory(),
|
|
||||||
) : RoomListService {
|
) : RoomListService {
|
||||||
|
|
||||||
private val allRooms = MutableStateFlow<List<RoomSummary>>(emptyList())
|
override val allRooms: RoomList = roomListFactory.createRoomList {
|
||||||
private val inviteRooms = MutableStateFlow<List<RoomSummary>>(emptyList())
|
innerRoomListService.allRooms()
|
||||||
|
|
||||||
private val allRoomsLoadingState: MutableStateFlow<RoomList.LoadingState> = MutableStateFlow(RoomList.LoadingState.NotLoaded)
|
|
||||||
private val allRoomsListProcessor = RoomSummaryListProcessor(allRooms, innerRoomListService, dispatcher, roomSummaryDetailsFactory)
|
|
||||||
private val invitesLoadingState: MutableStateFlow<RoomList.LoadingState> = MutableStateFlow(RoomList.LoadingState.NotLoaded)
|
|
||||||
private val inviteRoomsListProcessor = RoomSummaryListProcessor(inviteRooms, innerRoomListService, dispatcher, roomSummaryDetailsFactory)
|
|
||||||
|
|
||||||
init {
|
|
||||||
sessionCoroutineScope.launch(dispatcher) {
|
|
||||||
val allRooms = innerRoomListService.allRooms()
|
|
||||||
allRooms
|
|
||||||
.observeEntriesWithProcessor(allRoomsListProcessor)
|
|
||||||
.launchIn(this)
|
|
||||||
allRooms
|
|
||||||
.observeLoadingState(allRoomsLoadingState)
|
|
||||||
.launchIn(this)
|
|
||||||
|
|
||||||
|
|
||||||
launch {
|
|
||||||
// Wait until running, as invites is only available after that
|
|
||||||
innerRoomListService.stateFlow().first {
|
|
||||||
it == RoomListServiceState.RUNNING
|
|
||||||
}
|
|
||||||
val invites = innerRoomListService.invites()
|
|
||||||
invites
|
|
||||||
.observeEntriesWithProcessor(inviteRoomsListProcessor)
|
|
||||||
.launchIn(this)
|
|
||||||
invites
|
|
||||||
.observeLoadingState(invitesLoadingState)
|
|
||||||
.launchIn(this)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun allRooms(): RoomList {
|
override val invites: RoomList = roomListFactory.createRoomList {
|
||||||
return RustRoomList(allRooms, allRoomsLoadingState)
|
innerRoomListService.invites()
|
||||||
}
|
|
||||||
|
|
||||||
override fun invites(): RoomList {
|
|
||||||
return RustRoomList(inviteRooms, invitesLoadingState)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateAllRoomsVisibleRange(range: IntRange) {
|
override fun updateAllRoomsVisibleRange(range: IntRange) {
|
||||||
|
|
@ -107,12 +62,6 @@ class RustRoomListService(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun rebuildRoomSummaries() {
|
|
||||||
sessionCoroutineScope.launch {
|
|
||||||
allRoomsListProcessor.rebuildRoomSummaries()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override val syncIndicator: StateFlow<RoomListService.SyncIndicator> =
|
override val syncIndicator: StateFlow<RoomListService.SyncIndicator> =
|
||||||
innerRoomListService.syncIndicator()
|
innerRoomListService.syncIndicator()
|
||||||
.map { it.toSyncIndicator() }
|
.map { it.toSyncIndicator() }
|
||||||
|
|
@ -132,13 +81,6 @@ class RustRoomListService(
|
||||||
.stateIn(sessionCoroutineScope, SharingStarted.Eagerly, RoomListService.State.Idle)
|
.stateIn(sessionCoroutineScope, SharingStarted.Eagerly, RoomListService.State.Idle)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun RoomListLoadingState.toLoadingState(): RoomList.LoadingState {
|
|
||||||
return when (this) {
|
|
||||||
is RoomListLoadingState.Loaded -> RoomList.LoadingState.Loaded(maximumNumberOfRooms?.toInt() ?: 0)
|
|
||||||
RoomListLoadingState.NotLoaded -> RoomList.LoadingState.NotLoaded
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun RoomListServiceState.toRoomListState(): RoomListService.State {
|
private fun RoomListServiceState.toRoomListState(): RoomListService.State {
|
||||||
return when (this) {
|
return when (this) {
|
||||||
RoomListServiceState.INITIAL,
|
RoomListServiceState.INITIAL,
|
||||||
|
|
@ -156,20 +98,3 @@ private fun RoomListServiceSyncIndicator.toSyncIndicator(): RoomListService.Sync
|
||||||
RoomListServiceSyncIndicator.HIDE -> RoomListService.SyncIndicator.Hide
|
RoomListServiceSyncIndicator.HIDE -> RoomListService.SyncIndicator.Hide
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun org.matrix.rustcomponents.sdk.RoomList.observeEntriesWithProcessor(processor: RoomSummaryListProcessor): Flow<List<RoomListEntriesUpdate>> {
|
|
||||||
return entriesFlow { roomListEntries ->
|
|
||||||
processor.postEntries(roomListEntries)
|
|
||||||
}.onEach { update ->
|
|
||||||
processor.postUpdate(update)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun org.matrix.rustcomponents.sdk.RoomList.observeLoadingState(stateFlow: MutableStateFlow<RoomList.LoadingState>): Flow<RoomList.LoadingState> {
|
|
||||||
return loadingStateFlow()
|
|
||||||
.map { it.toLoadingState() }
|
|
||||||
.onEach {
|
|
||||||
stateFlow.value = it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,12 +22,10 @@ import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||||
import io.element.android.libraries.matrix.test.A_ROOM_ID_2
|
import io.element.android.libraries.matrix.test.A_ROOM_ID_2
|
||||||
import io.element.android.libraries.matrix.test.room.aRoomSummaryFilled
|
import io.element.android.libraries.matrix.test.room.aRoomSummaryFilled
|
||||||
import kotlinx.coroutines.CancellationException
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.test.StandardTestDispatcher
|
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||||
import kotlinx.coroutines.test.TestScope
|
import kotlinx.coroutines.test.TestScope
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import kotlinx.coroutines.withTimeout
|
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.matrix.rustcomponents.sdk.RoomList
|
import org.matrix.rustcomponents.sdk.RoomList
|
||||||
import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate
|
import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate
|
||||||
|
|
@ -38,7 +36,6 @@ import org.matrix.rustcomponents.sdk.RoomListServiceInterface
|
||||||
import org.matrix.rustcomponents.sdk.RoomListServiceStateListener
|
import org.matrix.rustcomponents.sdk.RoomListServiceStateListener
|
||||||
import org.matrix.rustcomponents.sdk.RoomListServiceSyncIndicatorListener
|
import org.matrix.rustcomponents.sdk.RoomListServiceSyncIndicatorListener
|
||||||
import org.matrix.rustcomponents.sdk.TaskHandle
|
import org.matrix.rustcomponents.sdk.TaskHandle
|
||||||
import kotlin.time.Duration.Companion.milliseconds
|
|
||||||
|
|
||||||
// NOTE: this class is using a fake implementation of a Rust SDK interface which returns actual Rust objects with pointers.
|
// NOTE: this class is using a fake implementation of a Rust SDK interface which returns actual Rust objects with pointers.
|
||||||
// Since we don't access the data in those objects, this is fine for our tests, but that's as far as we can test this class.
|
// Since we don't access the data in those objects, this is fine for our tests, but that's as far as we can test this class.
|
||||||
|
|
@ -46,38 +43,11 @@ class RoomSummaryListProcessorTests {
|
||||||
|
|
||||||
private val summaries = MutableStateFlow<List<RoomSummary>>(emptyList())
|
private val summaries = MutableStateFlow<List<RoomSummary>>(emptyList())
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `postUpdates can't start until postEntries is done`() = runTest {
|
|
||||||
val processor = createProcessor()
|
|
||||||
val update = listOf(RoomListEntriesUpdate.Reset(emptyList()))
|
|
||||||
|
|
||||||
val timeoutError = runCatching {
|
|
||||||
withTimeout(10.milliseconds) { processor.postUpdate(update) }
|
|
||||||
}.exceptionOrNull()
|
|
||||||
assertThat(timeoutError).isInstanceOf(CancellationException::class.java)
|
|
||||||
|
|
||||||
processor.postEntries(listOf(RoomListEntry.Empty))
|
|
||||||
processor.postUpdate(update)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `postEntries adds all new entries with no diffing`() = runTest {
|
|
||||||
summaries.value = listOf(aRoomSummaryFilled())
|
|
||||||
val processor = createProcessor()
|
|
||||||
|
|
||||||
processor.postEntries(listOf(RoomListEntry.Empty, RoomListEntry.Empty, RoomListEntry.Empty))
|
|
||||||
|
|
||||||
assertThat(summaries.value.count()).isEqualTo(4)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Append adds new entries at the end of the list`() = runTest {
|
fun `Append adds new entries at the end of the list`() = runTest {
|
||||||
summaries.value = listOf(aRoomSummaryFilled())
|
summaries.value = listOf(aRoomSummaryFilled())
|
||||||
val processor = createProcessor()
|
val processor = createProcessor()
|
||||||
|
|
||||||
// Start processing updates
|
|
||||||
processor.postEntries(listOf())
|
|
||||||
// Process actual update
|
|
||||||
processor.postUpdate(listOf(RoomListEntriesUpdate.Append(listOf(RoomListEntry.Empty, RoomListEntry.Empty, RoomListEntry.Empty))))
|
processor.postUpdate(listOf(RoomListEntriesUpdate.Append(listOf(RoomListEntry.Empty, RoomListEntry.Empty, RoomListEntry.Empty))))
|
||||||
|
|
||||||
assertThat(summaries.value.count()).isEqualTo(4)
|
assertThat(summaries.value.count()).isEqualTo(4)
|
||||||
|
|
@ -88,10 +58,6 @@ class RoomSummaryListProcessorTests {
|
||||||
fun `PushBack adds a new entry at the end of the list`() = runTest {
|
fun `PushBack adds a new entry at the end of the list`() = runTest {
|
||||||
summaries.value = listOf(aRoomSummaryFilled())
|
summaries.value = listOf(aRoomSummaryFilled())
|
||||||
val processor = createProcessor()
|
val processor = createProcessor()
|
||||||
|
|
||||||
// Start processing updates
|
|
||||||
processor.postEntries(listOf())
|
|
||||||
// Process actual update
|
|
||||||
processor.postUpdate(listOf(RoomListEntriesUpdate.PushBack(RoomListEntry.Empty)))
|
processor.postUpdate(listOf(RoomListEntriesUpdate.PushBack(RoomListEntry.Empty)))
|
||||||
|
|
||||||
assertThat(summaries.value.count()).isEqualTo(2)
|
assertThat(summaries.value.count()).isEqualTo(2)
|
||||||
|
|
@ -102,10 +68,6 @@ class RoomSummaryListProcessorTests {
|
||||||
fun `PushFront inserts a new entry at the start of the list`() = runTest {
|
fun `PushFront inserts a new entry at the start of the list`() = runTest {
|
||||||
summaries.value = listOf(aRoomSummaryFilled())
|
summaries.value = listOf(aRoomSummaryFilled())
|
||||||
val processor = createProcessor()
|
val processor = createProcessor()
|
||||||
|
|
||||||
// Start processing updates
|
|
||||||
processor.postEntries(listOf())
|
|
||||||
// Process actual update
|
|
||||||
processor.postUpdate(listOf(RoomListEntriesUpdate.PushFront(RoomListEntry.Empty)))
|
processor.postUpdate(listOf(RoomListEntriesUpdate.PushFront(RoomListEntry.Empty)))
|
||||||
|
|
||||||
assertThat(summaries.value.count()).isEqualTo(2)
|
assertThat(summaries.value.count()).isEqualTo(2)
|
||||||
|
|
@ -118,9 +80,6 @@ class RoomSummaryListProcessorTests {
|
||||||
val processor = createProcessor()
|
val processor = createProcessor()
|
||||||
val index = 0
|
val index = 0
|
||||||
|
|
||||||
// Start processing updates
|
|
||||||
processor.postEntries(listOf())
|
|
||||||
// Process actual update
|
|
||||||
processor.postUpdate(listOf(RoomListEntriesUpdate.Set(index.toUInt(), RoomListEntry.Empty)))
|
processor.postUpdate(listOf(RoomListEntriesUpdate.Set(index.toUInt(), RoomListEntry.Empty)))
|
||||||
|
|
||||||
assertThat(summaries.value.count()).isEqualTo(1)
|
assertThat(summaries.value.count()).isEqualTo(1)
|
||||||
|
|
@ -133,9 +92,6 @@ class RoomSummaryListProcessorTests {
|
||||||
val processor = createProcessor()
|
val processor = createProcessor()
|
||||||
val index = 0
|
val index = 0
|
||||||
|
|
||||||
// Start processing updates
|
|
||||||
processor.postEntries(listOf())
|
|
||||||
// Process actual update
|
|
||||||
processor.postUpdate(listOf(RoomListEntriesUpdate.Insert(index.toUInt(), RoomListEntry.Empty)))
|
processor.postUpdate(listOf(RoomListEntriesUpdate.Insert(index.toUInt(), RoomListEntry.Empty)))
|
||||||
|
|
||||||
assertThat(summaries.value.count()).isEqualTo(2)
|
assertThat(summaries.value.count()).isEqualTo(2)
|
||||||
|
|
@ -148,9 +104,6 @@ class RoomSummaryListProcessorTests {
|
||||||
val processor = createProcessor()
|
val processor = createProcessor()
|
||||||
val index = 0
|
val index = 0
|
||||||
|
|
||||||
// Start processing updates
|
|
||||||
processor.postEntries(listOf())
|
|
||||||
// Process actual update
|
|
||||||
processor.postUpdate(listOf(RoomListEntriesUpdate.Remove(index.toUInt())))
|
processor.postUpdate(listOf(RoomListEntriesUpdate.Remove(index.toUInt())))
|
||||||
|
|
||||||
assertThat(summaries.value.count()).isEqualTo(1)
|
assertThat(summaries.value.count()).isEqualTo(1)
|
||||||
|
|
@ -163,9 +116,6 @@ class RoomSummaryListProcessorTests {
|
||||||
val processor = createProcessor()
|
val processor = createProcessor()
|
||||||
val index = 0
|
val index = 0
|
||||||
|
|
||||||
// Start processing updates
|
|
||||||
processor.postEntries(listOf())
|
|
||||||
// Process actual update
|
|
||||||
processor.postUpdate(listOf(RoomListEntriesUpdate.PopBack))
|
processor.postUpdate(listOf(RoomListEntriesUpdate.PopBack))
|
||||||
|
|
||||||
assertThat(summaries.value.count()).isEqualTo(1)
|
assertThat(summaries.value.count()).isEqualTo(1)
|
||||||
|
|
@ -178,9 +128,6 @@ class RoomSummaryListProcessorTests {
|
||||||
val processor = createProcessor()
|
val processor = createProcessor()
|
||||||
val index = 0
|
val index = 0
|
||||||
|
|
||||||
// Start processing updates
|
|
||||||
processor.postEntries(listOf())
|
|
||||||
// Process actual update
|
|
||||||
processor.postUpdate(listOf(RoomListEntriesUpdate.PopFront))
|
processor.postUpdate(listOf(RoomListEntriesUpdate.PopFront))
|
||||||
|
|
||||||
assertThat(summaries.value.count()).isEqualTo(1)
|
assertThat(summaries.value.count()).isEqualTo(1)
|
||||||
|
|
@ -192,9 +139,6 @@ class RoomSummaryListProcessorTests {
|
||||||
summaries.value = listOf(aRoomSummaryFilled(roomId = A_ROOM_ID), aRoomSummaryFilled(A_ROOM_ID_2))
|
summaries.value = listOf(aRoomSummaryFilled(roomId = A_ROOM_ID), aRoomSummaryFilled(A_ROOM_ID_2))
|
||||||
val processor = createProcessor()
|
val processor = createProcessor()
|
||||||
|
|
||||||
// Start processing updates
|
|
||||||
processor.postEntries(listOf())
|
|
||||||
// Process actual update
|
|
||||||
processor.postUpdate(listOf(RoomListEntriesUpdate.Clear))
|
processor.postUpdate(listOf(RoomListEntriesUpdate.Clear))
|
||||||
|
|
||||||
assertThat(summaries.value).isEmpty()
|
assertThat(summaries.value).isEmpty()
|
||||||
|
|
@ -206,9 +150,6 @@ class RoomSummaryListProcessorTests {
|
||||||
val processor = createProcessor()
|
val processor = createProcessor()
|
||||||
val index = 0
|
val index = 0
|
||||||
|
|
||||||
// Start processing updates
|
|
||||||
processor.postEntries(listOf())
|
|
||||||
// Process actual update
|
|
||||||
processor.postUpdate(listOf(RoomListEntriesUpdate.Truncate(1u)))
|
processor.postUpdate(listOf(RoomListEntriesUpdate.Truncate(1u)))
|
||||||
|
|
||||||
assertThat(summaries.value.count()).isEqualTo(1)
|
assertThat(summaries.value.count()).isEqualTo(1)
|
||||||
|
|
|
||||||
|
|
@ -54,28 +54,20 @@ class FakeRoomListService : RoomListService {
|
||||||
var latestSlidingSyncRange: IntRange? = null
|
var latestSlidingSyncRange: IntRange? = null
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
override val allRooms: RoomList = SimplePagedRoomList(
|
||||||
|
allRoomSummariesFlow,
|
||||||
|
allRoomsLoadingStateFlow,
|
||||||
|
)
|
||||||
|
|
||||||
|
override val invites: RoomList = SimplePagedRoomList(
|
||||||
|
inviteRoomSummariesFlow,
|
||||||
|
inviteRoomsLoadingStateFlow,
|
||||||
|
)
|
||||||
|
|
||||||
override fun updateAllRoomsVisibleRange(range: IntRange) {
|
override fun updateAllRoomsVisibleRange(range: IntRange) {
|
||||||
latestSlidingSyncRange = range
|
latestSlidingSyncRange = range
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun rebuildRoomSummaries() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun allRooms(): RoomList {
|
|
||||||
return SimpleRoomList(
|
|
||||||
summaries = allRoomSummariesFlow,
|
|
||||||
loadingState = allRoomsLoadingStateFlow
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun invites(): RoomList {
|
|
||||||
return SimpleRoomList(
|
|
||||||
summaries = inviteRoomSummariesFlow,
|
|
||||||
loadingState = inviteRoomsLoadingStateFlow
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override val state: StateFlow<RoomListService.State> = roomListStateFlow
|
override val state: StateFlow<RoomListService.State> = roomListStateFlow
|
||||||
|
|
||||||
override val syncIndicator: StateFlow<RoomListService.SyncIndicator> = syncIndicatorStateFlow
|
override val syncIndicator: StateFlow<RoomListService.SyncIndicator> = syncIndicatorStateFlow
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,29 @@
|
||||||
|
|
||||||
package io.element.android.libraries.matrix.test.roomlist
|
package io.element.android.libraries.matrix.test.roomlist
|
||||||
|
|
||||||
|
import io.element.android.libraries.matrix.api.roomlist.DynamicRoomList
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomList
|
import io.element.android.libraries.matrix.api.roomlist.RoomList
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
||||||
data class SimpleRoomList(
|
data class SimplePagedRoomList(
|
||||||
override val summaries: StateFlow<List<RoomSummary>>,
|
override val summaries: StateFlow<List<RoomSummary>>,
|
||||||
override val loadingState: StateFlow<RoomList.LoadingState>
|
override val loadingState: StateFlow<RoomList.LoadingState>
|
||||||
) : RoomList
|
) : DynamicRoomList {
|
||||||
|
|
||||||
|
override suspend fun loadMore() {
|
||||||
|
//No-op
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun reset() {
|
||||||
|
//No-op
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun updateFilter(filter: DynamicRoomList.Filter) {
|
||||||
|
//No-op
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun rebuildSummaries() {
|
||||||
|
//No-op
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue