RoomList: introduce incremental loading to improve performances.

This commit is contained in:
ganfra 2023-11-29 11:53:15 +01:00
parent 656fcbd5e4
commit fda114d648
7 changed files with 99 additions and 56 deletions

View file

@ -65,7 +65,6 @@ fun RoomListInterface.loadingStateFlow(): Flow<RoomListLoadingState> =
internal fun RoomListInterface.entriesFlow(
pageSize: Int,
numberOfPages: Int,
roomListDynamicEvents: Flow<RoomListDynamicEvents>,
initialFilterKind: RoomListEntriesDynamicFilterKind
): Flow<List<RoomListEntriesUpdate>> =
@ -84,9 +83,7 @@ internal fun RoomListInterface.entriesFlow(
controller.setFilter(controllerEvents.filter)
}
is RoomListDynamicEvents.LoadMore -> {
repeat(numberOfPages) {
controller.addOnePage()
}
controller.addOnePage()
}
is RoomListDynamicEvents.Reset -> {
controller.resetToOnePage()

View file

@ -23,6 +23,7 @@ import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.getAndUpdate
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
@ -39,57 +40,28 @@ internal class RoomListFactory(
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.AllNonLeft,
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,
fun createRoomList(
pageSize: Int,
initialFilter: DynamicRoomList.Filter = DynamicRoomList.Filter.All,
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>()
// Makes sure we don't miss any events
val dynamicEvents = MutableSharedFlow<RoomListDynamicEvents>(replay = 100)
val currentFilter = MutableStateFlow(initialFilter)
val loadedPages = MutableStateFlow(1)
var innerRoomList: InnerRoomList? = null
coroutineScope.launch(dispatcher) {
innerRoomList = innerRoomListProvider()
innerRoomList = innerProvider()
innerRoomList?.let { innerRoomList ->
innerRoomList.entriesFlow(
pageSize = pageSize,
numberOfPages = numberOfPages,
initialFilterKind = initialFilterKind,
initialFilterKind = initialFilter.toRustFilter(),
roomListDynamicEvents = dynamicEvents
).onEach { update ->
processor.postUpdate(update)
@ -105,15 +77,26 @@ internal class RoomListFactory(
}.invokeOnCompletion {
innerRoomList?.destroy()
}
return RustDynamicRoomList(summariesFlow, loadingStateFlow, dynamicEvents, processor)
return RustDynamicRoomList(
summaries = summariesFlow,
loadingState = loadingStateFlow,
currentFilter = currentFilter,
loadedPages = loadedPages,
dynamicEvents = dynamicEvents,
processor = processor,
pageSize = pageSize,
)
}
}
private class RustDynamicRoomList(
override val summaries: MutableStateFlow<List<RoomSummary>>,
override val loadingState: MutableStateFlow<RoomList.LoadingState>,
override val currentFilter: MutableStateFlow<DynamicRoomList.Filter>,
override val loadedPages: MutableStateFlow<Int>,
private val dynamicEvents: MutableSharedFlow<RoomListDynamicEvents>,
private val processor: RoomSummaryListProcessor,
override val pageSize: Int,
) : DynamicRoomList {
override suspend fun rebuildSummaries() {
@ -121,16 +104,19 @@ private class RustDynamicRoomList(
}
override suspend fun updateFilter(filter: DynamicRoomList.Filter) {
currentFilter.emit(filter)
val filterEvent = RoomListDynamicEvents.SetFilter(filter.toRustFilter())
dynamicEvents.emit(filterEvent)
}
override suspend fun loadMore() {
dynamicEvents.emit(RoomListDynamicEvents.LoadMore)
loadedPages.getAndUpdate { it + 1 }
}
override suspend fun reset() {
dynamicEvents.emit(RoomListDynamicEvents.Reset)
loadedPages.emit(1)
}
}
@ -146,6 +132,7 @@ private fun DynamicRoomList.Filter.toRustFilter(): RoomListEntriesDynamicFilterK
DynamicRoomList.Filter.All -> RoomListEntriesDynamicFilterKind.All
is DynamicRoomList.Filter.NormalizedMatchRoomName -> RoomListEntriesDynamicFilterKind.NormalizedMatchRoomName(this.pattern)
DynamicRoomList.Filter.None -> RoomListEntriesDynamicFilterKind.None
DynamicRoomList.Filter.AllNonLeft -> RoomListEntriesDynamicFilterKind.AllNonLeft
}
}

View file

@ -16,8 +16,10 @@
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.RoomListService
import io.element.android.libraries.matrix.api.roomlist.loadAllIncrementally
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
@ -34,20 +36,31 @@ import org.matrix.rustcomponents.sdk.RoomListServiceSyncIndicator
import timber.log.Timber
import org.matrix.rustcomponents.sdk.RoomListService as InnerRustRoomListService
private const val DEFAULT_PAGE_SIZE = 20
internal class RustRoomListService(
private val innerRoomListService: InnerRustRoomListService,
private val sessionCoroutineScope: CoroutineScope,
roomListFactory: RoomListFactory,
) : RoomListService {
override val allRooms: RoomList = roomListFactory.createRoomList {
override val allRooms: DynamicRoomList = roomListFactory.createRoomList(
pageSize = DEFAULT_PAGE_SIZE,
initialFilter = DynamicRoomList.Filter.AllNonLeft,
) {
innerRoomListService.allRooms()
}
override val invites: RoomList = roomListFactory.createRoomList {
override val invites: RoomList = roomListFactory.createRoomList(
pageSize = Int.MAX_VALUE,
) {
innerRoomListService.invites()
}
init {
allRooms.loadAllIncrementally(sessionCoroutineScope)
}
override fun updateAllRoomsVisibleRange(range: IntRange) {
Timber.v("setVisibleRange=$range")
sessionCoroutineScope.launch {