feature (space) : add space cache and navigation to sub space/room

This commit is contained in:
ganfra 2025-09-10 10:48:34 +02:00 committed by Benoit Marty
parent b45a4c3b2c
commit d4d2aa1707
20 changed files with 278 additions and 101 deletions

View file

@ -8,10 +8,12 @@
package io.element.android.libraries.matrix.impl.spaces
import io.element.android.libraries.core.extensions.runCatchingExceptions
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
import io.element.android.libraries.matrix.api.spaces.SpaceRoomList
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collect
@ -21,16 +23,27 @@ import uniffi.matrix_sdk_ui.SpaceRoomListPaginationState
import org.matrix.rustcomponents.sdk.SpaceRoomList as InnerSpaceRoomList
class RustSpaceRoomList(
private val roomId: RoomId,
private val innerProvider: suspend () -> InnerSpaceRoomList,
sessionCoroutineScope: CoroutineScope,
spaceRoomMapper: SpaceRoomMapper,
private val spaceRoomCache: SpaceRoomCache,
) : SpaceRoomList {
private val inner = CompletableDeferred<InnerSpaceRoomList>()
override fun currentSpaceFlow(): Flow<SpaceRoom?> {
return spaceRoomCache.getSpaceRoomFlow(roomId)
}
override val spaceRoomsFlow = MutableSharedFlow<List<SpaceRoom>>(replay = 1, extraBufferCapacity = Int.MAX_VALUE)
override val paginationStatusFlow: MutableStateFlow<SpaceRoomList.PaginationStatus> =
MutableStateFlow(SpaceRoomList.PaginationStatus.Idle(hasMoreToLoad = false))
private val spaceListUpdateProcessor = SpaceListUpdateProcessor(spaceRoomsFlow, spaceRoomMapper)
private val spaceListUpdateProcessor = SpaceListUpdateProcessor(
spaceRoomsFlow = spaceRoomsFlow,
mapper = spaceRoomMapper,
spaceRoomCache = spaceRoomCache
)
init {
sessionCoroutineScope.launch {

View file

@ -39,8 +39,13 @@ class RustSpaceService(
private val sessionDispatcher: CoroutineDispatcher,
) : SpaceService {
private val spaceRoomMapper = SpaceRoomMapper()
private val spaceRoomCache = SpaceRoomCache()
override val spaceRoomsFlow = MutableSharedFlow<List<SpaceRoom>>(replay = 1, extraBufferCapacity = 1)
private val spaceListUpdateProcessor = SpaceListUpdateProcessor(spaceRoomsFlow, spaceRoomMapper)
private val spaceListUpdateProcessor = SpaceListUpdateProcessor(
spaceRoomsFlow = spaceRoomsFlow,
mapper = spaceRoomMapper,
spaceRoomCache = spaceRoomCache
)
override suspend fun joinedSpaces(): Result<List<SpaceRoom>> = withContext(sessionDispatcher) {
runCatchingExceptions {
@ -53,9 +58,11 @@ class RustSpaceService(
override fun spaceRoomList(id: RoomId): SpaceRoomList {
return RustSpaceRoomList(
roomId = id,
innerProvider = { innerSpaceService.spaceRoomList(id.value) },
sessionCoroutineScope = sessionCoroutineScope,
spaceRoomMapper = spaceRoomMapper
spaceRoomMapper = spaceRoomMapper,
spaceRoomCache = spaceRoomCache,
)
}

View file

@ -18,6 +18,7 @@ import timber.log.Timber
internal class SpaceListUpdateProcessor(
private val spaceRoomsFlow: MutableSharedFlow<List<SpaceRoom>>,
private val mapper: SpaceRoomMapper,
private val spaceRoomCache: SpaceRoomCache,
) {
private val mutex = Mutex()
@ -36,7 +37,8 @@ internal class SpaceListUpdateProcessor(
mutableListOf()
}
block(spaceRooms)
spaceRoomsFlow.tryEmit(spaceRooms)
spaceRoomCache.update(spaceRooms)
spaceRoomsFlow.emit(spaceRooms)
}
private fun MutableList<SpaceRoom>.applyUpdate(update: SpaceListUpdate) {

View file

@ -0,0 +1,44 @@
/*
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.matrix.impl.spaces
import dev.zacsweers.metro.Inject
import dev.zacsweers.metro.SingleIn
import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
/**
* An in memory cache of space rooms.
* Only caches Rooms with roomType [io.element.android.libraries.matrix.api.room.RoomType.Space].
*/
class SpaceRoomCache() {
private val inMemoryCache = MutableStateFlow<MutableMap<RoomId, SpaceRoom>>(LinkedHashMap())
private val mutex = Mutex()
fun getSpaceRoomFlow(roomId: RoomId): Flow<SpaceRoom?> {
return inMemoryCache.map { it[roomId] }
}
suspend fun update(spaceRooms: List<SpaceRoom>) = mutex.withLock {
inMemoryCache.update { cache ->
spaceRooms
.filter { it.isSpace }
.forEach { spaceRoom ->
cache.put(spaceRoom.roomId, spaceRoom)
}
cache
}
}
}