Merge branch 'develop' into feature/fga/space_manage_rooms
This commit is contained in:
commit
bb082191e4
495 changed files with 5979 additions and 3199 deletions
|
|
@ -203,6 +203,7 @@ class RustMatrixClient(
|
|||
roomMembershipObserver = roomMembershipObserver,
|
||||
sessionCoroutineScope = sessionCoroutineScope,
|
||||
sessionDispatcher = sessionDispatcher,
|
||||
analyticsService = analyticsService,
|
||||
)
|
||||
|
||||
override val sessionVerificationService = RustSessionVerificationService(
|
||||
|
|
@ -393,6 +394,7 @@ class RustMatrixClient(
|
|||
joinRuleOverride = createRoomParams.joinRuleOverride?.map(),
|
||||
historyVisibilityOverride = createRoomParams.historyVisibilityOverride?.map(),
|
||||
canonicalAlias = createRoomParams.roomAliasName.getOrNull(),
|
||||
isSpace = createRoomParams.isSpace,
|
||||
)
|
||||
val roomId = RoomId(innerClient.createRoom(rustParams))
|
||||
// Wait to receive the room back from the sync but do not returns failure if it fails.
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ internal class RoomListFactory(
|
|||
val loadingStateFlow: MutableStateFlow<RoomList.LoadingState> = MutableStateFlow(RoomList.LoadingState.NotLoaded)
|
||||
val filteredSummariesFlow = MutableSharedFlow<List<RoomSummary>>(replay = 1, extraBufferCapacity = 1)
|
||||
val summariesFlow = MutableSharedFlow<List<RoomSummary>>(replay = 1, extraBufferCapacity = 1)
|
||||
val processor = RoomSummaryListProcessor(summariesFlow, innerRoomListService, coroutineContext, roomSummaryFactory)
|
||||
val processor = RoomSummaryListProcessor(summariesFlow, innerRoomListService, coroutineContext, roomSummaryFactory, analyticsService)
|
||||
// Makes sure we don't miss any events
|
||||
val dynamicEvents = MutableSharedFlow<RoomListDynamicEvents>(replay = 100)
|
||||
val currentFilter = MutableStateFlow(initialFilter)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
package io.element.android.libraries.matrix.impl.roomlist
|
||||
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||
import io.element.android.services.analytics.api.AnalyticsService
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
|
|
@ -18,6 +19,7 @@ import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate
|
|||
import org.matrix.rustcomponents.sdk.RoomListServiceInterface
|
||||
import org.matrix.rustcomponents.sdk.use
|
||||
import timber.log.Timber
|
||||
import kotlin.collections.groupingBy
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class RoomSummaryListProcessor(
|
||||
|
|
@ -25,26 +27,21 @@ class RoomSummaryListProcessor(
|
|||
private val roomListService: RoomListServiceInterface,
|
||||
private val coroutineContext: CoroutineContext,
|
||||
private val roomSummaryFactory: RoomSummaryFactory,
|
||||
private val analyticsService: AnalyticsService,
|
||||
) {
|
||||
private val mutex = Mutex()
|
||||
|
||||
suspend fun postUpdate(updates: List<RoomListEntriesUpdate>) {
|
||||
updateRoomSummaries {
|
||||
updateRoomSummaries(updates) {
|
||||
Timber.v("Update rooms from postUpdates (with ${updates.size} items) on ${Thread.currentThread()}")
|
||||
updates.forEach { update ->
|
||||
applyUpdate(update)
|
||||
}
|
||||
|
||||
// TODO remove once https://github.com/element-hq/element-x-android/issues/5031 has been confirmed as fixed
|
||||
val duplicates = groupingBy { it.roomId }.eachCount().filter { it.value > 1 }
|
||||
if (duplicates.isNotEmpty()) {
|
||||
Timber.e("Found duplicates in room summaries after a list update from the SDK: $duplicates. Updates: $updates")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun rebuildRoomSummaries() {
|
||||
updateRoomSummaries {
|
||||
updateRoomSummaries(emptyList()) {
|
||||
forEachIndexed { i, summary ->
|
||||
val result = buildRoomSummaryForIdentifier(summary.roomId.value)
|
||||
if (result != null) {
|
||||
|
|
@ -112,12 +109,32 @@ class RoomSummaryListProcessor(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun updateRoomSummaries(block: suspend MutableList<RoomSummary>.() -> Unit) = withContext(coroutineContext) {
|
||||
private suspend fun updateRoomSummaries(updates: List<RoomListEntriesUpdate>, block: suspend MutableList<RoomSummary>.() -> Unit) = withContext(
|
||||
coroutineContext
|
||||
) {
|
||||
mutex.withLock {
|
||||
val current = roomSummaries.replayCache.lastOrNull()
|
||||
val mutableRoomSummaries = current.orEmpty().toMutableList()
|
||||
block(mutableRoomSummaries)
|
||||
roomSummaries.emit(mutableRoomSummaries)
|
||||
|
||||
// TODO remove once https://github.com/element-hq/element-x-android/issues/5031 has been confirmed as fixed
|
||||
val uniqueRooms = mutableRoomSummaries.distinctBy { it.roomId }
|
||||
|
||||
if (uniqueRooms.size != mutableRoomSummaries.size) {
|
||||
val duplicates = mutableRoomSummaries.groupingBy { it.roomId }.eachCount().filter { it.value > 1 }
|
||||
if (duplicates.isNotEmpty()) {
|
||||
analyticsService.trackError(
|
||||
IllegalStateException(
|
||||
"Found duplicates in room summaries after a list update from the SDK: $duplicates. " +
|
||||
"Updates: ${updates.description()}"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
roomSummaries.emit(uniqueRooms)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<RoomListEntriesUpdate>.description(): String = joinToString { it.describe() }
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ 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 io.element.android.services.analytics.api.AnalyticsService
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
|
|
@ -32,6 +33,7 @@ class RustSpaceRoomList(
|
|||
private val innerProvider: suspend () -> InnerSpaceRoomList,
|
||||
private val coroutineScope: CoroutineScope,
|
||||
spaceRoomMapper: SpaceRoomMapper,
|
||||
private val analyticsService: AnalyticsService,
|
||||
) : SpaceRoomList {
|
||||
private val innerCompletable = CompletableDeferred<InnerSpaceRoomList>()
|
||||
|
||||
|
|
@ -43,7 +45,8 @@ class RustSpaceRoomList(
|
|||
MutableStateFlow(SpaceRoomList.PaginationStatus.Idle(hasMoreToLoad = false))
|
||||
private val spaceListUpdateProcessor = SpaceListUpdateProcessor(
|
||||
spaceRoomsFlow = spaceRoomsFlow,
|
||||
mapper = spaceRoomMapper
|
||||
mapper = spaceRoomMapper,
|
||||
analyticsService = analyticsService,
|
||||
)
|
||||
|
||||
init {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ 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.impl.util.cancelAndDestroy
|
||||
import io.element.android.services.analytics.api.AnalyticsService
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
|
|
@ -41,23 +42,40 @@ class RustSpaceService(
|
|||
private val sessionCoroutineScope: CoroutineScope,
|
||||
private val sessionDispatcher: CoroutineDispatcher,
|
||||
private val roomMembershipObserver: RoomMembershipObserver,
|
||||
private val analyticsService: AnalyticsService,
|
||||
) : SpaceService {
|
||||
private val spaceRoomMapper = SpaceRoomMapper()
|
||||
override val spaceRoomsFlow = MutableSharedFlow<List<SpaceRoom>>(replay = 1, extraBufferCapacity = 1)
|
||||
private val spaceListUpdateProcessor = SpaceListUpdateProcessor(
|
||||
spaceRoomsFlow = spaceRoomsFlow,
|
||||
mapper = spaceRoomMapper
|
||||
mapper = spaceRoomMapper,
|
||||
analyticsService = analyticsService,
|
||||
)
|
||||
|
||||
override suspend fun joinedSpaces(): Result<List<SpaceRoom>> = withContext(sessionDispatcher) {
|
||||
runCatchingExceptions {
|
||||
innerSpaceService.topLevelJoinedSpaces()
|
||||
.map {
|
||||
it.let(spaceRoomMapper::map)
|
||||
}
|
||||
innerSpaceService
|
||||
.topLevelJoinedSpaces()
|
||||
.map(spaceRoomMapper::map)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun joinedParents(spaceId: RoomId): Result<List<SpaceRoom>> = withContext(sessionDispatcher) {
|
||||
runCatchingExceptions {
|
||||
innerSpaceService
|
||||
.joinedParentsOfChild(spaceId.value)
|
||||
.map(spaceRoomMapper::map)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getSpaceRoom(spaceId: RoomId): SpaceRoom? = withContext(sessionDispatcher) {
|
||||
runCatchingExceptions {
|
||||
innerSpaceService.getSpaceRoom(spaceId.value)?.let { spaceRoom ->
|
||||
spaceRoomMapper.map(spaceRoom)
|
||||
}
|
||||
}.getOrNull()
|
||||
}
|
||||
|
||||
override fun spaceRoomList(id: RoomId): SpaceRoomList {
|
||||
val childCoroutineScope = sessionCoroutineScope.childScope(sessionDispatcher, "SpaceRoomListScope-$this")
|
||||
return RustSpaceRoomList(
|
||||
|
|
@ -65,6 +83,7 @@ class RustSpaceService(
|
|||
innerProvider = { innerSpaceService.spaceRoomList(id.value) },
|
||||
coroutineScope = childCoroutineScope,
|
||||
spaceRoomMapper = spaceRoomMapper,
|
||||
analyticsService = analyticsService,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
package io.element.android.libraries.matrix.impl.spaces
|
||||
|
||||
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
|
||||
import io.element.android.services.analytics.api.AnalyticsService
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
|
|
@ -19,17 +20,18 @@ import timber.log.Timber
|
|||
internal class SpaceListUpdateProcessor(
|
||||
private val spaceRoomsFlow: MutableSharedFlow<List<SpaceRoom>>,
|
||||
private val mapper: SpaceRoomMapper,
|
||||
private val analyticsService: AnalyticsService,
|
||||
) {
|
||||
private val mutex = Mutex()
|
||||
|
||||
suspend fun postUpdates(updates: List<SpaceListUpdate>) {
|
||||
Timber.v("Update space rooms from postUpdates (with ${updates.size} items) on ${Thread.currentThread()}")
|
||||
updateSpaceRooms {
|
||||
updateSpaceRooms(updates) {
|
||||
updates.forEach { update -> applyUpdate(update) }
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun updateSpaceRooms(block: MutableList<SpaceRoom>.() -> Unit) =
|
||||
private suspend fun updateSpaceRooms(updates: List<SpaceListUpdate>, block: MutableList<SpaceRoom>.() -> Unit) =
|
||||
mutex.withLock {
|
||||
val spaceRooms = if (spaceRoomsFlow.replayCache.isNotEmpty()) {
|
||||
spaceRoomsFlow.first().toMutableList()
|
||||
|
|
@ -37,7 +39,17 @@ internal class SpaceListUpdateProcessor(
|
|||
mutableListOf()
|
||||
}
|
||||
block(spaceRooms)
|
||||
spaceRoomsFlow.emit(spaceRooms)
|
||||
val uniqueRooms = spaceRooms.distinctBy { it.roomId }
|
||||
|
||||
// TODO remove once https://github.com/element-hq/element-x-android/issues/5031 has been confirmed as fixed
|
||||
if (spaceRooms.size != uniqueRooms.size) {
|
||||
val duplicateKeys = spaceRooms.groupBy { it.roomId }.filter { it.value.size > 1 }.keys
|
||||
analyticsService.trackError(
|
||||
IllegalStateException("Found duplicate keys in space rooms list ($duplicateKeys) after SDK updates: ${updates.description()}")
|
||||
)
|
||||
}
|
||||
|
||||
spaceRoomsFlow.emit(uniqueRooms)
|
||||
}
|
||||
|
||||
private fun MutableList<SpaceRoom>.applyUpdate(update: SpaceListUpdate) {
|
||||
|
|
@ -83,3 +95,19 @@ internal class SpaceListUpdateProcessor(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<SpaceListUpdate>.description(): String = joinToString { it.description() }
|
||||
|
||||
private fun SpaceListUpdate.description(): String = when (this) {
|
||||
is SpaceListUpdate.Append -> "Append(${values.map { it.roomId }})"
|
||||
SpaceListUpdate.Clear -> "Clear"
|
||||
is SpaceListUpdate.Insert -> "Insert($index, ${value.roomId})"
|
||||
SpaceListUpdate.PopBack -> "PopBack"
|
||||
SpaceListUpdate.PopFront -> "PopFront"
|
||||
is SpaceListUpdate.PushBack -> "PushBack(${value.roomId})"
|
||||
is SpaceListUpdate.PushFront -> "PushFront(${value.roomId})"
|
||||
is SpaceListUpdate.Remove -> "Remove($index)"
|
||||
is SpaceListUpdate.Reset -> "Reset(${values.map { it.roomId }})"
|
||||
is SpaceListUpdate.Set -> "Set($index, ${value.roomId})"
|
||||
is SpaceListUpdate.Truncate -> "Truncate($length)"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,9 @@ import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiRoomListSe
|
|||
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.matrix.test.A_ROOM_ID_3
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID_4
|
||||
import io.element.android.libraries.matrix.test.room.aRoomSummary
|
||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
|
|
@ -33,11 +35,10 @@ class RoomSummaryListProcessorTest {
|
|||
summaries.value = listOf(aRoomSummary())
|
||||
val processor = createProcessor()
|
||||
|
||||
val newEntry = aRustRoom(A_ROOM_ID_2)
|
||||
processor.postUpdate(listOf(RoomListEntriesUpdate.Append(listOf(newEntry, newEntry, newEntry))))
|
||||
processor.postUpdate(listOf(RoomListEntriesUpdate.Append(listOf(aRustRoom(A_ROOM_ID_2), aRustRoom(A_ROOM_ID_3), aRustRoom(A_ROOM_ID_4)))))
|
||||
|
||||
assertThat(summaries.value.count()).isEqualTo(4)
|
||||
assertThat(summaries.value.subList(1, 4).all { it.roomId == A_ROOM_ID_2 }).isTrue()
|
||||
assertThat(summaries.value.subList(1, 4).map { it.roomId }).isEqualTo(listOf(A_ROOM_ID_2, A_ROOM_ID_3, A_ROOM_ID_4))
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -182,5 +183,6 @@ class RoomSummaryListProcessorTest {
|
|||
FakeFfiRoomListService(),
|
||||
coroutineContext = StandardTestDispatcher(testScheduler),
|
||||
roomSummaryFactory = RoomSummaryFactory(),
|
||||
analyticsService = FakeAnalyticsService(),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,9 @@ import io.element.android.libraries.matrix.impl.fixtures.factories.aRustSpaceRoo
|
|||
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.matrix.test.A_ROOM_ID_3
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID_4
|
||||
import io.element.android.libraries.previewutils.room.aSpaceRoom
|
||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.test.runTest
|
||||
|
|
@ -29,11 +31,14 @@ class RoomSummaryListProcessorTest {
|
|||
spaceRoomsFlow.value = listOf(aSpaceRoom())
|
||||
val processor = createProcessor()
|
||||
|
||||
val newEntry = aRustSpaceRoom(roomId = A_ROOM_ID_2)
|
||||
processor.postUpdates(listOf(SpaceListUpdate.Append(listOf(newEntry, newEntry, newEntry))))
|
||||
processor.postUpdates(
|
||||
listOf(
|
||||
SpaceListUpdate.Append(listOf(aRustSpaceRoom(roomId = A_ROOM_ID_2), aRustSpaceRoom(roomId = A_ROOM_ID_3), aRustSpaceRoom(roomId = A_ROOM_ID_4)))
|
||||
)
|
||||
)
|
||||
|
||||
assertThat(spaceRoomsFlow.value.count()).isEqualTo(4)
|
||||
assertThat(spaceRoomsFlow.value.subList(1, 4).all { it.roomId == A_ROOM_ID_2 }).isTrue()
|
||||
assertThat(spaceRoomsFlow.value.subList(1, 4).map { it.roomId }).isEqualTo(listOf(A_ROOM_ID_2, A_ROOM_ID_3, A_ROOM_ID_4))
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -186,5 +191,6 @@ class RoomSummaryListProcessorTest {
|
|||
) = SpaceListUpdateProcessor(
|
||||
spaceRoomsFlow = spaceRoomsFlow,
|
||||
mapper = SpaceRoomMapper(),
|
||||
analyticsService = FakeAnalyticsService(),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import io.element.android.libraries.matrix.impl.fixtures.factories.aRustSpaceRoo
|
|||
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.services.analytics.test.FakeAnalyticsService
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
|
|
@ -97,6 +98,7 @@ class RustSpaceRoomListTest {
|
|||
innerProvider = innerProvider,
|
||||
coroutineScope = backgroundScope,
|
||||
spaceRoomMapper = spaceRoomMapper,
|
||||
analyticsService = FakeAnalyticsService(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue