diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenter.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenter.kt index cb7ad57837..9d28095e9b 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenter.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenter.kt @@ -94,9 +94,7 @@ class JoinRoomPresenter( val roomInfo by remember { matrixClient.getRoomInfoFlow(roomId) }.collectAsState(initial = Optional.empty()) - val spaceRoom by remember { - spaceList.currentSpaceFlow() - }.collectAsState() + val spaceRoom by spaceList.currentSpaceFlow.collectAsState() val joinAction: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } val knockAction: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } val cancelKnockAction: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpacePresenter.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpacePresenter.kt index 1955ab652e..f5bfdcc840 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpacePresenter.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/SpacePresenter.kt @@ -29,7 +29,6 @@ import kotlinx.collections.immutable.toPersistentSet import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch -import java.util.Optional import kotlin.jvm.optionals.getOrNull @Inject @@ -66,7 +65,7 @@ class SpacePresenter( } }.collectAsState() - val currentSpace by remember { spaceRoomList.currentSpaceFlow() }.collectAsState(Optional.empty()) + val currentSpace by spaceRoomList.currentSpaceFlow.collectAsState() fun handleEvents(event: SpaceEvents) { when (event) { diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/SpaceRoomList.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/SpaceRoomList.kt index e55e1b87bd..060eb1f2db 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/SpaceRoomList.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/spaces/SpaceRoomList.kt @@ -17,7 +17,7 @@ interface SpaceRoomList { data class Idle(val hasMoreToLoad: Boolean) : PaginationStatus } - fun currentSpaceFlow(): StateFlow> + val currentSpaceFlow: StateFlow> val spaceRoomsFlow: Flow> val paginationStatusFlow: StateFlow diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/RustSpaceRoomList.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/RustSpaceRoomList.kt index 1a940bd2f0..dbcab85ab1 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/RustSpaceRoomList.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/RustSpaceRoomList.kt @@ -8,15 +8,14 @@ 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.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import uniffi.matrix_sdk_ui.SpaceRoomListPaginationState @@ -24,25 +23,21 @@ import java.util.Optional 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() - override fun currentSpaceFlow(): StateFlow> { - return spaceRoomCache.getSpaceRoomFlow(roomId) - } + override val currentSpaceFlow = MutableStateFlow>(Optional.empty()) override val spaceRoomsFlow = MutableSharedFlow>(replay = 1, extraBufferCapacity = Int.MAX_VALUE) + override val paginationStatusFlow: MutableStateFlow = MutableStateFlow(SpaceRoomList.PaginationStatus.Idle(hasMoreToLoad = false)) private val spaceListUpdateProcessor = SpaceListUpdateProcessor( spaceRoomsFlow = spaceRoomsFlow, - mapper = spaceRoomMapper, - spaceRoomCache = spaceRoomCache + mapper = spaceRoomMapper ) init { @@ -64,6 +59,14 @@ class RustSpaceRoomList( } .collect() } + sessionCoroutineScope.launch { + inner.await().spaceUpdateFlow() + .map { space -> space.map(spaceRoomMapper::map) } + .onEach { space -> + currentSpaceFlow.emit(space) + } + .collect() + } } override suspend fun paginate(): Result { diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/RustSpaceService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/RustSpaceService.kt index 2a63367577..3dd905caf2 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/RustSpaceService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/RustSpaceService.kt @@ -38,12 +38,10 @@ class RustSpaceService( private val sessionDispatcher: CoroutineDispatcher, ) : SpaceService { private val spaceRoomMapper = SpaceRoomMapper() - private val spaceRoomCache = SpaceRoomCache() override val spaceRoomsFlow = MutableSharedFlow>(replay = 1, extraBufferCapacity = 1) private val spaceListUpdateProcessor = SpaceListUpdateProcessor( spaceRoomsFlow = spaceRoomsFlow, - mapper = spaceRoomMapper, - spaceRoomCache = spaceRoomCache + mapper = spaceRoomMapper ) override suspend fun joinedSpaces(): Result> = withContext(sessionDispatcher) { @@ -57,11 +55,9 @@ class RustSpaceService( override fun spaceRoomList(id: RoomId): SpaceRoomList { return RustSpaceRoomList( - roomId = id, innerProvider = { innerSpaceService.spaceRoomList(id.value) }, sessionCoroutineScope = sessionCoroutineScope, spaceRoomMapper = spaceRoomMapper, - spaceRoomCache = spaceRoomCache, ) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/SpaceListUpdateProcessor.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/SpaceListUpdateProcessor.kt index f1193661ea..40310561e2 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/SpaceListUpdateProcessor.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/SpaceListUpdateProcessor.kt @@ -18,7 +18,6 @@ import timber.log.Timber internal class SpaceListUpdateProcessor( private val spaceRoomsFlow: MutableSharedFlow>, private val mapper: SpaceRoomMapper, - private val spaceRoomCache: SpaceRoomCache, ) { private val mutex = Mutex() @@ -37,7 +36,6 @@ internal class SpaceListUpdateProcessor( mutableListOf() } block(spaceRooms) - spaceRoomCache.update(spaceRooms) spaceRoomsFlow.emit(spaceRooms) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/SpaceRoomCache.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/SpaceRoomCache.kt deleted file mode 100644 index c8f55f1db6..0000000000 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/SpaceRoomCache.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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 io.element.android.libraries.core.coroutine.mapState -import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.api.spaces.SpaceRoom -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.update -import java.util.Optional - -/** - * 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>(emptyMap()) - fun getSpaceRoomFlow(roomId: RoomId): StateFlow> { - return inMemoryCache.mapState { Optional.ofNullable(it[roomId]) } - } - - fun update(spaceRooms: List) { - inMemoryCache.update { currentValues -> - val newValues = spaceRooms - .filter { it.isSpace } - .associateBy { it.roomId } - currentValues + newValues - } - } -} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/SpaceRoomListExtensions.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/SpaceRoomListExtensions.kt index 3d1a002e4d..c7107f745f 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/SpaceRoomListExtensions.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/spaces/SpaceRoomListExtensions.kt @@ -16,11 +16,14 @@ import kotlinx.coroutines.flow.buffer import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.catch import org.matrix.rustcomponents.sdk.SpaceListUpdate +import org.matrix.rustcomponents.sdk.SpaceRoom import org.matrix.rustcomponents.sdk.SpaceRoomListEntriesListener import org.matrix.rustcomponents.sdk.SpaceRoomListInterface import org.matrix.rustcomponents.sdk.SpaceRoomListPaginationStateListener +import org.matrix.rustcomponents.sdk.SpaceRoomListSpaceListener import timber.log.Timber import uniffi.matrix_sdk_ui.SpaceRoomListPaginationState +import java.util.Optional internal fun SpaceRoomListInterface.paginationStateFlow(): Flow = callbackFlow { val listener = object : SpaceRoomListPaginationStateListener { @@ -55,3 +58,21 @@ internal fun SpaceRoomListInterface.spaceListUpdateFlow(): Flow> = + callbackFlow { + val listener = object : SpaceRoomListSpaceListener { + override fun onUpdate(space: SpaceRoom?) { + trySendBlocking(Optional.ofNullable(space)) + } + } + Timber.d("Open spaceUpdateFlow for SpaceRoomListInterface ${this@spaceUpdateFlow}") + trySendBlocking(Optional.ofNullable(space())) + val taskHandle = subscribeToSpaceUpdates(listener) + awaitClose { + Timber.d("Close spaceUpdateFlow for SpaceRoomListInterface ${this@spaceUpdateFlow}") + taskHandle.cancelAndDestroy() + } + }.catch { + Timber.d(it, "spaceUpdateFlow() failed") + }.buffer(Channel.UNLIMITED) diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/spaces/RoomSummaryListProcessorTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/spaces/RoomSummaryListProcessorTest.kt index 47c3a19a6b..ae05c7b4e9 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/spaces/RoomSummaryListProcessorTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/spaces/RoomSummaryListProcessorTest.kt @@ -185,6 +185,5 @@ class RoomSummaryListProcessorTest { ) = SpaceListUpdateProcessor( spaceRoomsFlow = spaceRoomsFlow, mapper = SpaceRoomMapper(), - spaceRoomCache = SpaceRoomCache(), ) } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/spaces/RustSpaceRoomListTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/spaces/RustSpaceRoomListTest.kt index 3d4cb98abc..007b6a669f 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/spaces/RustSpaceRoomListTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/spaces/RustSpaceRoomListTest.kt @@ -11,13 +11,10 @@ package io.element.android.libraries.matrix.impl.spaces import app.cash.turbine.test import com.google.common.truth.Truth.assertThat -import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.spaces.SpaceRoomList import io.element.android.libraries.matrix.impl.fixtures.factories.aRustSpaceRoom import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiSpaceRoomList -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.previewutils.room.aSpaceRoom import io.element.android.tests.testutils.lambda.lambdaRecorder import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope @@ -26,7 +23,6 @@ import kotlinx.coroutines.test.runTest import org.junit.Test import org.matrix.rustcomponents.sdk.SpaceListUpdate import uniffi.matrix_sdk_ui.SpaceRoomListPaginationState -import kotlin.jvm.optionals.getOrNull import org.matrix.rustcomponents.sdk.SpaceRoomList as InnerSpaceRoomList class RustSpaceRoomListTest { @@ -87,33 +83,15 @@ class RustSpaceRoomListTest { paginateResult.assertions().isCalledOnce() } - @Test - fun `currentSpaceFlow reads value from the SpaceRoomCache`() = runTest { - val spaceRoomCache = SpaceRoomCache() - val sut = createRustSpaceRoomList( - spaceRoomCache = spaceRoomCache, - ) - sut.currentSpaceFlow().test { - assertThat(awaitItem().getOrNull()).isNull() - val spaceRoom = aSpaceRoom(roomId = A_ROOM_ID) - spaceRoomCache.update(listOf(spaceRoom)) - assertThat(awaitItem().getOrNull()).isEqualTo(spaceRoom) - } - } - private fun TestScope.createRustSpaceRoomList( - roomId: RoomId = A_ROOM_ID, innerSpaceRoomList: InnerSpaceRoomList = FakeFfiSpaceRoomList(), innerProvider: suspend () -> InnerSpaceRoomList = { innerSpaceRoomList }, spaceRoomMapper: SpaceRoomMapper = SpaceRoomMapper(), - spaceRoomCache: SpaceRoomCache = SpaceRoomCache(), ): RustSpaceRoomList { return RustSpaceRoomList( - roomId = roomId, innerProvider = innerProvider, sessionCoroutineScope = backgroundScope, spaceRoomMapper = spaceRoomMapper, - spaceRoomCache = spaceRoomCache, ) } } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/spaces/SpaceRoomCacheTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/spaces/SpaceRoomCacheTest.kt deleted file mode 100644 index 061169c5e8..0000000000 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/spaces/SpaceRoomCacheTest.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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 app.cash.turbine.test -import com.google.common.truth.Truth.assertThat -import io.element.android.libraries.matrix.api.room.RoomType -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.previewutils.room.aSpaceRoom -import kotlinx.coroutines.test.runTest -import org.junit.Test - -class SpaceRoomCacheTest { - @Test - fun `getSpaceRoomFlow emits items`() = runTest { - val sut = SpaceRoomCache() - sut.getSpaceRoomFlow(A_ROOM_ID).test { - assertThat(awaitItem().isEmpty).isTrue() - val room = aSpaceRoom( - roomId = A_ROOM_ID, - roomType = RoomType.Room, - ) - sut.update(listOf(room)) - // Not a space, should not be cached - expectNoEvents() - val space = aSpaceRoom( - roomId = A_ROOM_ID, - roomType = RoomType.Space, - ) - sut.update(listOf(space)) - assertThat(awaitItem().get()).isEqualTo(space) - val spaceOther = aSpaceRoom( - roomId = A_ROOM_ID_2, - roomType = RoomType.Space, - ) - sut.update(listOf(spaceOther)) - expectNoEvents() - } - } -} diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/spaces/FakeSpaceRoomList.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/spaces/FakeSpaceRoomList.kt index 3579f162b9..d86e178fa9 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/spaces/FakeSpaceRoomList.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/spaces/FakeSpaceRoomList.kt @@ -24,7 +24,7 @@ class FakeSpaceRoomList( private val paginateResult: () -> Result = { lambdaError() }, ) : SpaceRoomList { private val currentSpaceMutableStateFlow: MutableStateFlow> = MutableStateFlow(Optional.ofNullable(initialSpaceFlowValue)) - override fun currentSpaceFlow(): StateFlow> = currentSpaceMutableStateFlow.asStateFlow() + override val currentSpaceFlow: StateFlow> = currentSpaceMutableStateFlow.asStateFlow() fun emitCurrentSpace(value: SpaceRoom?) { currentSpaceMutableStateFlow.value = Optional.ofNullable(value)