misc (space) : ensure SpaceRoomList is destroyed

This commit is contained in:
ganfra 2025-10-01 10:49:35 +02:00
parent c127d4d63c
commit 11c0799216
12 changed files with 120 additions and 66 deletions

View file

@ -8,26 +8,31 @@
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.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import timber.log.Timber
import uniffi.matrix_sdk_ui.SpaceRoomListPaginationState
import java.util.Optional
import org.matrix.rustcomponents.sdk.SpaceRoomList as InnerSpaceRoomList
class RustSpaceRoomList(
override val roomId: RoomId,
private val innerProvider: suspend () -> InnerSpaceRoomList,
sessionCoroutineScope: CoroutineScope,
private val coroutineScope: CoroutineScope,
spaceRoomMapper: SpaceRoomMapper,
) : SpaceRoomList {
private val inner = CompletableDeferred<InnerSpaceRoomList>()
private val innerCompletable = CompletableDeferred<InnerSpaceRoomList>()
override val currentSpaceFlow = MutableStateFlow<Optional<SpaceRoom>>(Optional.empty())
@ -41,37 +46,45 @@ class RustSpaceRoomList(
)
init {
sessionCoroutineScope.launch {
inner.complete(innerProvider())
}
sessionCoroutineScope.launch {
inner.await().paginationStateFlow()
coroutineScope.launch {
val inner = innerProvider()
innerCompletable.complete(inner)
inner.paginationStateFlow()
.onEach { paginationStatus ->
paginationStatusFlow.emit(paginationStatus.into())
}
.collect()
}
.launchIn(this)
sessionCoroutineScope.launch {
inner.await().spaceListUpdateFlow()
inner.spaceListUpdateFlow()
.onEach { updates ->
spaceListUpdateProcessor.postUpdates(updates)
}
.collect()
}
sessionCoroutineScope.launch {
inner.await().spaceUpdateFlow()
.launchIn(this)
inner.spaceUpdateFlow()
.map { space -> space.map(spaceRoomMapper::map) }
.onEach { space ->
currentSpaceFlow.emit(space)
}
.collect()
.launchIn(this)
}
}
override suspend fun paginate(): Result<Unit> {
return runCatchingExceptions {
inner.await().paginate()
innerCompletable.await().paginate()
}
}
@OptIn(ExperimentalCoroutinesApi::class)
override fun destroy() {
Timber.d("Destroying SpaceRoomList $roomId")
coroutineScope.cancel()
try {
innerCompletable.getCompleted().destroy()
} catch (_: Exception) {
// Ignore, we just want to make sure it's completed
}
}

View file

@ -7,6 +7,7 @@
package io.element.android.libraries.matrix.impl.spaces
import io.element.android.libraries.core.coroutine.childScope
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
@ -54,9 +55,11 @@ class RustSpaceService(
}
override fun spaceRoomList(id: RoomId): SpaceRoomList {
val childCoroutineScope = sessionCoroutineScope.childScope(sessionDispatcher, "SpaceRoomListScope-$this")
return RustSpaceRoomList(
roomId = id,
innerProvider = { innerSpaceService.spaceRoomList(id.value) },
sessionCoroutineScope = sessionCoroutineScope,
coroutineScope = childCoroutineScope,
spaceRoomMapper = spaceRoomMapper,
)
}

View file

@ -11,9 +11,11 @@ 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.tests.testutils.lambda.lambdaRecorder
import kotlinx.coroutines.ExperimentalCoroutinesApi
@ -84,13 +86,15 @@ class RustSpaceRoomListTest {
}
private fun TestScope.createRustSpaceRoomList(
roomId: RoomId = A_ROOM_ID,
innerSpaceRoomList: InnerSpaceRoomList = FakeFfiSpaceRoomList(),
innerProvider: suspend () -> InnerSpaceRoomList = { innerSpaceRoomList },
spaceRoomMapper: SpaceRoomMapper = SpaceRoomMapper(),
): RustSpaceRoomList {
return RustSpaceRoomList(
roomId = roomId,
innerProvider = innerProvider,
sessionCoroutineScope = backgroundScope,
coroutineScope = backgroundScope,
spaceRoomMapper = spaceRoomMapper,
)
}