Spaces : expose new SpaceServiceFilter
This commit is contained in:
parent
2132b65bf6
commit
9d576394ac
6 changed files with 179 additions and 0 deletions
|
|
@ -13,6 +13,7 @@ import kotlinx.coroutines.flow.SharedFlow
|
||||||
|
|
||||||
interface SpaceService {
|
interface SpaceService {
|
||||||
val topLevelSpacesFlow: SharedFlow<List<SpaceRoom>>
|
val topLevelSpacesFlow: SharedFlow<List<SpaceRoom>>
|
||||||
|
val spaceFiltersFlow: SharedFlow<List<SpaceServiceFilter>>
|
||||||
suspend fun joinedParents(spaceId: RoomId): Result<List<SpaceRoom>>
|
suspend fun joinedParents(spaceId: RoomId): Result<List<SpaceRoom>>
|
||||||
|
|
||||||
suspend fun getSpaceRoom(spaceId: RoomId): SpaceRoom?
|
suspend fun getSpaceRoom(spaceId: RoomId): SpaceRoom?
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Element Creations Ltd.
|
||||||
|
* 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.api.spaces
|
||||||
|
|
||||||
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a space filter for filtering rooms by space membership.
|
||||||
|
*
|
||||||
|
* @property spaceRoom The space room associated with this filter.
|
||||||
|
* @property level The nesting level of the space (0 = top level, 1 = first level child, etc.).
|
||||||
|
* @property descendants The list of room IDs that are descendants of this space.
|
||||||
|
*/
|
||||||
|
data class SpaceServiceFilter(
|
||||||
|
val spaceRoom: SpaceRoom,
|
||||||
|
val level: Int,
|
||||||
|
val descendants: List<RoomId>,
|
||||||
|
)
|
||||||
|
|
@ -16,6 +16,7 @@ import io.element.android.libraries.matrix.api.spaces.LeaveSpaceHandle
|
||||||
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
|
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
|
||||||
import io.element.android.libraries.matrix.api.spaces.SpaceRoomList
|
import io.element.android.libraries.matrix.api.spaces.SpaceRoomList
|
||||||
import io.element.android.libraries.matrix.api.spaces.SpaceService
|
import io.element.android.libraries.matrix.api.spaces.SpaceService
|
||||||
|
import io.element.android.libraries.matrix.api.spaces.SpaceServiceFilter
|
||||||
import io.element.android.libraries.matrix.impl.util.cancelAndDestroy
|
import io.element.android.libraries.matrix.impl.util.cancelAndDestroy
|
||||||
import io.element.android.services.analytics.api.AnalyticsService
|
import io.element.android.services.analytics.api.AnalyticsService
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
|
|
@ -31,9 +32,11 @@ import kotlinx.coroutines.flow.catch
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.matrix.rustcomponents.sdk.SpaceFilterUpdate
|
||||||
import org.matrix.rustcomponents.sdk.SpaceListUpdate
|
import org.matrix.rustcomponents.sdk.SpaceListUpdate
|
||||||
import org.matrix.rustcomponents.sdk.SpaceServiceInterface
|
import org.matrix.rustcomponents.sdk.SpaceServiceInterface
|
||||||
import org.matrix.rustcomponents.sdk.SpaceServiceJoinedSpacesListener
|
import org.matrix.rustcomponents.sdk.SpaceServiceJoinedSpacesListener
|
||||||
|
import org.matrix.rustcomponents.sdk.SpaceServiceSpaceFiltersListener
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import org.matrix.rustcomponents.sdk.SpaceService as ClientSpaceService
|
import org.matrix.rustcomponents.sdk.SpaceService as ClientSpaceService
|
||||||
|
|
||||||
|
|
@ -45,6 +48,8 @@ class RustSpaceService(
|
||||||
private val analyticsService: AnalyticsService,
|
private val analyticsService: AnalyticsService,
|
||||||
) : SpaceService {
|
) : SpaceService {
|
||||||
private val spaceRoomMapper = SpaceRoomMapper()
|
private val spaceRoomMapper = SpaceRoomMapper()
|
||||||
|
private val spaceFilterMapper = SpaceServiceFilterMapper(spaceRoomMapper)
|
||||||
|
|
||||||
override val topLevelSpacesFlow = MutableSharedFlow<List<SpaceRoom>>(replay = 1, extraBufferCapacity = 1)
|
override val topLevelSpacesFlow = MutableSharedFlow<List<SpaceRoom>>(replay = 1, extraBufferCapacity = 1)
|
||||||
private val spaceListUpdateProcessor = SpaceListUpdateProcessor(
|
private val spaceListUpdateProcessor = SpaceListUpdateProcessor(
|
||||||
spaceRoomsFlow = topLevelSpacesFlow,
|
spaceRoomsFlow = topLevelSpacesFlow,
|
||||||
|
|
@ -52,6 +57,12 @@ class RustSpaceService(
|
||||||
analyticsService = analyticsService,
|
analyticsService = analyticsService,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
override val spaceFiltersFlow = MutableSharedFlow<List<SpaceServiceFilter>>(replay = 1, extraBufferCapacity = 1)
|
||||||
|
private val spaceFilterUpdateProcessor = SpaceServiceFilterUpdateProcessor(
|
||||||
|
spaceFiltersFlow = spaceFiltersFlow,
|
||||||
|
mapper = spaceFilterMapper,
|
||||||
|
)
|
||||||
|
|
||||||
override suspend fun joinedParents(spaceId: RoomId): Result<List<SpaceRoom>> = withContext(sessionDispatcher) {
|
override suspend fun joinedParents(spaceId: RoomId): Result<List<SpaceRoom>> = withContext(sessionDispatcher) {
|
||||||
runCatchingExceptions {
|
runCatchingExceptions {
|
||||||
innerSpaceService
|
innerSpaceService
|
||||||
|
|
@ -115,6 +126,13 @@ class RustSpaceService(
|
||||||
spaceListUpdateProcessor.postUpdates(updates)
|
spaceListUpdateProcessor.postUpdates(updates)
|
||||||
}
|
}
|
||||||
.launchIn(sessionCoroutineScope)
|
.launchIn(sessionCoroutineScope)
|
||||||
|
|
||||||
|
innerSpaceService
|
||||||
|
.spaceFilterListUpdate()
|
||||||
|
.onEach { updates ->
|
||||||
|
spaceFilterUpdateProcessor.postUpdates(updates)
|
||||||
|
}
|
||||||
|
.launchIn(sessionCoroutineScope)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -134,3 +152,20 @@ internal fun SpaceServiceInterface.spaceListUpdate(): Flow<List<SpaceListUpdate>
|
||||||
}.catch {
|
}.catch {
|
||||||
Timber.d(it, "spaceDiffFlow() failed")
|
Timber.d(it, "spaceDiffFlow() failed")
|
||||||
}.buffer(Channel.UNLIMITED)
|
}.buffer(Channel.UNLIMITED)
|
||||||
|
|
||||||
|
internal fun SpaceServiceInterface.spaceFilterListUpdate(): Flow<List<SpaceFilterUpdate>> =
|
||||||
|
callbackFlow {
|
||||||
|
val listener = object : SpaceServiceSpaceFiltersListener {
|
||||||
|
override fun onUpdate(filterUpdates: List<SpaceFilterUpdate>) {
|
||||||
|
trySendBlocking(filterUpdates)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Timber.d("Open spaceFilterDiffFlow for SpaceServiceInterface ${this@spaceFilterListUpdate}")
|
||||||
|
val taskHandle = subscribeToSpaceFilters(listener)
|
||||||
|
awaitClose {
|
||||||
|
Timber.d("Close spaceFilterDiffFlow for SpaceServiceInterface ${this@spaceFilterListUpdate}")
|
||||||
|
taskHandle.cancelAndDestroy()
|
||||||
|
}
|
||||||
|
}.catch {
|
||||||
|
Timber.d(it, "spaceFilterListUpdate() failed")
|
||||||
|
}.buffer(Channel.UNLIMITED)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Element Creations Ltd.
|
||||||
|
* 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.matrix.api.core.RoomId
|
||||||
|
import io.element.android.libraries.matrix.api.spaces.SpaceServiceFilter
|
||||||
|
import org.matrix.rustcomponents.sdk.SpaceFilter as RustSpaceFilter
|
||||||
|
|
||||||
|
class SpaceServiceFilterMapper(
|
||||||
|
private val spaceRoomMapper: SpaceRoomMapper,
|
||||||
|
) {
|
||||||
|
fun map(spaceFilter: RustSpaceFilter): SpaceServiceFilter {
|
||||||
|
return SpaceServiceFilter(
|
||||||
|
spaceRoom = spaceRoomMapper.map(spaceFilter.spaceRoom),
|
||||||
|
level = spaceFilter.level.toInt(),
|
||||||
|
descendants = spaceFilter.descendants.map { RoomId(it) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Element Creations Ltd.
|
||||||
|
* 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.matrix.api.spaces.SpaceServiceFilter
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
import org.matrix.rustcomponents.sdk.SpaceFilterUpdate
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
|
internal class SpaceServiceFilterUpdateProcessor(
|
||||||
|
private val spaceFiltersFlow: MutableSharedFlow<List<SpaceServiceFilter>>,
|
||||||
|
private val mapper: SpaceServiceFilterMapper,
|
||||||
|
) {
|
||||||
|
private val mutex = Mutex()
|
||||||
|
|
||||||
|
suspend fun postUpdates(updates: List<SpaceFilterUpdate>) {
|
||||||
|
Timber.v("Update space filters from postUpdates (with ${updates.size} items) on ${Thread.currentThread()}")
|
||||||
|
updateSpaceFilters {
|
||||||
|
updates.forEach { update -> applyUpdate(update) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun updateSpaceFilters(block: MutableList<SpaceServiceFilter>.() -> Unit) =
|
||||||
|
mutex.withLock {
|
||||||
|
val spaceFilters = if (spaceFiltersFlow.replayCache.isNotEmpty()) {
|
||||||
|
spaceFiltersFlow.first().toMutableList()
|
||||||
|
} else {
|
||||||
|
mutableListOf()
|
||||||
|
}
|
||||||
|
block(spaceFilters)
|
||||||
|
spaceFiltersFlow.emit(spaceFilters)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun MutableList<SpaceServiceFilter>.applyUpdate(update: SpaceFilterUpdate) {
|
||||||
|
when (update) {
|
||||||
|
is SpaceFilterUpdate.Append -> {
|
||||||
|
val newFilters = update.values.map(mapper::map)
|
||||||
|
addAll(newFilters)
|
||||||
|
}
|
||||||
|
SpaceFilterUpdate.Clear -> clear()
|
||||||
|
is SpaceFilterUpdate.Insert -> {
|
||||||
|
val newFilter = mapper.map(update.value)
|
||||||
|
add(update.index.toInt(), newFilter)
|
||||||
|
}
|
||||||
|
SpaceFilterUpdate.PopBack -> {
|
||||||
|
removeAt(lastIndex)
|
||||||
|
}
|
||||||
|
SpaceFilterUpdate.PopFront -> {
|
||||||
|
removeAt(0)
|
||||||
|
}
|
||||||
|
is SpaceFilterUpdate.PushBack -> {
|
||||||
|
val newFilter = mapper.map(update.value)
|
||||||
|
add(newFilter)
|
||||||
|
}
|
||||||
|
is SpaceFilterUpdate.PushFront -> {
|
||||||
|
val newFilter = mapper.map(update.value)
|
||||||
|
add(0, newFilter)
|
||||||
|
}
|
||||||
|
is SpaceFilterUpdate.Remove -> {
|
||||||
|
removeAt(update.index.toInt())
|
||||||
|
}
|
||||||
|
is SpaceFilterUpdate.Reset -> {
|
||||||
|
clear()
|
||||||
|
val newFilters = update.values.map(mapper::map)
|
||||||
|
addAll(newFilters)
|
||||||
|
}
|
||||||
|
is SpaceFilterUpdate.Set -> {
|
||||||
|
val newFilter = mapper.map(update.value)
|
||||||
|
this[update.index.toInt()] = newFilter
|
||||||
|
}
|
||||||
|
is SpaceFilterUpdate.Truncate -> {
|
||||||
|
subList(update.length.toInt(), size).clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -13,6 +13,7 @@ import io.element.android.libraries.matrix.api.spaces.LeaveSpaceHandle
|
||||||
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
|
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
|
||||||
import io.element.android.libraries.matrix.api.spaces.SpaceRoomList
|
import io.element.android.libraries.matrix.api.spaces.SpaceRoomList
|
||||||
import io.element.android.libraries.matrix.api.spaces.SpaceService
|
import io.element.android.libraries.matrix.api.spaces.SpaceService
|
||||||
|
import io.element.android.libraries.matrix.api.spaces.SpaceServiceFilter
|
||||||
import io.element.android.tests.testutils.lambda.lambdaError
|
import io.element.android.tests.testutils.lambda.lambdaError
|
||||||
import io.element.android.tests.testutils.simulateLongTask
|
import io.element.android.tests.testutils.simulateLongTask
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
|
@ -36,6 +37,14 @@ class FakeSpaceService(
|
||||||
_topLevelSpacesFlow.emit(value)
|
_topLevelSpacesFlow.emit(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val _spaceServiceFiltersFlow = MutableSharedFlow<List<SpaceServiceFilter>>()
|
||||||
|
override val spaceFiltersFlow: SharedFlow<List<SpaceServiceFilter>>
|
||||||
|
get() = _spaceServiceFiltersFlow.asSharedFlow()
|
||||||
|
|
||||||
|
suspend fun emitSpaceFilters(value: List<SpaceServiceFilter>) {
|
||||||
|
_spaceServiceFiltersFlow.emit(value)
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun joinedParents(spaceId: RoomId): Result<List<SpaceRoom>> {
|
override suspend fun joinedParents(spaceId: RoomId): Result<List<SpaceRoom>> {
|
||||||
return joinedParentsResult(spaceId)
|
return joinedParentsResult(spaceId)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue