Merge branch 'develop' into separate_import_error
This commit is contained in:
commit
700ea331fe
679 changed files with 12323 additions and 1753 deletions
|
|
@ -23,13 +23,8 @@ import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
|
|||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.createroom.CreateRoomParameters
|
||||
import io.element.android.libraries.matrix.api.createroom.RoomPreset
|
||||
import io.element.android.libraries.matrix.api.encryption.EncryptionService
|
||||
import io.element.android.libraries.matrix.api.media.MatrixMediaLoader
|
||||
import io.element.android.libraries.matrix.api.media.MediaPreviewService
|
||||
import io.element.android.libraries.matrix.api.notification.NotificationService
|
||||
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
|
||||
import io.element.android.libraries.matrix.api.oidc.AccountManagementAction
|
||||
import io.element.android.libraries.matrix.api.pusher.PushersService
|
||||
import io.element.android.libraries.matrix.api.room.BaseRoom
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||
|
|
@ -39,16 +34,13 @@ import io.element.android.libraries.matrix.api.room.RoomMember
|
|||
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
||||
import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias
|
||||
import io.element.android.libraries.matrix.api.room.join.JoinRule
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryService
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomVisibility
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
||||
import io.element.android.libraries.matrix.api.spaces.SpaceService
|
||||
import io.element.android.libraries.matrix.api.sync.SlidingSyncVersion
|
||||
import io.element.android.libraries.matrix.api.sync.SyncService
|
||||
import io.element.android.libraries.matrix.api.sync.SyncState
|
||||
import io.element.android.libraries.matrix.api.user.MatrixSearchUserResults
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
||||
import io.element.android.libraries.matrix.impl.encryption.RustEncryptionService
|
||||
import io.element.android.libraries.matrix.impl.exception.mapClientException
|
||||
import io.element.android.libraries.matrix.impl.mapper.map
|
||||
|
|
@ -128,11 +120,10 @@ import org.matrix.rustcomponents.sdk.SyncService as ClientSyncService
|
|||
|
||||
class RustMatrixClient(
|
||||
private val innerClient: Client,
|
||||
private val baseDirectory: File,
|
||||
private val sessionStore: SessionStore,
|
||||
private val appCoroutineScope: CoroutineScope,
|
||||
private val sessionDelegate: RustClientSessionDelegate,
|
||||
private val innerSyncService: ClientSyncService,
|
||||
appCoroutineScope: CoroutineScope,
|
||||
dispatchers: CoroutineDispatchers,
|
||||
baseCacheDirectory: File,
|
||||
clock: SystemClock,
|
||||
|
|
@ -147,27 +138,29 @@ class RustMatrixClient(
|
|||
private val innerRoomListService = innerSyncService.roomListService()
|
||||
private val innerSpaceService = innerClient.spaceService()
|
||||
|
||||
private val rustSyncService = RustSyncService(
|
||||
override val roomMembershipObserver = RoomMembershipObserver()
|
||||
|
||||
override val syncService = RustSyncService(
|
||||
inner = innerSyncService,
|
||||
dispatcher = sessionDispatcher,
|
||||
sessionCoroutineScope = sessionCoroutineScope
|
||||
)
|
||||
private val pushersService = RustPushersService(
|
||||
override val pushersService = RustPushersService(
|
||||
client = innerClient,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
private val notificationProcessSetup = NotificationProcessSetup.SingleProcess(innerSyncService)
|
||||
private val innerNotificationClient = runBlocking { innerClient.notificationClient(notificationProcessSetup) }
|
||||
private val notificationService = RustNotificationService(sessionId, innerNotificationClient, dispatchers, clock)
|
||||
private val notificationSettingsService = RustNotificationSettingsService(innerClient, sessionCoroutineScope, dispatchers)
|
||||
private val encryptionService = RustEncryptionService(
|
||||
override val notificationService = RustNotificationService(sessionId, innerNotificationClient, dispatchers, clock)
|
||||
override val notificationSettingsService = RustNotificationSettingsService(innerClient, sessionCoroutineScope, dispatchers)
|
||||
override val encryptionService = RustEncryptionService(
|
||||
client = innerClient,
|
||||
syncService = rustSyncService,
|
||||
syncService = syncService,
|
||||
sessionCoroutineScope = sessionCoroutineScope,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
|
||||
private val roomDirectoryService = RustRoomDirectoryService(
|
||||
override val roomDirectoryService = RustRoomDirectoryService(
|
||||
client = innerClient,
|
||||
sessionDispatcher = sessionDispatcher,
|
||||
)
|
||||
|
|
@ -189,18 +182,19 @@ class RustMatrixClient(
|
|||
|
||||
override val spaceService: SpaceService = RustSpaceService(
|
||||
innerSpaceService = innerSpaceService,
|
||||
roomMembershipObserver = roomMembershipObserver,
|
||||
sessionCoroutineScope = sessionCoroutineScope,
|
||||
sessionDispatcher = sessionDispatcher,
|
||||
)
|
||||
|
||||
private val verificationService = RustSessionVerificationService(
|
||||
override val sessionVerificationService = RustSessionVerificationService(
|
||||
client = innerClient,
|
||||
isSyncServiceReady = rustSyncService.syncState.map { it == SyncState.Running },
|
||||
isSyncServiceReady = syncService.syncState.map { it == SyncState.Running },
|
||||
sessionCoroutineScope = sessionCoroutineScope,
|
||||
)
|
||||
|
||||
private val roomInfoMapper = RoomInfoMapper()
|
||||
private val roomMembershipObserver = RoomMembershipObserver()
|
||||
|
||||
private val roomFactory = RustRoomFactory(
|
||||
roomListService = roomListService,
|
||||
innerRoomListService = innerRoomListService,
|
||||
|
|
@ -218,13 +212,13 @@ class RustMatrixClient(
|
|||
featureFlagService = featureFlagService,
|
||||
)
|
||||
|
||||
override val mediaLoader: MatrixMediaLoader = RustMediaLoader(
|
||||
override val matrixMediaLoader: MatrixMediaLoader = RustMediaLoader(
|
||||
baseCacheDirectory = baseCacheDirectory,
|
||||
dispatchers = dispatchers,
|
||||
innerClient = innerClient,
|
||||
)
|
||||
|
||||
private val mediaPreviewService = RustMediaPreviewService(
|
||||
override val mediaPreviewService = RustMediaPreviewService(
|
||||
sessionCoroutineScope = sessionCoroutineScope,
|
||||
innerClient = innerClient,
|
||||
sessionDispatcher = sessionDispatcher,
|
||||
|
|
@ -535,33 +529,17 @@ class RustMatrixClient(
|
|||
}.mapFailure { it.mapClientException() }
|
||||
}
|
||||
|
||||
override fun syncService(): SyncService = rustSyncService
|
||||
|
||||
override fun sessionVerificationService(): SessionVerificationService = verificationService
|
||||
|
||||
override fun pushersService(): PushersService = pushersService
|
||||
|
||||
override fun notificationService(): NotificationService = notificationService
|
||||
|
||||
override fun encryptionService(): EncryptionService = encryptionService
|
||||
|
||||
override fun notificationSettingsService(): NotificationSettingsService = notificationSettingsService
|
||||
|
||||
override fun roomDirectoryService(): RoomDirectoryService = roomDirectoryService
|
||||
|
||||
override fun mediaPreviewService(): MediaPreviewService = mediaPreviewService
|
||||
|
||||
internal suspend fun destroy() {
|
||||
innerNotificationClient.close()
|
||||
|
||||
roomFactory.destroy()
|
||||
rustSyncService.destroy()
|
||||
syncService.destroy()
|
||||
notificationSettingsService.destroy()
|
||||
notificationProcessSetup.destroy()
|
||||
|
||||
sessionCoroutineScope.cancel()
|
||||
clientDelegateTaskHandle?.cancelAndDestroy()
|
||||
verificationService.destroy()
|
||||
sessionVerificationService.destroy()
|
||||
|
||||
sessionDelegate.clearCurrentClient()
|
||||
innerRoomListService.close()
|
||||
|
|
@ -572,7 +550,7 @@ class RustMatrixClient(
|
|||
}
|
||||
|
||||
override suspend fun getCacheSize(): Long {
|
||||
return baseDirectory.getCacheSize()
|
||||
return getCacheSize(includeCryptoDb = false)
|
||||
}
|
||||
|
||||
override suspend fun clearCache() {
|
||||
|
|
@ -671,8 +649,6 @@ class RustMatrixClient(
|
|||
}
|
||||
}
|
||||
|
||||
override fun roomMembershipObserver(): RoomMembershipObserver = roomMembershipObserver
|
||||
|
||||
override fun getRoomInfoFlow(roomId: RoomId): Flow<Optional<RoomInfo>> {
|
||||
return mxCallbackFlow {
|
||||
val roomNotFound = innerRoomListService.roomOrNull(roomId.value).use { it == null }
|
||||
|
|
@ -737,7 +713,7 @@ class RustMatrixClient(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun File.getCacheSize(
|
||||
private suspend fun getCacheSize(
|
||||
includeCryptoDb: Boolean = false,
|
||||
): Long = withContext(sessionDispatcher) {
|
||||
val sessionDirectory = sessionPathsProvider.provides(sessionId) ?: return@withContext 0L
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ package io.element.android.libraries.matrix.impl
|
|||
|
||||
import dev.zacsweers.metro.Inject
|
||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
import io.element.android.libraries.di.BaseDirectory
|
||||
import io.element.android.libraries.di.CacheDirectory
|
||||
import io.element.android.libraries.di.annotations.AppCoroutineScope
|
||||
import io.element.android.libraries.featureflag.api.FeatureFlagService
|
||||
|
|
@ -43,7 +42,6 @@ import java.io.File
|
|||
|
||||
@Inject
|
||||
class RustMatrixClientFactory(
|
||||
@BaseDirectory private val baseDirectory: File,
|
||||
@CacheDirectory private val cacheDirectory: File,
|
||||
@AppCoroutineScope
|
||||
private val appCoroutineScope: CoroutineScope,
|
||||
|
|
@ -87,7 +85,6 @@ class RustMatrixClientFactory(
|
|||
|
||||
return RustMatrixClient(
|
||||
innerClient = client,
|
||||
baseDirectory = baseDirectory,
|
||||
sessionStore = sessionStore,
|
||||
appCoroutineScope = appCoroutineScope,
|
||||
sessionDelegate = sessionDelegate,
|
||||
|
|
@ -136,13 +133,15 @@ class RustMatrixClientFactory(
|
|||
)
|
||||
.enableShareHistoryOnInvite(featureFlagService.isFeatureEnabled(FeatureFlags.EnableKeyShareOnInvite))
|
||||
.threadsEnabled(featureFlagService.isFeatureEnabled(FeatureFlags.Threads), threadSubscriptions = false)
|
||||
.requestConfig(RequestConfig(
|
||||
timeout = 30_000uL,
|
||||
retryLimit = 0u,
|
||||
// Use default values for the rest
|
||||
maxConcurrentRequests = null,
|
||||
maxRetryTime = null,
|
||||
))
|
||||
.requestConfig(
|
||||
RequestConfig(
|
||||
timeout = 30_000uL,
|
||||
retryLimit = 0u,
|
||||
// Use default values for the rest
|
||||
maxConcurrentRequests = null,
|
||||
maxRetryTime = null,
|
||||
)
|
||||
)
|
||||
.run {
|
||||
// Apply sliding sync version settings
|
||||
when (slidingSyncType) {
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import io.element.android.libraries.matrix.api.notificationsettings.Notification
|
|||
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
||||
import io.element.android.libraries.matrix.api.roomdirectory.RoomDirectoryService
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
||||
import io.element.android.libraries.matrix.api.spaces.SpaceService
|
||||
import io.element.android.libraries.matrix.api.sync.SyncService
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
|
@ -35,17 +36,17 @@ object SessionMatrixModule {
|
|||
|
||||
@Provides
|
||||
fun providesSessionVerificationService(matrixClient: MatrixClient): SessionVerificationService {
|
||||
return matrixClient.sessionVerificationService()
|
||||
return matrixClient.sessionVerificationService
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun providesNotificationSettingsService(matrixClient: MatrixClient): NotificationSettingsService {
|
||||
return matrixClient.notificationSettingsService()
|
||||
return matrixClient.notificationSettingsService
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun provideRoomMembershipObserver(matrixClient: MatrixClient): RoomMembershipObserver {
|
||||
return matrixClient.roomMembershipObserver()
|
||||
return matrixClient.roomMembershipObserver
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
@ -55,32 +56,37 @@ object SessionMatrixModule {
|
|||
|
||||
@Provides
|
||||
fun providesSyncService(matrixClient: MatrixClient): SyncService {
|
||||
return matrixClient.syncService()
|
||||
return matrixClient.syncService
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun providesEncryptionService(matrixClient: MatrixClient): EncryptionService {
|
||||
return matrixClient.encryptionService()
|
||||
return matrixClient.encryptionService
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun provideMediaLoader(matrixClient: MatrixClient): MatrixMediaLoader {
|
||||
return matrixClient.mediaLoader
|
||||
fun providesMatrixMediaLoader(matrixClient: MatrixClient): MatrixMediaLoader {
|
||||
return matrixClient.matrixMediaLoader
|
||||
}
|
||||
|
||||
@SessionCoroutineScope
|
||||
@Provides
|
||||
fun provideSessionCoroutineScope(matrixClient: MatrixClient): CoroutineScope {
|
||||
fun providesSessionCoroutineScope(matrixClient: MatrixClient): CoroutineScope {
|
||||
return matrixClient.sessionCoroutineScope
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun providesRoomDirectoryService(matrixClient: MatrixClient): RoomDirectoryService {
|
||||
return matrixClient.roomDirectoryService()
|
||||
return matrixClient.roomDirectoryService
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun providesMediaPreviewService(matrixClient: MatrixClient): MediaPreviewService {
|
||||
return matrixClient.mediaPreviewService()
|
||||
return matrixClient.mediaPreviewService
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun providesSpaceService(matrixClient: MatrixClient): SpaceService {
|
||||
return matrixClient.spaceService
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ fun Throwable.mapRecoveryException(): RecoveryException {
|
|||
message = errorMessage
|
||||
)
|
||||
is RustRecoveryException.BackupExistsOnServer -> RecoveryException.BackupExistsOnServer
|
||||
is RustRecoveryException.Import -> RecoveryException.Import(
|
||||
message = errorMessage
|
||||
)
|
||||
is RustRecoveryException.Client -> RecoveryException.Client(
|
||||
source.mapClientException()
|
||||
)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import io.element.android.libraries.matrix.api.encryption.IdentityResetHandle
|
|||
import io.element.android.libraries.matrix.api.encryption.RecoveryState
|
||||
import io.element.android.libraries.matrix.api.encryption.identity.IdentityState
|
||||
import io.element.android.libraries.matrix.api.sync.SyncState
|
||||
import io.element.android.libraries.matrix.impl.exception.mapClientException
|
||||
import io.element.android.libraries.matrix.impl.sync.RustSyncService
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
|
|
@ -46,7 +47,7 @@ import org.matrix.rustcomponents.sdk.EnableRecoveryProgress as RustEnableRecover
|
|||
import org.matrix.rustcomponents.sdk.SteadyStateException as RustSteadyStateException
|
||||
import org.matrix.rustcomponents.sdk.RecoveryException as RustRecoveryException
|
||||
|
||||
internal class RustEncryptionService(
|
||||
class RustEncryptionService(
|
||||
client: Client,
|
||||
syncService: RustSyncService,
|
||||
sessionCoroutineScope: CoroutineScope,
|
||||
|
|
@ -97,6 +98,20 @@ internal class RustEncryptionService(
|
|||
}
|
||||
.stateIn(sessionCoroutineScope, SharingStarted.Eagerly, false)
|
||||
|
||||
/**
|
||||
* Check if the user has any devices available to verify against every 5 seconds.
|
||||
* TODO This is a temporary workaround, when we will have a way to observe
|
||||
* the sessions, this code will have to be updated.
|
||||
*/
|
||||
override val hasDevicesToVerifyAgainst: StateFlow<Boolean> = flow {
|
||||
while (currentCoroutineContext().isActive) {
|
||||
val result = hasDevicesToVerifyAgainst().getOrDefault(false)
|
||||
emit(result)
|
||||
delay(5_000)
|
||||
}
|
||||
}
|
||||
.stateIn(sessionCoroutineScope, SharingStarted.Eagerly, false)
|
||||
|
||||
override suspend fun enableBackups(): Result<Unit> = withContext(dispatchers.io) {
|
||||
runCatchingExceptions {
|
||||
service.enableBackups()
|
||||
|
|
@ -172,6 +187,14 @@ internal class RustEncryptionService(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun hasDevicesToVerifyAgainst(): Result<Boolean> = withContext(dispatchers.io) {
|
||||
runCatchingExceptions {
|
||||
service.hasDevicesToVerifyAgainst()
|
||||
}.mapFailure {
|
||||
it.mapClientException()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun resetRecoveryKey(): Result<String> = withContext(dispatchers.io) {
|
||||
runCatchingExceptions {
|
||||
service.resetRecoveryKey()
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ import org.matrix.rustcomponents.sdk.RoomList as InnerRoomList
|
|||
|
||||
private val ROOM_LIST_RUST_FILTERS = listOf(
|
||||
RoomListEntriesDynamicFilterKind.NonLeft,
|
||||
RoomListEntriesDynamicFilterKind.NonSpace,
|
||||
RoomListEntriesDynamicFilterKind.DeduplicateVersions
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -15,41 +15,57 @@ import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
|||
|
||||
val RoomListFilter.predicate
|
||||
get() = when (this) {
|
||||
is RoomListFilter.All -> { _: RoomSummary -> true }
|
||||
is RoomListFilter.Any -> { _: RoomSummary -> true }
|
||||
RoomListFilter.None -> { _: RoomSummary -> false }
|
||||
is RoomListFilter.All -> { roomSummary -> NonSpacePredicate(roomSummary) || IsInvitedPredicate(roomSummary) }
|
||||
is RoomListFilter.Any -> { roomSummary -> NonSpacePredicate(roomSummary) || IsInvitedPredicate(roomSummary) }
|
||||
RoomListFilter.None -> { _ -> false }
|
||||
RoomListFilter.Category.Group -> { roomSummary: RoomSummary ->
|
||||
!roomSummary.info.isDm && !roomSummary.isInvited()
|
||||
!roomSummary.info.isDm && NonInvitedPredicate(roomSummary) && NonSpacePredicate(roomSummary)
|
||||
}
|
||||
RoomListFilter.Category.People -> { roomSummary: RoomSummary ->
|
||||
roomSummary.info.isDm && !roomSummary.isInvited()
|
||||
roomSummary.info.isDm && NonInvitedPredicate(roomSummary) && NonSpacePredicate(roomSummary)
|
||||
}
|
||||
RoomListFilter.Category.Space -> IsSpacePredicate
|
||||
RoomListFilter.Favorite -> { roomSummary: RoomSummary ->
|
||||
roomSummary.info.isFavorite && !roomSummary.isInvited()
|
||||
roomSummary.info.isFavorite && NonInvitedPredicate(roomSummary) && NonSpacePredicate(roomSummary)
|
||||
}
|
||||
RoomListFilter.Unread -> { roomSummary: RoomSummary ->
|
||||
!roomSummary.isInvited() && (roomSummary.info.numUnreadNotifications > 0 || roomSummary.info.isMarkedUnread)
|
||||
NonInvitedPredicate(roomSummary) &&
|
||||
NonSpacePredicate(roomSummary) &&
|
||||
(roomSummary.info.numUnreadNotifications > 0 || roomSummary.info.isMarkedUnread)
|
||||
}
|
||||
is RoomListFilter.NormalizedMatchRoomName -> { roomSummary: RoomSummary ->
|
||||
roomSummary.info.name?.withoutAccents().orEmpty().contains(normalizedPattern, ignoreCase = true)
|
||||
}
|
||||
RoomListFilter.Invite -> { roomSummary: RoomSummary ->
|
||||
roomSummary.isInvited()
|
||||
roomSummary.info.name?.withoutAccents().orEmpty().contains(normalizedPattern, ignoreCase = true) &&
|
||||
(NonSpacePredicate(roomSummary) || IsInvitedPredicate(roomSummary))
|
||||
}
|
||||
RoomListFilter.Invite -> IsInvitedPredicate
|
||||
}
|
||||
|
||||
fun List<RoomSummary>.filter(filter: RoomListFilter): List<RoomSummary> {
|
||||
return when (filter) {
|
||||
is RoomListFilter.All -> {
|
||||
val predicates = filter.filters.map { it.predicate }
|
||||
val predicates = if (filter.filters.isNotEmpty()) {
|
||||
filter.filters.map { it.predicate }
|
||||
} else {
|
||||
listOf(filter.predicate)
|
||||
}
|
||||
filter { roomSummary -> predicates.all { it(roomSummary) } }
|
||||
}
|
||||
is RoomListFilter.Any -> {
|
||||
val predicates = filter.filters.map { it.predicate }
|
||||
val predicates = if (filter.filters.isNotEmpty()) {
|
||||
filter.filters.map { it.predicate }
|
||||
} else {
|
||||
listOf(filter.predicate)
|
||||
}
|
||||
filter { roomSummary -> predicates.any { it(roomSummary) } }
|
||||
}
|
||||
else -> filter(filter.predicate)
|
||||
}
|
||||
}
|
||||
|
||||
private fun RoomSummary.isInvited() = info.currentUserMembership == CurrentUserMembership.INVITED
|
||||
private val IsSpacePredicate = { roomSummary: RoomSummary -> roomSummary.info.isSpace }
|
||||
|
||||
private val NonSpacePredicate = { roomSummary: RoomSummary -> !IsSpacePredicate(roomSummary) }
|
||||
|
||||
private val IsInvitedPredicate = { roomSummary: RoomSummary -> roomSummary.info.currentUserMembership == CurrentUserMembership.INVITED }
|
||||
|
||||
private val NonInvitedPredicate = { roomSummary: RoomSummary -> !IsInvitedPredicate(roomSummary) }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* 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.extensions.runCatchingExceptions
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
||||
import io.element.android.libraries.matrix.api.spaces.LeaveSpaceHandle
|
||||
import io.element.android.libraries.matrix.api.spaces.LeaveSpaceRoom
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import org.matrix.rustcomponents.sdk.LeaveSpaceHandle as RustLeaveSpaceHandle
|
||||
|
||||
class RustLeaveSpaceHandle(
|
||||
override val id: RoomId,
|
||||
private val spaceRoomMapper: SpaceRoomMapper,
|
||||
private val roomMembershipObserver: RoomMembershipObserver,
|
||||
sessionCoroutineScope: CoroutineScope,
|
||||
private val innerProvider: suspend () -> RustLeaveSpaceHandle,
|
||||
) : LeaveSpaceHandle {
|
||||
private val inner = CompletableDeferred<RustLeaveSpaceHandle>()
|
||||
|
||||
init {
|
||||
sessionCoroutineScope.launch {
|
||||
inner.complete(innerProvider())
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun rooms(): Result<List<LeaveSpaceRoom>> = runCatchingExceptions {
|
||||
inner.await().rooms().map { leaveSpaceRoom ->
|
||||
LeaveSpaceRoom(
|
||||
spaceRoom = spaceRoomMapper.map(leaveSpaceRoom.spaceRoom),
|
||||
isLastAdmin = leaveSpaceRoom.isLastAdmin,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun leave(roomIds: List<RoomId>): Result<Unit> = runCatchingExceptions {
|
||||
// Ensure the space is included and is the last room to be left
|
||||
val roomToLeave = roomIds - id + id
|
||||
inner.await().leave(roomToLeave.map { it.value })
|
||||
}.onSuccess {
|
||||
roomMembershipObserver.notifyUserLeftRoom(
|
||||
roomId = id,
|
||||
isSpace = true,
|
||||
membershipBeforeLeft = CurrentUserMembership.JOINED,
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override fun close() {
|
||||
Timber.d("Destroying LeaveSpaceHandle $id")
|
||||
try {
|
||||
inner.getCompleted().destroy()
|
||||
} catch (_: Exception) {
|
||||
// Ignore, we just want to make sure it's completed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,8 @@ 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.room.RoomMembershipObserver
|
||||
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
|
||||
|
|
@ -37,6 +39,7 @@ class RustSpaceService(
|
|||
private val innerSpaceService: ClientSpaceService,
|
||||
private val sessionCoroutineScope: CoroutineScope,
|
||||
private val sessionDispatcher: CoroutineDispatcher,
|
||||
private val roomMembershipObserver: RoomMembershipObserver,
|
||||
) : SpaceService {
|
||||
private val spaceRoomMapper = SpaceRoomMapper()
|
||||
override val spaceRoomsFlow = MutableSharedFlow<List<SpaceRoom>>(replay = 1, extraBufferCapacity = 1)
|
||||
|
|
@ -64,6 +67,17 @@ class RustSpaceService(
|
|||
)
|
||||
}
|
||||
|
||||
override fun getLeaveSpaceHandle(spaceId: RoomId): LeaveSpaceHandle {
|
||||
return RustLeaveSpaceHandle(
|
||||
id = spaceId,
|
||||
spaceRoomMapper = spaceRoomMapper,
|
||||
roomMembershipObserver = roomMembershipObserver,
|
||||
sessionCoroutineScope = sessionCoroutineScope,
|
||||
) {
|
||||
innerSpaceService.leaveSpace(spaceId.value)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
innerSpaceService
|
||||
.spaceListUpdate()
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ class SpaceRoomMapper {
|
|||
guestCanJoin = spaceRoom.guestCanJoin,
|
||||
heroes = spaceRoom.heroes.orEmpty().map { it.map() },
|
||||
joinRule = spaceRoom.joinRule?.map(),
|
||||
name = spaceRoom.name,
|
||||
rawName = spaceRoom.name,
|
||||
numJoinedMembers = spaceRoom.numJoinedMembers.toInt(),
|
||||
roomId = RoomId(spaceRoom.roomId),
|
||||
roomType = spaceRoom.roomType.map(),
|
||||
|
|
@ -32,6 +32,7 @@ class SpaceRoomMapper {
|
|||
topic = spaceRoom.topic,
|
||||
worldReadable = spaceRoom.worldReadable.orFalse(),
|
||||
via = spaceRoom.via,
|
||||
isDirect = spaceRoom.isDirect,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,14 +36,12 @@ class RustMatrixClientFactoryTest {
|
|||
}
|
||||
|
||||
fun TestScope.createRustMatrixClientFactory(
|
||||
baseDirectory: File = File("/base"),
|
||||
cacheDirectory: File = File("/cache"),
|
||||
sessionStore: SessionStore = InMemorySessionStore(
|
||||
updateUserProfileResult = { _, _, _ -> },
|
||||
),
|
||||
clientBuilderProvider: ClientBuilderProvider = FakeClientBuilderProvider(),
|
||||
) = RustMatrixClientFactory(
|
||||
baseDirectory = baseDirectory,
|
||||
cacheDirectory = cacheDirectory,
|
||||
appCoroutineScope = backgroundScope,
|
||||
coroutineDispatchers = testCoroutineDispatchers(),
|
||||
|
|
|
|||
|
|
@ -102,7 +102,6 @@ class RustMatrixClientTest {
|
|||
),
|
||||
) = RustMatrixClient(
|
||||
innerClient = client,
|
||||
baseDirectory = File(""),
|
||||
sessionStore = sessionStore,
|
||||
appCoroutineScope = backgroundScope,
|
||||
sessionDelegate = aRustClientSessionDelegate(
|
||||
|
|
|
|||
|
|
@ -53,7 +53,6 @@ class RustMatrixAuthenticationServiceTest {
|
|||
val baseDirectory = File("/base")
|
||||
val cacheDirectory = File("/cache")
|
||||
val rustMatrixClientFactory = createRustMatrixClientFactory(
|
||||
baseDirectory = baseDirectory,
|
||||
cacheDirectory = cacheDirectory,
|
||||
sessionStore = sessionStore,
|
||||
clientBuilderProvider = clientBuilderProvider,
|
||||
|
|
|
|||
|
|
@ -32,6 +32,10 @@ class FakeFfiEncryption : Encryption(NoPointer) {
|
|||
return false
|
||||
}
|
||||
|
||||
override suspend fun hasDevicesToVerifyAgainst(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun backupState(): BackupState {
|
||||
return BackupState.ENABLED
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,14 @@ class RoomListFilterTest {
|
|||
currentUserMembership = CurrentUserMembership.INVITED
|
||||
)
|
||||
|
||||
private val space = aRoomSummary(
|
||||
isSpace = true
|
||||
)
|
||||
private val invitedSpace = aRoomSummary(
|
||||
isSpace = true,
|
||||
currentUserMembership = CurrentUserMembership.INVITED
|
||||
)
|
||||
|
||||
private val roomSummaries = listOf(
|
||||
regularRoom,
|
||||
dmRoom,
|
||||
|
|
@ -49,13 +57,15 @@ class RoomListFilterTest {
|
|||
unreadNotificationRoom,
|
||||
roomToSearch,
|
||||
roomWithAccent,
|
||||
invitedRoom
|
||||
invitedRoom,
|
||||
space,
|
||||
invitedSpace,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `Room list filter all empty`() = runTest {
|
||||
val filter = RoomListFilter.all()
|
||||
assertThat(roomSummaries.filter(filter)).isEqualTo(roomSummaries)
|
||||
assertThat(roomSummaries.filter(filter)).isEqualTo(roomSummaries - space)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -83,6 +93,12 @@ class RoomListFilterTest {
|
|||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Room list filter space`() = runTest {
|
||||
val filter = RoomListFilter.Category.Space
|
||||
assertThat(roomSummaries.filter(filter)).containsExactly(space, invitedSpace)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Room list filter favorite`() = runTest {
|
||||
val filter = RoomListFilter.Favorite
|
||||
|
|
@ -98,7 +114,7 @@ class RoomListFilterTest {
|
|||
@Test
|
||||
fun `Room list filter invites`() = runTest {
|
||||
val filter = RoomListFilter.Invite
|
||||
assertThat(roomSummaries.filter(filter)).containsExactly(invitedRoom)
|
||||
assertThat(roomSummaries.filter(filter)).containsExactly(invitedRoom, invitedSpace)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -136,10 +152,4 @@ class RoomListFilterTest {
|
|||
)
|
||||
assertThat(roomSummaries.filter(filter)).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Room list filter all with empty list`() = runTest {
|
||||
val filter = RoomListFilter.all()
|
||||
assertThat(roomSummaries.filter(filter)).isEqualTo(roomSummaries)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue