RoomList: more rework on RoomSummaryDataSource

This commit is contained in:
ganfra
2023-06-26 18:02:53 +02:00
parent e392f15872
commit e5c86675a4
11 changed files with 152 additions and 98 deletions

View File

@@ -179,7 +179,7 @@ class LoggedInFlowNode @AssistedInject constructor(
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
combine(
syncService.syncState.debounce(100),
syncService.syncState,
networkMonitor.connectivity
) { syncState, networkStatus ->
syncState == SyncState.InError && networkStatus == NetworkStatus.Online

View File

@@ -50,7 +50,7 @@ class InviteListPresenter @Inject constructor(
override fun present(): InviteListState {
val invites by client
.roomSummaryDataSource
.inviteList()
.inviteRooms()
.collectAsState()
var seenInvites by remember { mutableStateOf<Set<RoomId>>(emptySet()) }

View File

@@ -24,7 +24,6 @@ import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@@ -67,7 +66,7 @@ class ForwardMessagesPresenter @AssistedInject constructor(
var results: SearchBarResultState<ImmutableList<RoomSummaryDetails>> by remember { mutableStateOf(SearchBarResultState.NotSearching()) }
val forwardingActionState: MutableState<Async<ImmutableList<RoomId>>> = remember { mutableStateOf(Async.Uninitialized) }
val summaries by client.roomSummaryDataSource.roomList().collectAsState()
val summaries by client.roomSummaryDataSource.allRooms().collectAsState()
LaunchedEffect(query, summaries) {
val filteredSummaries = summaries.filterIsInstance<RoomSummary.Filled>()

View File

@@ -44,7 +44,7 @@ class DefaultInviteStateDataSource @Inject constructor(
override fun inviteState(): InvitesState {
val invites by client
.roomSummaryDataSource
.inviteList()
.inviteRooms()
.collectAsState()
val seenInvites by seenInvitesStore

View File

@@ -77,7 +77,7 @@ class RoomListPresenter @Inject constructor(
var filter by rememberSaveable { mutableStateOf("") }
val roomSummaries by client
.roomSummaryDataSource
.roomList()
.allRooms()
.collectAsState()
val networkConnectionStatus by networkMonitor.connectivity.collectAsState()

View File

@@ -28,7 +28,7 @@ interface RoomSummaryDataSource {
}
fun loadingState(): StateFlow<LoadingState>
fun roomList(): StateFlow<List<RoomSummary>>
fun inviteList(): StateFlow<List<RoomSummary>>
fun allRooms(): StateFlow<List<RoomSummary>>
fun inviteRooms(): StateFlow<List<RoomSummary>>
fun updateRoomListVisibleRange(range: IntRange)
}

View File

@@ -123,7 +123,6 @@ class RustMatrixClient constructor(
onSlidingSyncUpdate()
}
}.launchIn(sessionCoroutineScope)
rustRoomSummaryDataSource.init()
}
override fun getRoom(roomId: RoomId): MatrixRoom? {
@@ -180,7 +179,7 @@ class RustMatrixClient constructor(
// Wait to receive the room back from the sync
withTimeout(30_000L) {
roomSummaryDataSource.roomList()
roomSummaryDataSource.allRooms()
.filter { roomSummaries ->
roomSummaries.map { it.identifier() }.contains(roomId.value)
}.first()

View File

@@ -0,0 +1,128 @@
/*
* 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.room
import io.element.android.libraries.matrix.api.room.RoomSummary
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate
import org.matrix.rustcomponents.sdk.RoomListEntry
import org.matrix.rustcomponents.sdk.RoomListService
import timber.log.Timber
import java.util.UUID
class RoomSummaryListProcessor(
private val roomSummaries: MutableStateFlow<List<RoomSummary>>,
private val roomListService: RoomListService,
private val roomSummaryDetailsFactory: RoomSummaryDetailsFactory = RoomSummaryDetailsFactory(),
) {
private val roomSummariesByIdentifier = HashMap<String, RoomSummary>()
private val mutex = Mutex()
suspend fun postEntries(entries: List<RoomListEntry>) {
updateRoomSummaries {
Timber.v("Update rooms from postEntries (with ${entries.size} items) on ${Thread.currentThread()}")
addAll(entries.map(::buildSummaryForRoomListEntry))
}
}
suspend fun postUpdate(update: RoomListEntriesUpdate) {
updateRoomSummaries {
Timber.v("Update rooms from postUpdate ($update) on ${Thread.currentThread()}")
applyUpdate(update)
}
}
private fun MutableList<RoomSummary>.applyUpdate(update: RoomListEntriesUpdate) {
when (update) {
is RoomListEntriesUpdate.Append -> {
val roomSummaries = update.values.map {
buildSummaryForRoomListEntry(it)
}
addAll(roomSummaries)
}
is RoomListEntriesUpdate.PushBack -> {
val roomSummary = buildSummaryForRoomListEntry(update.value)
add(roomSummary)
}
is RoomListEntriesUpdate.PushFront -> {
val roomSummary = buildSummaryForRoomListEntry(update.value)
add(0, roomSummary)
}
is RoomListEntriesUpdate.Set -> {
val roomSummary = buildSummaryForRoomListEntry(update.value)
this[update.index.toInt()] = roomSummary
}
is RoomListEntriesUpdate.Insert -> {
val roomSummary = buildSummaryForRoomListEntry(update.value)
add(update.index.toInt(), roomSummary)
}
is RoomListEntriesUpdate.Remove -> {
removeAt(update.index.toInt())
}
is RoomListEntriesUpdate.Reset -> {
Timber.v("Reset size: ${update.values.size}")
clear()
addAll(update.values.map { buildSummaryForRoomListEntry(it) })
}
RoomListEntriesUpdate.PopBack -> {
removeFirstOrNull()
}
RoomListEntriesUpdate.PopFront -> {
removeLastOrNull()
}
RoomListEntriesUpdate.Clear -> {
clear()
}
}
}
private fun buildSummaryForRoomListEntry(entry: RoomListEntry): RoomSummary {
return when (entry) {
RoomListEntry.Empty -> buildEmptyRoomSummary()
is RoomListEntry.Filled -> buildAndCacheRoomSummaryForIdentifier(entry.roomId)
is RoomListEntry.Invalidated -> {
roomSummariesByIdentifier[entry.roomId] ?: buildEmptyRoomSummary()
}
}
}
private fun buildEmptyRoomSummary(): RoomSummary {
return RoomSummary.Empty(UUID.randomUUID().toString())
}
private fun buildAndCacheRoomSummaryForIdentifier(identifier: String): RoomSummary {
val builtRoomSummary = roomListService.roomOrNull(identifier)?.use { roomListItem ->
roomListItem.fullRoom().use { fullRoom ->
RoomSummary.Filled(
details = roomSummaryDetailsFactory.create(roomListItem, fullRoom)
)
}
} ?: buildEmptyRoomSummary()
roomSummariesByIdentifier[builtRoomSummary.identifier()] = builtRoomSummary
return builtRoomSummary
}
private suspend fun updateRoomSummaries(block: MutableList<RoomSummary>.() -> Unit) =
mutex.withLock {
val mutableRoomSummaries = roomSummaries.value.toMutableList()
block(mutableRoomSummaries)
roomSummaries.value = mutableRoomSummaries
}
}

View File

@@ -22,48 +22,44 @@ import io.element.android.libraries.matrix.api.room.RoomSummaryDataSource
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.getAndUpdate
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate
import org.matrix.rustcomponents.sdk.RoomListEntry
import org.matrix.rustcomponents.sdk.RoomListException
import org.matrix.rustcomponents.sdk.RoomListInput
import org.matrix.rustcomponents.sdk.RoomListRange
import org.matrix.rustcomponents.sdk.RoomListService
import timber.log.Timber
import java.util.UUID
internal class RustRoomSummaryDataSource(
private val roomListService: RoomListService,
private val sessionCoroutineScope: CoroutineScope,
private val coroutineDispatchers: CoroutineDispatchers,
private val roomSummaryDetailsFactory: RoomSummaryDetailsFactory = RoomSummaryDetailsFactory(),
coroutineDispatchers: CoroutineDispatchers,
roomSummaryDetailsFactory: RoomSummaryDetailsFactory = RoomSummaryDetailsFactory(),
) : RoomSummaryDataSource {
private val roomList = MutableStateFlow<List<RoomSummary>>(emptyList())
private val inviteList = MutableStateFlow<List<RoomSummary>>(emptyList())
private val loadingState = MutableStateFlow(RoomSummaryDataSource.LoadingState.NotLoaded)
private val allRooms = MutableStateFlow<List<RoomSummary>>(emptyList())
private val inviteRooms = MutableStateFlow<List<RoomSummary>>(emptyList())
fun init() {
private val loadingState = MutableStateFlow(RoomSummaryDataSource.LoadingState.NotLoaded)
private val allRoomsListProcessor = RoomSummaryListProcessor(allRooms, roomListService, roomSummaryDetailsFactory)
init {
sessionCoroutineScope.launch(coroutineDispatchers.computation) {
roomListService.allRooms().entriesFlow { roomListEntries ->
roomList.value = roomListEntries.map(::buildSummaryForRoomListEntry)
allRoomsListProcessor.postEntries(roomListEntries)
}.onEach { update ->
roomList.getAndUpdate {
it.applyUpdate(update)
}
allRoomsListProcessor.postUpdate(update)
}.launchIn(this)
}
}
override fun roomList(): StateFlow<List<RoomSummary>> {
return roomList
override fun allRooms(): StateFlow<List<RoomSummary>> {
return allRooms
}
override fun inviteList(): StateFlow<List<RoomSummary>> {
return inviteList
override fun inviteRooms(): StateFlow<List<RoomSummary>> {
return inviteRooms
}
override fun loadingState(): StateFlow<RoomSummaryDataSource.LoadingState> {
@@ -83,72 +79,4 @@ internal class RustRoomSummaryDataSource(
}
}
}
private fun List<RoomSummary>.applyUpdate(update: RoomListEntriesUpdate): List<RoomSummary> {
val newList = toMutableList()
when (update) {
is RoomListEntriesUpdate.Append -> {
val roomSummaries = update.values.map {
buildSummaryForRoomListEntry(it)
}
newList.addAll(roomSummaries)
}
is RoomListEntriesUpdate.PushBack -> {
val roomSummary = buildSummaryForRoomListEntry(update.value)
newList.add(roomSummary)
}
is RoomListEntriesUpdate.PushFront -> {
val roomSummary = buildSummaryForRoomListEntry(update.value)
newList.add(0, roomSummary)
}
is RoomListEntriesUpdate.Set -> {
val roomSummary = buildSummaryForRoomListEntry(update.value)
newList[update.index.toInt()] = roomSummary
}
is RoomListEntriesUpdate.Insert -> {
val roomSummary = buildSummaryForRoomListEntry(update.value)
newList.add(update.index.toInt(), roomSummary)
}
is RoomListEntriesUpdate.Remove -> {
newList.removeAt(update.index.toInt())
}
is RoomListEntriesUpdate.Reset -> {
newList.clear()
newList.addAll(update.values.map { buildSummaryForRoomListEntry(it) })
}
RoomListEntriesUpdate.PopBack -> {
newList.removeFirstOrNull()
}
RoomListEntriesUpdate.PopFront -> {
newList.removeLastOrNull()
}
RoomListEntriesUpdate.Clear -> {
newList.clear()
}
}
return newList
}
private fun buildSummaryForRoomListEntry(entry: RoomListEntry): RoomSummary {
return when (entry) {
RoomListEntry.Empty -> buildEmptyRoomSummary()
is RoomListEntry.Invalidated -> buildRoomSummaryForIdentifier(entry.roomId)
is RoomListEntry.Filled -> buildRoomSummaryForIdentifier(entry.roomId)
}
}
private fun buildEmptyRoomSummary(): RoomSummary {
return RoomSummary.Empty(UUID.randomUUID().toString())
}
private fun buildRoomSummaryForIdentifier(identifier: String): RoomSummary {
val roomListItem = roomListService.roomOrNull(identifier) ?: return RoomSummary.Empty(identifier)
return roomListItem.use {
roomListItem.fullRoom().use { fullRoom ->
RoomSummary.Filled(
details = roomSummaryDetailsFactory.create(roomListItem, fullRoom)
)
}
}
}
}

View File

@@ -30,7 +30,7 @@ import org.matrix.rustcomponents.sdk.RoomListServiceState
class RustSyncService(
private val roomListService: RoomListService,
private val sessionCoroutineScope: CoroutineScope
sessionCoroutineScope: CoroutineScope
) : SyncService {
override fun startSync() {

View File

@@ -29,7 +29,7 @@ class FakeRoomSummaryDataSource : RoomSummaryDataSource {
roomSummariesFlow.emit(roomSummaries)
}
override fun roomList(): StateFlow<List<RoomSummary>> {
override fun allRooms(): StateFlow<List<RoomSummary>> {
return roomSummariesFlow
}