Spaces : expose new SpaceServiceFilter

This commit is contained in:
ganfra
2026-01-30 17:37:54 +01:00
parent 2132b65bf6
commit 9d576394ac
6 changed files with 179 additions and 0 deletions

View File

@@ -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.SpaceRoomList
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.services.analytics.api.AnalyticsService
import kotlinx.coroutines.CoroutineDispatcher
@@ -31,9 +32,11 @@ import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.withContext
import org.matrix.rustcomponents.sdk.SpaceFilterUpdate
import org.matrix.rustcomponents.sdk.SpaceListUpdate
import org.matrix.rustcomponents.sdk.SpaceServiceInterface
import org.matrix.rustcomponents.sdk.SpaceServiceJoinedSpacesListener
import org.matrix.rustcomponents.sdk.SpaceServiceSpaceFiltersListener
import timber.log.Timber
import org.matrix.rustcomponents.sdk.SpaceService as ClientSpaceService
@@ -45,6 +48,8 @@ class RustSpaceService(
private val analyticsService: AnalyticsService,
) : SpaceService {
private val spaceRoomMapper = SpaceRoomMapper()
private val spaceFilterMapper = SpaceServiceFilterMapper(spaceRoomMapper)
override val topLevelSpacesFlow = MutableSharedFlow<List<SpaceRoom>>(replay = 1, extraBufferCapacity = 1)
private val spaceListUpdateProcessor = SpaceListUpdateProcessor(
spaceRoomsFlow = topLevelSpacesFlow,
@@ -52,6 +57,12 @@ class RustSpaceService(
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) {
runCatchingExceptions {
innerSpaceService
@@ -115,6 +126,13 @@ class RustSpaceService(
spaceListUpdateProcessor.postUpdates(updates)
}
.launchIn(sessionCoroutineScope)
innerSpaceService
.spaceFilterListUpdate()
.onEach { updates ->
spaceFilterUpdateProcessor.postUpdates(updates)
}
.launchIn(sessionCoroutineScope)
}
}
@@ -134,3 +152,20 @@ internal fun SpaceServiceInterface.spaceListUpdate(): Flow<List<SpaceListUpdate>
}.catch {
Timber.d(it, "spaceDiffFlow() failed")
}.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)

View File

@@ -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) },
)
}
}

View File

@@ -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()
}
}
}
}