From 14c23254797318410585b1725dc645a9c98f549c Mon Sep 17 00:00:00 2001 From: ganfra Date: Fri, 6 Jun 2025 21:02:07 +0200 Subject: [PATCH] misc (matrix) : use innerClient.subscribeToRoomInfo sdk method --- .../android/appnav/room/RoomFlowNode.kt | 4 +- .../joinroom/impl/JoinRoomPresenter.kt | 4 +- .../joinroom/impl/JoinRoomPresenterTest.kt | 70 ++++++++--------- .../libraries/matrix/api/MatrixClient.kt | 26 ++----- .../libraries/matrix/impl/RustMatrixClient.kt | 76 ++++++++++--------- .../matrix/impl/analytics/JoinedRoomExt.kt | 11 ++- .../matrix/impl/room/RustRoomFactory.kt | 3 +- .../matrix/impl/room/join/DefaultJoinRoom.kt | 14 +--- .../impl/room/join/DefaultJoinRoomTest.kt | 19 +++-- .../libraries/matrix/test/FakeMatrixClient.kt | 20 ++--- ...otificationBroadcastReceiverHandlerTest.kt | 6 +- 11 files changed, 118 insertions(+), 135 deletions(-) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt index 0dca1d62212..103e97b2583 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt @@ -42,8 +42,6 @@ import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomIdOrAlias -import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias -import io.element.android.libraries.matrix.api.getRoomInfoFlow 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.room.alias.ResolvedRoomAlias @@ -124,7 +122,7 @@ class RoomFlowNode @AssistedInject constructor( } private fun subscribeToRoomInfoFlow(roomId: RoomId, serverNames: List) { - val roomInfoFlow = client.getRoomInfoFlow(roomIdOrAlias = roomId.toRoomIdOrAlias()) + val roomInfoFlow = client.getRoomInfoFlow(roomId) val isSpaceFlow = roomInfoFlow.map { it.getOrNull()?.isSpace.orFalse() }.distinctUntilChanged() val currentMembershipFlow = roomInfoFlow.map { it.getOrNull()?.currentUserMembership }.distinctUntilChanged() combine(currentMembershipFlow, isSpaceFlow) { membership, isSpace -> diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenter.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenter.kt index cbe710b2ddd..7d3b64f6f41 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenter.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenter.kt @@ -39,10 +39,8 @@ import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomIdOrAlias -import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias import io.element.android.libraries.matrix.api.exception.ClientException import io.element.android.libraries.matrix.api.exception.ErrorKind -import io.element.android.libraries.matrix.api.getRoomInfoFlow import io.element.android.libraries.matrix.api.room.CurrentUserMembership import io.element.android.libraries.matrix.api.room.RoomInfo import io.element.android.libraries.matrix.api.room.RoomMember @@ -88,7 +86,7 @@ class JoinRoomPresenter @AssistedInject constructor( val coroutineScope = rememberCoroutineScope() var retryCount by remember { mutableIntStateOf(0) } val roomInfo by remember { - matrixClient.getRoomInfoFlow(roomId.toRoomIdOrAlias()) + matrixClient.getRoomInfoFlow(roomId) }.collectAsState(initial = Optional.empty()) val joinAction: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } val knockAction: MutableState> = remember { mutableStateOf(AsyncAction.Uninitialized) } diff --git a/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt b/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt index 006cc986abe..c751dc971f1 100644 --- a/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt +++ b/features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt @@ -45,10 +45,10 @@ import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.A_USER_ID_2 import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.core.aBuildMeta +import io.element.android.libraries.matrix.test.room.aRoomInfo import io.element.android.libraries.matrix.test.room.aRoomMember import io.element.android.libraries.matrix.test.room.aRoomPreview import io.element.android.libraries.matrix.test.room.aRoomPreviewInfo -import io.element.android.libraries.matrix.test.room.aRoomSummary import io.element.android.libraries.matrix.test.room.join.FakeJoinRoom import io.element.android.libraries.matrix.ui.model.InviteSender import io.element.android.libraries.matrix.ui.model.toInviteSender @@ -88,12 +88,12 @@ class JoinRoomPresenterTest { @Test fun `present - when room is joined then content state is filled with his data`() = runTest { - val roomSummary = aRoomSummary() + val roomInfo = aRoomInfo() val matrixClient = FakeMatrixClient( getNotJoinedRoomResult = { _, _ -> Result.failure(AN_EXCEPTION) }, ).apply { - getRoomSummaryFlowLambda = { _ -> - flowOf(Optional.of(roomSummary)) + getRoomInfoFlowLambda = { _ -> + flowOf(Optional.of(roomInfo)) } } val presenter = createJoinRoomPresenter( @@ -104,24 +104,24 @@ class JoinRoomPresenterTest { awaitItem().also { state -> val contentState = state.contentState as ContentState.Loaded assertThat(contentState.roomId).isEqualTo(A_ROOM_ID) - assertThat(contentState.name).isEqualTo(roomSummary.info.name) - assertThat(contentState.topic).isEqualTo(roomSummary.info.topic) - assertThat(contentState.alias).isEqualTo(roomSummary.info.canonicalAlias) - assertThat(contentState.numberOfMembers).isEqualTo(roomSummary.info.joinedMembersCount) - assertThat(contentState.isDm).isEqualTo(roomSummary.info.isDirect) - assertThat(contentState.roomAvatarUrl).isEqualTo(roomSummary.info.avatarUrl) + assertThat(contentState.name).isEqualTo(roomInfo.name) + assertThat(contentState.topic).isEqualTo(roomInfo.topic) + assertThat(contentState.alias).isEqualTo(roomInfo.canonicalAlias) + assertThat(contentState.numberOfMembers).isEqualTo(roomInfo.joinedMembersCount) + assertThat(contentState.isDm).isEqualTo(roomInfo.isDirect) + assertThat(contentState.roomAvatarUrl).isEqualTo(roomInfo.avatarUrl) } } } @Test fun `present - when room is invited then join authorization is equal to invited`() = runTest { - val roomSummary = aRoomSummary(currentUserMembership = CurrentUserMembership.INVITED) + val roomInfo = aRoomInfo(currentUserMembership = CurrentUserMembership.INVITED) val matrixClient = FakeMatrixClient( getNotJoinedRoomResult = { _, _ -> Result.failure(AN_EXCEPTION) }, ).apply { - getRoomSummaryFlowLambda = { _ -> - flowOf(Optional.of(roomSummary)) + getRoomInfoFlowLambda = { _ -> + flowOf(Optional.of(roomInfo)) } } val seenInvitesStore = InMemorySeenInvitesStore() @@ -129,7 +129,7 @@ class JoinRoomPresenterTest { matrixClient = matrixClient, seenInvitesStore = seenInvitesStore, ) - val inviteData = roomSummary.info.toInviteData() + val inviteData = roomInfo.toInviteData() assertThat(seenInvitesStore.seenRoomIds().first()).isEmpty() presenter.test { skipItems(2) @@ -137,7 +137,7 @@ class JoinRoomPresenterTest { assertThat(state.joinAuthorisationStatus).isEqualTo(JoinAuthorisationStatus.IsInvited(inviteData, null)) } // Check that the roomId is stored in the seen invites store - assertThat(seenInvitesStore.seenRoomIds().first()).containsExactly(roomSummary.roomId) + assertThat(seenInvitesStore.seenRoomIds().first()).containsExactly(roomInfo.id) } } @@ -145,17 +145,17 @@ class JoinRoomPresenterTest { fun `present - when room is invited then join authorization is equal to invited, an inviter is provided`() = runTest { val inviter = aRoomMember(userId = UserId("@bob:example.com"), displayName = "Bob") val expectedInviteSender = inviter.toInviteSender() - val roomSummary = aRoomSummary( + val roomInfo = aRoomInfo( currentUserMembership = CurrentUserMembership.INVITED, joinedMembersCount = 5, inviter = inviter, ) - val inviteData = roomSummary.info.toInviteData() + val inviteData = roomInfo.toInviteData() val matrixClient = FakeMatrixClient( getNotJoinedRoomResult = { _, _ -> Result.failure(AN_EXCEPTION) }, ).apply { - getRoomSummaryFlowLambda = { _ -> - flowOf(Optional.of(roomSummary)) + getRoomInfoFlowLambda = { _ -> + flowOf(Optional.of(roomInfo)) } } val presenter = createJoinRoomPresenter( @@ -172,7 +172,7 @@ class JoinRoomPresenterTest { @Test fun `present - when room is invited read the number of member from the room preview`() = runTest { - val roomSummary = aRoomSummary( + val roomInfo = aRoomInfo( currentUserMembership = CurrentUserMembership.INVITED, // It seems that the SDK does not provide this value. joinedMembersCount = 0, @@ -188,8 +188,8 @@ class JoinRoomPresenterTest { ) }, ).apply { - getRoomSummaryFlowLambda = { _ -> - flowOf(Optional.of(roomSummary)) + getRoomInfoFlowLambda = { _ -> + flowOf(Optional.of(roomInfo)) } } val presenter = createJoinRoomPresenter( @@ -209,13 +209,13 @@ class JoinRoomPresenterTest { val acceptDeclinePresenter = Presenter { anAcceptDeclineInviteState(eventSink = eventSinkRecorder) } - val roomSummary = aRoomSummary(currentUserMembership = CurrentUserMembership.INVITED) + val roomInfo = aRoomInfo(currentUserMembership = CurrentUserMembership.INVITED) val matrixClient = FakeMatrixClient().apply { - getRoomSummaryFlowLambda = { _ -> - flowOf(Optional.of(roomSummary)) + getRoomInfoFlowLambda = { _ -> + flowOf(Optional.of(roomInfo)) } } - val inviteData = roomSummary.info.toInviteData() + val inviteData = roomInfo.toInviteData() val presenter = createJoinRoomPresenter( matrixClient = matrixClient, acceptDeclineInvitePresenter = acceptDeclinePresenter @@ -324,7 +324,7 @@ class JoinRoomPresenterTest { @Test fun `present - when room is banned, then join authorization is equal to IsBanned`() = runTest { - val roomSummary = aRoomSummary(currentUserMembership = CurrentUserMembership.BANNED, joinRule = JoinRule.Public) + val roomInfo = aRoomInfo(currentUserMembership = CurrentUserMembership.BANNED, joinRule = JoinRule.Public) val matrixClient = FakeMatrixClient( getNotJoinedRoomResult = { _, _ -> Result.success( @@ -346,8 +346,8 @@ class JoinRoomPresenterTest { ) } ).apply { - getRoomSummaryFlowLambda = { _ -> - flowOf(Optional.of(roomSummary)) + getRoomInfoFlowLambda = { _ -> + flowOf(Optional.of(roomInfo)) } } val presenter = createJoinRoomPresenter( @@ -369,12 +369,12 @@ class JoinRoomPresenterTest { @Test fun `present - when room is left and public then join authorization is equal to canJoin`() = runTest { - val roomSummary = aRoomSummary(currentUserMembership = CurrentUserMembership.LEFT, joinRule = JoinRule.Public) + val roomInfo = aRoomInfo(currentUserMembership = CurrentUserMembership.LEFT, joinRule = JoinRule.Public) val matrixClient = FakeMatrixClient( getNotJoinedRoomResult = { _, _ -> Result.failure(AN_EXCEPTION) }, ).apply { - getRoomSummaryFlowLambda = { _ -> - flowOf(Optional.of(roomSummary)) + getRoomInfoFlowLambda = { _ -> + flowOf(Optional.of(roomInfo)) } } val presenter = createJoinRoomPresenter( @@ -390,12 +390,12 @@ class JoinRoomPresenterTest { @Test fun `present - when room is left and join rule null then join authorization is equal to Unknown`() = runTest { - val roomSummary = aRoomSummary(currentUserMembership = CurrentUserMembership.LEFT, joinRule = null) + val roomInfo = aRoomInfo(currentUserMembership = CurrentUserMembership.LEFT, joinRule = null) val matrixClient = FakeMatrixClient( getNotJoinedRoomResult = { _, _ -> Result.failure(AN_EXCEPTION) }, ).apply { - getRoomSummaryFlowLambda = { _ -> - flowOf(Optional.of(roomSummary)) + getRoomInfoFlowLambda = { _ -> + flowOf(Optional.of(roomInfo)) } } val presenter = createJoinRoomPresenter( diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt index 91aa6deeeb9..c0005b7cfa2 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt @@ -31,7 +31,6 @@ 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.roomdirectory.RoomDirectoryService import io.element.android.libraries.matrix.api.roomlist.RoomListService -import io.element.android.libraries.matrix.api.roomlist.RoomSummary 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.user.MatrixSearchUserResults @@ -41,8 +40,6 @@ import kotlinx.collections.immutable.ImmutableList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.map import java.util.Optional interface MatrixClient { @@ -65,9 +62,9 @@ interface MatrixClient { suspend fun setDisplayName(displayName: String): Result suspend fun uploadAvatar(mimeType: String, data: ByteArray): Result suspend fun removeAvatar(): Result - suspend fun joinRoom(roomId: RoomId): Result - suspend fun joinRoomByIdOrAlias(roomIdOrAlias: RoomIdOrAlias, serverNames: List): Result - suspend fun knockRoom(roomIdOrAlias: RoomIdOrAlias, message: String, serverNames: List): Result + suspend fun joinRoom(roomId: RoomId): Result + suspend fun joinRoomByIdOrAlias(roomIdOrAlias: RoomIdOrAlias, serverNames: List): Result + suspend fun knockRoom(roomIdOrAlias: RoomIdOrAlias, message: String, serverNames: List): Result fun syncService(): SyncService fun sessionVerificationService(): SessionVerificationService fun pushersService(): PushersService @@ -99,11 +96,11 @@ interface MatrixClient { fun roomMembershipObserver(): RoomMembershipObserver /** - * Get a room summary flow for a given room ID or alias. - * The flow will emit a new value whenever the room summary is updated. + * Get a room info flow for a given room ID. + * The flow will emit a new value whenever the room info is updated. * The flow will emit Optional.empty item if the room is not found. */ - fun getRoomSummaryFlow(roomIdOrAlias: RoomIdOrAlias): Flow> + fun getRoomInfoFlow(roomId: RoomId): Flow> fun isMe(userId: UserId?) = userId == sessionId @@ -169,17 +166,6 @@ interface MatrixClient { suspend fun canReportRoom(): Boolean } -/** - * Get a room info flow for a given room ID or alias. - * The flow will emit a new value whenever the room info is updated. - * The flow will emit Optional.empty item if the room is not found. - */ -fun MatrixClient.getRoomInfoFlow(roomIdOrAlias: RoomIdOrAlias): Flow> { - return getRoomSummaryFlow(roomIdOrAlias) - .map { roomSummary -> roomSummary.map { it.info } } - .distinctUntilChanged() -} - /** * Returns a room alias from a room alias name, or null if the name is not valid. * @param name the room alias name ie. the local part of the room alias. diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index d944d6ef6ea..007628b8e10 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -22,7 +22,6 @@ import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomId 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.core.toRoomIdOrAlias 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 @@ -35,6 +34,7 @@ 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 import io.element.android.libraries.matrix.api.room.NotJoinedRoom +import io.element.android.libraries.matrix.api.room.RoomInfo 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 @@ -42,7 +42,6 @@ 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.roomlist.RoomSummary 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 @@ -60,6 +59,7 @@ import io.element.android.libraries.matrix.impl.pushers.RustPushersService import io.element.android.libraries.matrix.impl.room.GetRoomResult import io.element.android.libraries.matrix.impl.room.NotJoinedRustRoom import io.element.android.libraries.matrix.impl.room.RoomContentForwarder +import io.element.android.libraries.matrix.impl.room.RoomInfoMapper import io.element.android.libraries.matrix.impl.room.RoomSyncSubscriber import io.element.android.libraries.matrix.impl.room.RustRoomFactory import io.element.android.libraries.matrix.impl.room.TimelineEventTypeFilterFactory @@ -70,6 +70,7 @@ import io.element.android.libraries.matrix.impl.roomdirectory.RustRoomDirectoryS import io.element.android.libraries.matrix.impl.roomdirectory.map import io.element.android.libraries.matrix.impl.roomlist.RoomListFactory import io.element.android.libraries.matrix.impl.roomlist.RustRoomListService +import io.element.android.libraries.matrix.impl.roomlist.roomOrNull import io.element.android.libraries.matrix.impl.sync.RustSyncService import io.element.android.libraries.matrix.impl.sync.map import io.element.android.libraries.matrix.impl.usersearch.UserProfileMapper @@ -93,7 +94,6 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.buffer import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull @@ -109,6 +109,7 @@ import org.matrix.rustcomponents.sdk.ClientException import org.matrix.rustcomponents.sdk.IgnoredUsersListener import org.matrix.rustcomponents.sdk.NotificationProcessSetup import org.matrix.rustcomponents.sdk.PowerLevels +import org.matrix.rustcomponents.sdk.RoomInfoListener import org.matrix.rustcomponents.sdk.SendQueueRoomErrorListener import org.matrix.rustcomponents.sdk.TaskHandle import org.matrix.rustcomponents.sdk.use @@ -188,6 +189,7 @@ class RustMatrixClient( sessionCoroutineScope = sessionCoroutineScope, ) + private val roomInfoMapper = RoomInfoMapper() private val roomMembershipObserver = RoomMembershipObserver() private val roomFactory = RustRoomFactory( roomListService = roomListService, @@ -203,6 +205,7 @@ class RustMatrixClient( timelineEventTypeFilterFactory = timelineEventTypeFilterFactory, featureFlagService = featureFlagService, roomMembershipObserver = roomMembershipObserver, + roomInfoMapper = roomInfoMapper, ) override val mediaLoader: MatrixMediaLoader = RustMediaLoader( @@ -276,24 +279,23 @@ class RustMatrixClient( } /** - * Wait for the room to be available in the room list with the correct membership for the current user. - * @param roomIdOrAlias the room id or alias to wait for + * Wait for the room to be available in the client with the correct membership for the current user. + * @param roomId the room id to wait for * @param timeout the timeout to wait for the room to be available * @param currentUserMembership the membership to wait for * @throws TimeoutCancellationException if the room is not available after the timeout */ private suspend fun awaitRoom( - roomIdOrAlias: RoomIdOrAlias, + roomId: RoomId, timeout: Duration, currentUserMembership: CurrentUserMembership, - ): RoomSummary { + ): RoomInfo { return withTimeout(timeout) { - getRoomSummaryFlow(roomIdOrAlias) - .mapNotNull { optionalRoomSummary -> optionalRoomSummary.getOrNull() } - .filter { roomSummary -> roomSummary.info.currentUserMembership == currentUserMembership } - .first() + getRoomInfoFlow(roomId) + .mapNotNull { roomInfo -> roomInfo.getOrNull() } + .first { info -> info.currentUserMembership == currentUserMembership } // Ensure that the room is ready - .also { innerClient.awaitRoomRemoteEcho(it.roomId.value) } + .also { innerClient.awaitRoomRemoteEcho(roomId.value).destroy() } } } @@ -345,7 +347,7 @@ class RustMatrixClient( val roomId = RoomId(innerClient.createRoom(rustParams)) // Wait to receive the room back from the sync but do not returns failure if it fails. try { - awaitRoom(roomId.toRoomIdOrAlias(), 30.seconds, CurrentUserMembership.JOINED) + awaitRoom(roomId, 30.seconds, CurrentUserMembership.JOINED) } catch (e: Exception) { Timber.e(e, "Timeout waiting for the room to be available in the room list") } @@ -396,11 +398,11 @@ class RustMatrixClient( runCatchingExceptions { innerClient.removeAvatar() } } - override suspend fun joinRoom(roomId: RoomId): Result = withContext(sessionDispatcher) { + override suspend fun joinRoom(roomId: RoomId): Result = withContext(sessionDispatcher) { runCatchingExceptions { innerClient.joinRoomById(roomId.value).destroy() try { - awaitRoom(roomId.toRoomIdOrAlias(), 10.seconds, CurrentUserMembership.JOINED) + awaitRoom(roomId, 10.seconds, CurrentUserMembership.JOINED) } catch (e: Exception) { Timber.e(e, "Timeout waiting for the room to be available in the room list") null @@ -408,14 +410,16 @@ class RustMatrixClient( } }.mapFailure { it.mapClientException() } - override suspend fun joinRoomByIdOrAlias(roomIdOrAlias: RoomIdOrAlias, serverNames: List): Result = withContext(sessionDispatcher) { + override suspend fun joinRoomByIdOrAlias(roomIdOrAlias: RoomIdOrAlias, serverNames: List): Result = withContext(sessionDispatcher) { runCatchingExceptions { - innerClient.joinRoomByIdOrAlias( + val roomId = innerClient.joinRoomByIdOrAlias( roomIdOrAlias = roomIdOrAlias.identifier, serverNames = serverNames, - ).destroy() + ).use { + RoomId(it.id()) + } try { - awaitRoom(roomIdOrAlias, 10.seconds, CurrentUserMembership.JOINED) + awaitRoom(roomId, 10.seconds, CurrentUserMembership.JOINED) } catch (e: Exception) { Timber.e(e, "Timeout waiting for the room to be available in the room list") null @@ -423,13 +427,15 @@ class RustMatrixClient( }.mapFailure { it.mapClientException() } } - override suspend fun knockRoom(roomIdOrAlias: RoomIdOrAlias, message: String, serverNames: List): Result = withContext( + override suspend fun knockRoom(roomIdOrAlias: RoomIdOrAlias, message: String, serverNames: List): Result = withContext( sessionDispatcher ) { runCatchingExceptions { - innerClient.knock(roomIdOrAlias.identifier, message, serverNames).destroy() + val roomId = innerClient.knock(roomIdOrAlias.identifier, message, serverNames).use { + RoomId(it.id()) + } try { - awaitRoom(roomIdOrAlias, 10.seconds, CurrentUserMembership.KNOCKED) + awaitRoom(roomId, 10.seconds, CurrentUserMembership.KNOCKED) } catch (e: Exception) { Timber.e(e, "Timeout waiting for the room to be available in the room list") null @@ -622,21 +628,19 @@ class RustMatrixClient( override fun roomMembershipObserver(): RoomMembershipObserver = roomMembershipObserver - override fun getRoomSummaryFlow(roomIdOrAlias: RoomIdOrAlias): Flow> { - val predicate: (RoomSummary) -> Boolean = when (roomIdOrAlias) { - is RoomIdOrAlias.Alias -> { roomSummary -> - roomSummary.info.aliases.contains(roomIdOrAlias.roomAlias) - } - is RoomIdOrAlias.Id -> { roomSummary -> - roomSummary.roomId == roomIdOrAlias.roomId + override fun getRoomInfoFlow(roomId: RoomId): Flow> { + return mxCallbackFlow { + val roomNotFound = innerRoomListService.roomOrNull(roomId.value).use { it == null } + if (roomNotFound) { + channel.send(Optional.empty()) } - } - return roomListService.allRooms.summaries - .map { roomSummaries -> - val roomSummary = roomSummaries.firstOrNull(predicate) - Optional.ofNullable(roomSummary) - } - .distinctUntilChanged() + innerClient.subscribeToRoomInfo(roomId.value, object : RoomInfoListener { + override fun call(roomInfo: org.matrix.rustcomponents.sdk.RoomInfo) { + val mappedRoomInfo = roomInfoMapper.map(roomInfo) + channel.trySend(Optional.of(mappedRoomInfo)) + } + }) + }.distinctUntilChanged() } override suspend fun setAllSendQueuesEnabled(enabled: Boolean) { diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/analytics/JoinedRoomExt.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/analytics/JoinedRoomExt.kt index 2770dc1250a..66fcee1e48c 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/analytics/JoinedRoomExt.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/analytics/JoinedRoomExt.kt @@ -9,6 +9,7 @@ package io.element.android.libraries.matrix.impl.analytics import im.vector.app.features.analytics.plan.JoinedRoom import io.element.android.libraries.matrix.api.room.BaseRoom +import io.element.android.libraries.matrix.api.room.RoomInfo import io.element.android.libraries.matrix.api.room.isDm import kotlinx.coroutines.flow.first @@ -26,10 +27,14 @@ private fun Long.toAnalyticsRoomSize(): JoinedRoom.RoomSize { suspend fun BaseRoom.toAnalyticsJoinedRoom(trigger: JoinedRoom.Trigger?): JoinedRoom { val roomInfo = roomInfoFlow.first() + return roomInfo.toAnalyticsJoinedRoom(trigger) +} + +fun RoomInfo.toAnalyticsJoinedRoom(trigger: JoinedRoom.Trigger?): JoinedRoom { return JoinedRoom( - isDM = roomInfo.isDm, - isSpace = roomInfo.isSpace, - roomSize = roomInfo.joinedMembersCount.toAnalyticsRoomSize(), + isDM = isDm, + isSpace = isSpace, + roomSize = joinedMembersCount.toAnalyticsRoomSize(), trigger = trigger ) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomFactory.kt index e5a8ff4eb06..4ea1611b385 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomFactory.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomFactory.kt @@ -51,13 +51,12 @@ class RustRoomFactory( private val timelineEventTypeFilterFactory: TimelineEventTypeFilterFactory, private val featureFlagService: FeatureFlagService, private val roomMembershipObserver: RoomMembershipObserver, + private val roomInfoMapper: RoomInfoMapper, ) { private val dispatcher = dispatchers.io.limitedParallelism(1) private val mutex = Mutex() private val isDestroyed: AtomicBoolean = AtomicBoolean(false) - private val roomInfoMapper = RoomInfoMapper() - private val eventFilters = TimelineConfig.excludedEvents .takeIf { it.isNotEmpty() } ?.let { listStateEventType -> diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/join/DefaultJoinRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/join/DefaultJoinRoom.kt index a93ffad91ed..0abe45c0fa4 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/join/DefaultJoinRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/join/DefaultJoinRoom.kt @@ -13,7 +13,6 @@ import io.element.android.libraries.di.SessionScope import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomIdOrAlias import io.element.android.libraries.matrix.api.room.join.JoinRoom -import io.element.android.libraries.matrix.api.roomlist.RoomSummary import io.element.android.libraries.matrix.impl.analytics.toAnalyticsJoinedRoom import io.element.android.services.analytics.api.AnalyticsService import javax.inject.Inject @@ -39,15 +38,10 @@ class DefaultJoinRoom @Inject constructor( is RoomIdOrAlias.Alias -> { client.joinRoomByIdOrAlias(roomIdOrAlias, serverNames = emptyList()) } - }.onSuccess { roomSummary -> - client.captureJoinedRoomAnalytics(roomSummary, trigger) + }.onSuccess { roomInfo -> + if (roomInfo != null) { + analyticsService.capture(roomInfo.toAnalyticsJoinedRoom(trigger)) + } }.map { } } - - private suspend fun MatrixClient.captureJoinedRoomAnalytics(roomSummary: RoomSummary?, trigger: JoinedRoom.Trigger) { - if (roomSummary == null) return - getRoom(roomSummary.roomId)?.use { room -> - analyticsService.capture(room.toAnalyticsJoinedRoom(trigger)) - } - } } diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/join/DefaultJoinRoomTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/join/DefaultJoinRoomTest.kt index 6193d229c89..d33148931e6 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/join/DefaultJoinRoomTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/join/DefaultJoinRoomTest.kt @@ -20,7 +20,6 @@ import io.element.android.libraries.matrix.test.A_SERVER_LIST import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.room.FakeBaseRoom import io.element.android.libraries.matrix.test.room.aRoomInfo -import io.element.android.libraries.matrix.test.room.aRoomSummary import io.element.android.services.analytics.test.FakeAnalyticsService import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value @@ -30,9 +29,9 @@ import org.junit.Test class DefaultJoinRoomTest { @Test fun `when using roomId and there is no server names, the classic join room API is used`() = runTest { - val roomSummary = aRoomSummary() - val joinRoomLambda = lambdaRecorder { _: RoomId -> Result.success(roomSummary) } - val joinRoomByIdOrAliasLambda = lambdaRecorder { _: RoomIdOrAlias, _: List -> Result.success(roomSummary) } + val roomInfo = aRoomInfo() + val joinRoomLambda = lambdaRecorder { _: RoomId -> Result.success(roomInfo) } + val joinRoomByIdOrAliasLambda = lambdaRecorder { _: RoomIdOrAlias, _: List -> Result.success(roomInfo) } val roomResult = FakeBaseRoom().apply { givenRoomInfo(aRoomInfo()) } @@ -67,9 +66,9 @@ class DefaultJoinRoomTest { @Test fun `when using roomId and server names are available, joinRoomByIdOrAlias API is used`() = runTest { - val roomSummary = aRoomSummary() - val joinRoomLambda = lambdaRecorder { _: RoomId -> Result.success(roomSummary) } - val joinRoomByIdOrAliasLambda = lambdaRecorder { _: RoomIdOrAlias, _: List -> Result.success(roomSummary) } + val roomInfo = aRoomInfo() + val joinRoomLambda = lambdaRecorder { _: RoomId -> Result.success(roomInfo) } + val joinRoomByIdOrAliasLambda = lambdaRecorder { _: RoomIdOrAlias, _: List -> Result.success(roomInfo) } val roomResult = FakeBaseRoom().apply { givenRoomInfo(aRoomInfo()) } @@ -105,9 +104,9 @@ class DefaultJoinRoomTest { @Test fun `when using roomAlias, joinRoomByIdOrAlias API is used`() = runTest { - val roomSummary = aRoomSummary() - val joinRoomLambda = lambdaRecorder { _: RoomId -> Result.success(roomSummary) } - val joinRoomByIdOrAliasLambda = lambdaRecorder { _: RoomIdOrAlias, _: List -> Result.success(roomSummary) } + val roomInfo = aRoomInfo() + val joinRoomLambda = lambdaRecorder { _: RoomId -> Result.success(roomInfo) } + val joinRoomByIdOrAliasLambda = lambdaRecorder { _: RoomIdOrAlias, _: List -> Result.success(roomInfo) } val roomResult = FakeBaseRoom().apply { givenRoomInfo(aRoomInfo()) } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt index 50a23b39ffa..7d3ee5e994e 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt @@ -25,11 +25,11 @@ 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.JoinedRoom import io.element.android.libraries.matrix.api.room.NotJoinedRoom +import io.element.android.libraries.matrix.api.room.RoomInfo 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.roomdirectory.RoomDirectoryService import io.element.android.libraries.matrix.api.roomlist.RoomListService -import io.element.android.libraries.matrix.api.roomlist.RoomSummary import io.element.android.libraries.matrix.api.sync.SlidingSyncVersion import io.element.android.libraries.matrix.api.user.MatrixSearchUserResults import io.element.android.libraries.matrix.api.user.MatrixUser @@ -111,17 +111,17 @@ class FakeMatrixClient( private var setDisplayNameResult: Result = Result.success(Unit) private var uploadAvatarResult: Result = Result.success(Unit) private var removeAvatarResult: Result = Result.success(Unit) - var joinRoomLambda: (RoomId) -> Result = { + var joinRoomLambda: (RoomId) -> Result = { Result.success(null) } - var joinRoomByIdOrAliasLambda: (RoomIdOrAlias, List) -> Result = { _, _ -> + var joinRoomByIdOrAliasLambda: (RoomIdOrAlias, List) -> Result = { _, _ -> Result.success(null) } - var knockRoomLambda: (RoomIdOrAlias, String, List) -> Result = { _, _, _ -> + var knockRoomLambda: (RoomIdOrAlias, String, List) -> Result = { _, _, _ -> Result.success(null) } - var getRoomSummaryFlowLambda = { _: RoomIdOrAlias -> - flowOf>(Optional.empty()) + var getRoomInfoFlowLambda = { _: RoomId -> + flowOf>(Optional.empty()) } var logoutLambda: (Boolean, Boolean) -> Unit = { _, _ -> } @@ -216,13 +216,13 @@ class FakeMatrixClient( return removeAvatarResult } - override suspend fun joinRoom(roomId: RoomId): Result = joinRoomLambda(roomId) + override suspend fun joinRoom(roomId: RoomId): Result = joinRoomLambda(roomId) - override suspend fun joinRoomByIdOrAlias(roomIdOrAlias: RoomIdOrAlias, serverNames: List): Result { + override suspend fun joinRoomByIdOrAlias(roomIdOrAlias: RoomIdOrAlias, serverNames: List): Result { return joinRoomByIdOrAliasLambda(roomIdOrAlias, serverNames) } - override suspend fun knockRoom(roomIdOrAlias: RoomIdOrAlias, message: String, serverNames: List): Result { + override suspend fun knockRoom(roomIdOrAlias: RoomIdOrAlias, message: String, serverNames: List): Result { return knockRoomLambda(roomIdOrAlias, message, serverNames) } @@ -304,7 +304,7 @@ class FakeMatrixClient( return Result.success(visitedRoomsId) } - override fun getRoomSummaryFlow(roomIdOrAlias: RoomIdOrAlias) = getRoomSummaryFlowLambda(roomIdOrAlias) + override fun getRoomInfoFlow(roomId: RoomId) = getRoomInfoFlowLambda(roomId) var setAllSendQueuesEnabledLambda = lambdaRecorder(ensureNeverCalled = true) { _: Boolean -> // no-op diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiverHandlerTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiverHandlerTest.kt index c51e7ad7a85..3fd94863a16 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiverHandlerTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiverHandlerTest.kt @@ -14,9 +14,9 @@ import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.ThreadId import io.element.android.libraries.matrix.api.room.IntentionalMention +import io.element.android.libraries.matrix.api.room.RoomInfo import io.element.android.libraries.matrix.api.room.message.ReplyParameters import io.element.android.libraries.matrix.api.room.message.replyInThread -import io.element.android.libraries.matrix.api.roomlist.RoomSummary import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.A_MESSAGE @@ -262,7 +262,7 @@ class NotificationBroadcastReceiverHandlerTest { @Test fun `Test join room`() = runTest { - val joinRoom = lambdaRecorder> { _ -> Result.success(null) } + val joinRoom = lambdaRecorder> { _ -> Result.success(null) } val clearMembershipNotificationForRoomLambda = lambdaRecorder { _, _ -> } val fakeNotificationCleaner = FakeNotificationCleaner( clearMembershipNotificationForRoomLambda = clearMembershipNotificationForRoomLambda, @@ -471,7 +471,7 @@ class NotificationBroadcastReceiverHandlerTest { private fun TestScope.createNotificationBroadcastReceiverHandler( joinedRoom: FakeJoinedRoom? = FakeJoinedRoom(), - joinRoom: (RoomId) -> Result = { lambdaError() }, + joinRoom: (RoomId) -> Result = { lambdaError() }, matrixClient: MatrixClient? = FakeMatrixClient().apply { givenGetRoomResult(A_ROOM_ID, joinedRoom) joinRoomLambda = joinRoom