diff --git a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenter.kt b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenter.kt index 73a82a71f5..95bebc07e6 100644 --- a/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenter.kt +++ b/features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenter.kt @@ -33,6 +33,7 @@ import io.element.android.libraries.architecture.runCatchingUpdatingState import io.element.android.libraries.architecture.runUpdatingState 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.toRoomIdOrAlias import io.element.android.libraries.matrix.api.room.join.JoinRoom import io.element.android.libraries.push.api.notifications.NotificationCleaner import kotlinx.coroutines.CoroutineScope @@ -107,7 +108,7 @@ class AcceptDeclineInvitePresenter @Inject constructor( ) = launch { acceptedAction.runUpdatingState { joinRoom( - roomId = roomId, + roomIdOrAlias = roomId.toRoomIdOrAlias(), serverNames = emptyList(), trigger = JoinedRoom.Trigger.Invite, ) diff --git a/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenterTest.kt b/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenterTest.kt index 39a0321b01..239ac9c597 100644 --- a/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenterTest.kt +++ b/features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenterTest.kt @@ -23,7 +23,9 @@ import io.element.android.features.invite.api.response.InviteData import io.element.android.libraries.architecture.AsyncAction 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.SessionId +import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_ROOM_NAME import io.element.android.libraries.matrix.test.A_SESSION_ID @@ -178,8 +180,8 @@ class AcceptDeclineInvitePresenterTest { @Test fun `present - accepting invite error flow`() = runTest { - val joinRoomFailure = lambdaRecorder { roomId: RoomId, _: List, _: JoinedRoom.Trigger -> - Result.failure(RuntimeException("Failed to join room $roomId")) + val joinRoomFailure = lambdaRecorder { roomIdOrAlias: RoomIdOrAlias, _: List, _: JoinedRoom.Trigger -> + Result.failure(RuntimeException("Failed to join room $roomIdOrAlias")) } val presenter = createAcceptDeclineInvitePresenter(joinRoomLambda = joinRoomFailure) presenter.test { @@ -208,7 +210,7 @@ class AcceptDeclineInvitePresenterTest { assert(joinRoomFailure) .isCalledOnce() .with( - value(A_ROOM_ID), + value(A_ROOM_ID.toRoomIdOrAlias()), value(emptyList()), value(JoinedRoom.Trigger.Invite) ) @@ -222,7 +224,7 @@ class AcceptDeclineInvitePresenterTest { val fakeNotificationCleaner = FakeNotificationCleaner( clearMembershipNotificationForRoomLambda = clearMembershipNotificationForRoomLambda ) - val joinRoomSuccess = lambdaRecorder { _: RoomId, _: List, _: JoinedRoom.Trigger -> + val joinRoomSuccess = lambdaRecorder { _: RoomIdOrAlias, _: List, _: JoinedRoom.Trigger -> Result.success(Unit) } val presenter = createAcceptDeclineInvitePresenter( @@ -248,7 +250,7 @@ class AcceptDeclineInvitePresenterTest { assert(joinRoomSuccess) .isCalledOnce() .with( - value(A_ROOM_ID), + value(A_ROOM_ID.toRoomIdOrAlias()), value(emptyList()), value(JoinedRoom.Trigger.Invite) ) @@ -271,7 +273,7 @@ class AcceptDeclineInvitePresenterTest { private fun createAcceptDeclineInvitePresenter( client: MatrixClient = FakeMatrixClient(), - joinRoomLambda: (RoomId, List, JoinedRoom.Trigger) -> Result = { _, _, _ -> + joinRoomLambda: (RoomIdOrAlias, List, JoinedRoom.Trigger) -> Result = { _, _, _ -> Result.success(Unit) }, notificationCleaner: NotificationCleaner = FakeNotificationCleaner(), 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 bba2a82a34..3fb4659c96 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 @@ -96,7 +96,7 @@ class JoinRoomPresenter @AssistedInject constructor( } else -> { value = ContentState.Loading(roomIdOrAlias) - val result = matrixClient.getRoomPreviewFromRoomId(roomId, serverNames) + val result = matrixClient.getRoomPreview(roomIdOrAlias, serverNames) value = result.fold( onSuccess = { roomPreview -> roomPreview.toContentState() @@ -153,7 +153,7 @@ class JoinRoomPresenter @AssistedInject constructor( private fun CoroutineScope.joinRoom(joinAction: MutableState>) = launch { joinAction.runUpdatingState { joinRoom.invoke( - roomId = roomId, + roomIdOrAlias = roomIdOrAlias, serverNames = serverNames, trigger = trigger ) diff --git a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomState.kt b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomState.kt index ab66d0d80c..f71ece39af 100644 --- a/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomState.kt +++ b/features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomState.kt @@ -37,7 +37,11 @@ data class JoinRoomState( val eventSink: (JoinRoomEvents) -> Unit ) { val joinAuthorisationStatus = when (contentState) { + // Use the join authorisation status from the loaded content state is ContentState.Loaded -> contentState.joinAuthorisationStatus + // Assume that if the room is unknown, the user can join it + is ContentState.UnknownRoom -> JoinAuthorisationStatus.CanJoin + // Otherwise assume that the user can't join the room else -> JoinAuthorisationStatus.Unknown } } 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 7a6e08244c..b636e1497c 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 @@ -29,6 +29,7 @@ import io.element.android.libraries.core.meta.BuildMeta 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.UserId import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias import io.element.android.libraries.matrix.api.room.CurrentUserMembership @@ -180,7 +181,7 @@ class JoinRoomPresenterTest { @Test fun `present - when room is joined with success, all the parameters are provided`() = runTest { val aTrigger = JoinedRoom.Trigger.MobilePermalink - val joinRoomLambda = lambdaRecorder { _: RoomId, _: List, _: JoinedRoom.Trigger -> + val joinRoomLambda = lambdaRecorder { _: RoomIdOrAlias, _: List, _: JoinedRoom.Trigger -> Result.success(Unit) } val presenter = createJoinRoomPresenter( @@ -201,7 +202,7 @@ class JoinRoomPresenterTest { } joinRoomLambda.assertions() .isCalledOnce() - .with(value(A_ROOM_ID), value(A_SERVER_LIST), value(aTrigger)) + .with(value(A_ROOM_ID.toRoomIdOrAlias()), value(A_SERVER_LIST), value(aTrigger)) } } @@ -366,7 +367,7 @@ class JoinRoomPresenterTest { @Test fun `present - when room is not known RoomPreview is loaded`() = runTest { val client = FakeMatrixClient( - getRoomPreviewFromRoomIdResult = { _, _ -> + getRoomPreviewResult = { _, _ -> Result.success( RoomPreview( roomId = A_ROOM_ID, @@ -411,7 +412,7 @@ class JoinRoomPresenterTest { @Test fun `present - when room is not known RoomPreview is loaded with error`() = runTest { val client = FakeMatrixClient( - getRoomPreviewFromRoomIdResult = { _, _ -> + getRoomPreviewResult = { _, _ -> Result.failure(AN_EXCEPTION) } ) @@ -449,7 +450,7 @@ class JoinRoomPresenterTest { @Test fun `present - when room is not known RoomPreview is loaded with error 403`() = runTest { val client = FakeMatrixClient( - getRoomPreviewFromRoomIdResult = { _, _ -> + getRoomPreviewResult = { _, _ -> Result.failure(Exception("403")) } ) @@ -474,7 +475,7 @@ class JoinRoomPresenterTest { serverNames: List = emptyList(), trigger: JoinedRoom.Trigger = JoinedRoom.Trigger.Invite, matrixClient: MatrixClient = FakeMatrixClient(), - joinRoomLambda: (RoomId, List, JoinedRoom.Trigger) -> Result = { _, _, _ -> + joinRoomLambda: (RoomIdOrAlias, List, JoinedRoom.Trigger) -> Result = { _, _, _ -> Result.success(Unit) }, knockRoom: KnockRoom = FakeKnockRoom(), 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 b3b47d4499..3209d49e03 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 @@ -19,6 +19,7 @@ package io.element.android.libraries.matrix.api import io.element.android.libraries.matrix.api.core.ProgressCallback 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.SessionId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.createroom.CreateRoomParameters @@ -35,6 +36,7 @@ import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias import io.element.android.libraries.matrix.api.room.preview.RoomPreview 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.SyncService import io.element.android.libraries.matrix.api.user.MatrixSearchUserResults import io.element.android.libraries.matrix.api.user.MatrixUser @@ -65,8 +67,8 @@ interface MatrixClient : Closeable { 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(roomId: RoomId, serverNames: List): Result + suspend fun joinRoom(roomId: RoomId): Result + suspend fun joinRoomByIdOrAlias(roomIdOrAlias: RoomIdOrAlias, serverNames: List): Result suspend fun knockRoom(roomId: RoomId): Result fun syncService(): SyncService fun sessionVerificationService(): SessionVerificationService @@ -104,7 +106,6 @@ interface MatrixClient : Closeable { suspend fun trackRecentlyVisitedRoom(roomId: RoomId): Result suspend fun getRecentlyVisitedRooms(): Result> suspend fun resolveRoomAlias(roomAlias: RoomAlias): Result - suspend fun getRoomPreviewFromRoomId(roomId: RoomId, serverNames: List): Result /** * Enables or disables the sending queue, according to the given parameter. @@ -132,4 +133,5 @@ interface MatrixClient : Closeable { * Execute generic GET requests through the SDKs internal HTTP client. */ suspend fun getUrl(url: String): Result + suspend fun getRoomPreview(roomIdOrAlias: RoomIdOrAlias, serverNames: List): Result } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/join/JoinRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/join/JoinRoom.kt index fe6a2d9e47..87cc76e199 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/join/JoinRoom.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/join/JoinRoom.kt @@ -17,11 +17,11 @@ package io.element.android.libraries.matrix.api.room.join import im.vector.app.features.analytics.plan.JoinedRoom -import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.api.core.RoomIdOrAlias interface JoinRoom { suspend operator fun invoke( - roomId: RoomId, + roomIdOrAlias: RoomIdOrAlias, serverNames: List, trigger: JoinedRoom.Trigger, ): Result diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomSummary.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomSummary.kt index 6841af9721..b932b235fa 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomSummary.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomSummary.kt @@ -28,6 +28,7 @@ data class RoomSummary( val roomId: RoomId, val name: String?, val canonicalAlias: RoomAlias?, + val alternativeAliases: List, val isDirect: Boolean, val avatarUrl: String?, val lastMessage: RoomMessage?, @@ -44,4 +45,6 @@ data class RoomSummary( val heroes: List, ) { val lastMessageTimestamp = lastMessage?.originServerTs + val aliases: List + get() = listOfNotNull(canonicalAlias) + alternativeAliases } 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 db86e1a2a5..3aab5bebed 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 @@ -24,7 +24,9 @@ import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.ProgressCallback 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.createroom.RoomVisibility @@ -41,6 +43,7 @@ import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias import io.element.android.libraries.matrix.api.room.preview.RoomPreview 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.SyncService import io.element.android.libraries.matrix.api.sync.SyncState import io.element.android.libraries.matrix.api.user.MatrixSearchUserResults @@ -175,7 +178,7 @@ class RustMatrixClient( val (anonymizedAccessToken, anonymizedRefreshToken) = existingData.anonymizedTokens() clientLog.d( "Removing session data with access token '$anonymizedAccessToken' " + - "and refresh token '$anonymizedRefreshToken'." + "and refresh token '$anonymizedRefreshToken'." ) if (existingData != null) { // Set isTokenValid to false @@ -320,16 +323,23 @@ class RustMatrixClient( /** * Wait for the room to be available in the room list. - * @param roomId the room id to wait for + * @param roomIdOrAlias the room id or alias to wait for * @param timeout the timeout to wait for the room to be available * @throws TimeoutCancellationException if the room is not available after the timeout */ - private suspend fun awaitRoom(roomId: RoomId, timeout: Duration) { - withTimeout(timeout) { + private suspend fun awaitRoom(roomIdOrAlias: RoomIdOrAlias, timeout: Duration): RoomSummary { + val predicate: (List) -> Boolean = when (roomIdOrAlias) { + is RoomIdOrAlias.Alias -> { roomSummaries: List -> + roomSummaries.flatMap { it.aliases }.contains(roomIdOrAlias.roomAlias) + } + is RoomIdOrAlias.Id -> { roomSummaries: List -> + roomSummaries.map { it.roomId }.contains(roomIdOrAlias.roomId) + } + } + return withTimeout(timeout) { roomListService.allRooms.summaries - .filter { roomSummaries -> - roomSummaries.map { it.roomId }.contains(roomId) - } + .filter(predicate) + .first() .first() } } @@ -373,7 +383,7 @@ class RustMatrixClient( val roomId = RoomId(client.createRoom(rustParams)) // Wait to receive the room back from the sync but do not returns failure if it fails. try { - awaitRoom(roomId, 30.seconds) + awaitRoom(roomId.toRoomIdOrAlias(), 30.seconds) } catch (e: Exception) { Timber.e(e, "Timeout waiting for the room to be available in the room list") } @@ -424,30 +434,29 @@ class RustMatrixClient( runCatching { client.removeAvatar() } } - override suspend fun joinRoom(roomId: RoomId): Result = withContext(sessionDispatcher) { + override suspend fun joinRoom(roomId: RoomId): Result = withContext(sessionDispatcher) { runCatching { client.joinRoomById(roomId.value).destroy() try { - awaitRoom(roomId, 10.seconds) + awaitRoom(roomId.toRoomIdOrAlias(), 10.seconds) } catch (e: Exception) { Timber.e(e, "Timeout waiting for the room to be available in the room list") + null } } } - override suspend fun joinRoomByIdOrAlias( - roomId: RoomId, - serverNames: List, - ): Result = withContext(sessionDispatcher) { + override suspend fun joinRoomByIdOrAlias(roomIdOrAlias: RoomIdOrAlias, serverNames: List): Result = withContext(sessionDispatcher) { runCatching { client.joinRoomByIdOrAlias( - roomIdOrAlias = roomId.value, + roomIdOrAlias = roomIdOrAlias.identifier, serverNames = serverNames, ).destroy() try { - awaitRoom(roomId, 10.seconds) + awaitRoom(roomIdOrAlias, 10.seconds) } catch (e: Exception) { Timber.e(e, "Timeout waiting for the room to be available in the room list") + null } } } @@ -478,12 +487,12 @@ class RustMatrixClient( } } - override suspend fun getRoomPreviewFromRoomId(roomId: RoomId, serverNames: List): Result = withContext(sessionDispatcher) { + override suspend fun getRoomPreview(roomIdOrAlias: RoomIdOrAlias, serverNames: List): Result = withContext(sessionDispatcher) { runCatching { - client.getRoomPreviewFromRoomId( - roomId = roomId.value, - viaServers = serverNames, - ).let(RoomPreviewMapper::map) + when (roomIdOrAlias) { + is RoomIdOrAlias.Alias -> client.getRoomPreviewFromRoomAlias(roomIdOrAlias.roomAlias.value) + is RoomIdOrAlias.Id -> client.getRoomPreviewFromRoomId(roomIdOrAlias.roomId.value, serverNames) + }.let(RoomPreviewMapper::map) } } @@ -581,7 +590,7 @@ class RustMatrixClient( var room = getRoom(roomId) if (room == null) { emit(Optional.empty()) - awaitRoom(roomId, INFINITE) + awaitRoom(roomId.toRoomIdOrAlias(), INFINITE) room = getRoom(roomId) } room?.use { 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 d2ce4e61d5..063facbc96 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 @@ -20,8 +20,9 @@ import com.squareup.anvil.annotations.ContributesBinding import im.vector.app.features.analytics.plan.JoinedRoom import io.element.android.libraries.di.SessionScope 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.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 @@ -32,18 +33,30 @@ class DefaultJoinRoom @Inject constructor( private val analyticsService: AnalyticsService, ) : JoinRoom { override suspend fun invoke( - roomId: RoomId, + roomIdOrAlias: RoomIdOrAlias, serverNames: List, - trigger: JoinedRoom.Trigger, + trigger: JoinedRoom.Trigger ): Result { - return if (serverNames.isEmpty()) { - client.joinRoom(roomId) - } else { - client.joinRoomByIdOrAlias(roomId, serverNames) - }.onSuccess { - client.getRoom(roomId)?.use { room -> - analyticsService.capture(room.toAnalyticsJoinedRoom(trigger)) + return when (roomIdOrAlias) { + is RoomIdOrAlias.Id -> { + if (serverNames.isEmpty()) { + client.joinRoom(roomIdOrAlias.roomId) + } else { + client.joinRoomByIdOrAlias(roomIdOrAlias, serverNames) + } } + is RoomIdOrAlias.Alias -> { + client.joinRoomByIdOrAlias(roomIdOrAlias, serverNames = emptyList()) + } + }.onSuccess { roomSummary -> + client.captureJoinedRoomAnalytics(roomSummary, 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/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryDetailsFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryDetailsFactory.kt index a833734a5c..9c67eb1221 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryDetailsFactory.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryDetailsFactory.kt @@ -38,6 +38,7 @@ class RoomSummaryDetailsFactory(private val roomMessageFactory: RoomMessageFacto roomId = RoomId(roomInfo.id), name = roomInfo.displayName, canonicalAlias = roomInfo.canonicalAlias?.let(::RoomAlias), + alternativeAliases = roomInfo.alternativeAliases.map(::RoomAlias), isDirect = roomInfo.isDirect, avatarUrl = roomInfo.avatarUrl, numUnreadMentions = roomInfo.numUnreadMentions.toInt(), 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 6296f219de..9941b6b1ed 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,11 +20,15 @@ import com.google.common.truth.Truth.assertThat import im.vector.app.features.analytics.plan.JoinedRoom 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.impl.analytics.toAnalyticsJoinedRoom +import io.element.android.libraries.matrix.test.A_ROOM_ALIAS import io.element.android.libraries.matrix.test.A_ROOM_ID 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.FakeMatrixRoom +import io.element.android.libraries.matrix.test.room.aRoomSummaryFilled import io.element.android.services.analytics.test.FakeAnalyticsService import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value @@ -33,9 +37,10 @@ import org.junit.Test class DefaultJoinRoomTest { @Test - fun `when there is no server names, the classic join room API is used`() = runTest { - val joinRoomLambda = lambdaRecorder { _: RoomId -> Result.success(Unit) } - val joinRoomByIdOrAliasLambda = lambdaRecorder { _: RoomId, _: List -> Result.success(Unit) } + fun `when using roomId and there is no server names, the classic join room API is used`() = runTest { + val roomSummary = aRoomSummaryFilled() + val joinRoomLambda = lambdaRecorder { _: RoomId -> Result.success(roomSummary) } + val joinRoomByIdOrAliasLambda = lambdaRecorder { _: RoomIdOrAlias, _: List -> Result.success(roomSummary) } val roomResult = FakeMatrixRoom() val aTrigger = JoinedRoom.Trigger.MobilePermalink val client: MatrixClient = FakeMatrixClient().also { @@ -51,7 +56,7 @@ class DefaultJoinRoomTest { client = client, analyticsService = analyticsService, ) - sut.invoke(A_ROOM_ID, emptyList(), aTrigger) + sut.invoke(A_ROOM_ID.toRoomIdOrAlias(), emptyList(), aTrigger) joinRoomByIdOrAliasLambda .assertions() .isNeverCalled() @@ -67,9 +72,10 @@ class DefaultJoinRoomTest { } @Test - fun `when server names are available, joinRoomByIdOrAlias API is used`() = runTest { - val joinRoomLambda = lambdaRecorder { _: RoomId -> Result.success(Unit) } - val joinRoomByIdOrAliasLambda = lambdaRecorder { _: RoomId, _: List -> Result.success(Unit) } + fun `when using roomId and server names are available, joinRoomByIdOrAlias API is used`() = runTest { + val roomSummary = aRoomSummaryFilled() + val joinRoomLambda = lambdaRecorder { _: RoomId -> Result.success(roomSummary) } + val joinRoomByIdOrAliasLambda = lambdaRecorder { _: RoomIdOrAlias, _: List -> Result.success(roomSummary) } val roomResult = FakeMatrixRoom() val aTrigger = JoinedRoom.Trigger.MobilePermalink val client: MatrixClient = FakeMatrixClient().also { @@ -85,12 +91,12 @@ class DefaultJoinRoomTest { client = client, analyticsService = analyticsService, ) - sut.invoke(A_ROOM_ID, A_SERVER_LIST, aTrigger) + sut.invoke(A_ROOM_ID.toRoomIdOrAlias(), A_SERVER_LIST, aTrigger) joinRoomByIdOrAliasLambda .assertions() .isCalledOnce() .with( - value(A_ROOM_ID), + value(A_ROOM_ID.toRoomIdOrAlias()), value(A_SERVER_LIST) ) joinRoomLambda @@ -100,4 +106,40 @@ class DefaultJoinRoomTest { roomResult.toAnalyticsJoinedRoom(aTrigger) ) } + + @Test + fun `when using roomAlias, joinRoomByIdOrAlias API is used`() = runTest { + val roomSummary = aRoomSummaryFilled() + val joinRoomLambda = lambdaRecorder { _: RoomId -> Result.success(roomSummary) } + val joinRoomByIdOrAliasLambda = lambdaRecorder { _: RoomIdOrAlias, _: List -> Result.success(roomSummary) } + val roomResult = FakeMatrixRoom() + val aTrigger = JoinedRoom.Trigger.MobilePermalink + val client: MatrixClient = FakeMatrixClient().also { + it.joinRoomLambda = joinRoomLambda + it.joinRoomByIdOrAliasLambda = joinRoomByIdOrAliasLambda + it.givenGetRoomResult( + roomId = A_ROOM_ID, + result = roomResult + ) + } + val analyticsService = FakeAnalyticsService() + val sut = DefaultJoinRoom( + client = client, + analyticsService = analyticsService, + ) + sut.invoke(A_ROOM_ALIAS.toRoomIdOrAlias(), A_SERVER_LIST, aTrigger) + joinRoomByIdOrAliasLambda + .assertions() + .isCalledOnce() + .with( + value(A_ROOM_ALIAS.toRoomIdOrAlias()), + value(emptyList()) + ) + joinRoomLambda + .assertions() + .isNeverCalled() + assertThat(analyticsService.capturedEvents).containsExactly( + roomResult.toAnalyticsJoinedRoom(aTrigger) + ) + } } 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 1b6223279c..1fb8878488 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 @@ -20,6 +20,7 @@ import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.ProgressCallback 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.SessionId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.createroom.CreateRoomParameters @@ -36,6 +37,7 @@ import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias import io.element.android.libraries.matrix.api.room.preview.RoomPreview 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.user.MatrixSearchUserResults import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.api.verification.SessionVerificationService @@ -80,7 +82,7 @@ class FakeMatrixClient( private val roomDirectoryService: RoomDirectoryService = FakeRoomDirectoryService(), private val accountManagementUrlString: Result = Result.success(null), private val resolveRoomAliasResult: (RoomAlias) -> Result = { Result.success(ResolvedRoomAlias(A_ROOM_ID, emptyList())) }, - private val getRoomPreviewFromRoomIdResult: (RoomId, List) -> Result = { _, _ -> Result.failure(AN_EXCEPTION) }, + private val getRoomPreviewResult: (RoomIdOrAlias, List) -> Result = { _, _ -> Result.failure(AN_EXCEPTION) }, private val clearCacheLambda: () -> Unit = { lambdaError() }, private val userIdServerNameLambda: () -> String = { lambdaError() }, private val getUrlLambda: (String) -> Result = { lambdaError() }, @@ -108,11 +110,11 @@ 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 = { - Result.success(Unit) + var joinRoomLambda: (RoomId) -> Result = { + Result.success(null) } - var joinRoomByIdOrAliasLambda: (RoomId, List) -> Result = { _, _ -> - Result.success(Unit) + var joinRoomByIdOrAliasLambda: (RoomIdOrAlias, List) -> Result = { _, _ -> + Result.success(null) } var knockRoomLambda: (RoomId) -> Result = { Result.success(Unit) @@ -207,10 +209,10 @@ 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(roomId: RoomId, serverNames: List): Result { - return joinRoomByIdOrAliasLambda(roomId, serverNames) + override suspend fun joinRoomByIdOrAlias(roomIdOrAlias: RoomIdOrAlias, serverNames: List): Result { + return joinRoomByIdOrAliasLambda(roomIdOrAlias, serverNames) } override suspend fun knockRoom(roomId: RoomId): Result = knockRoomLambda(roomId) @@ -297,8 +299,8 @@ class FakeMatrixClient( resolveRoomAliasResult(roomAlias) } - override suspend fun getRoomPreviewFromRoomId(roomId: RoomId, serverNames: List) = simulateLongTask { - getRoomPreviewFromRoomIdResult(roomId, serverNames) + override suspend fun getRoomPreview(roomIdOrAlias: RoomIdOrAlias, serverNames: List): Result = simulateLongTask { + getRoomPreviewResult(roomIdOrAlias, serverNames) } override suspend fun getRecentlyVisitedRooms(): Result> { diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt index f5ef3faa0b..f83379a70e 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt @@ -68,6 +68,7 @@ fun aRoomSummary( notificationMode: RoomNotificationMode? = null, inviter: RoomMember? = null, canonicalAlias: RoomAlias? = null, + alternativeAliases: List = emptyList(), hasRoomCall: Boolean = false, isDm: Boolean = false, isFavorite: Boolean = false, @@ -86,6 +87,7 @@ fun aRoomSummary( userDefinedNotificationMode = notificationMode, inviter = inviter, canonicalAlias = canonicalAlias, + alternativeAliases = alternativeAliases, hasRoomCall = hasRoomCall, isDm = isDm, isFavorite = isFavorite, diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/join/FakeJoinRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/join/FakeJoinRoom.kt index 55eade69d4..830d02f741 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/join/FakeJoinRoom.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/join/FakeJoinRoom.kt @@ -17,18 +17,18 @@ package io.element.android.libraries.matrix.test.room.join import im.vector.app.features.analytics.plan.JoinedRoom -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.room.join.JoinRoom import io.element.android.tests.testutils.simulateLongTask class FakeJoinRoom( - var lambda: (RoomId, List, JoinedRoom.Trigger) -> Result + var lambda: (RoomIdOrAlias, List, JoinedRoom.Trigger) -> Result ) : JoinRoom { override suspend fun invoke( - roomId: RoomId, + roomIdOrAlias: RoomIdOrAlias, serverNames: List, trigger: JoinedRoom.Trigger, ): Result = simulateLongTask { - lambda(roomId, serverNames, trigger) + lambda(roomIdOrAlias, serverNames, trigger) } } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/RoomSummaryDetailsProvider.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/RoomSummaryDetailsProvider.kt index c5e117808b..7454ec30bb 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/RoomSummaryDetailsProvider.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/RoomSummaryDetailsProvider.kt @@ -38,6 +38,7 @@ fun aRoomSummaryDetails( roomId: RoomId = RoomId("!room:domain"), name: String? = "roomName", canonicalAlias: RoomAlias? = null, + alternativeAliases: List = emptyList(), isDirect: Boolean = true, avatarUrl: String? = null, lastMessage: RoomMessage? = null, @@ -56,6 +57,7 @@ fun aRoomSummaryDetails( roomId = roomId, name = name, canonicalAlias = canonicalAlias, + alternativeAliases = alternativeAliases, isDirect = isDirect, avatarUrl = avatarUrl, lastMessage = lastMessage, 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 f4dedfad97..40d32878df 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 @@ -25,6 +25,7 @@ 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.core.asEventId import io.element.android.libraries.matrix.api.room.Mention +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 @@ -261,7 +262,7 @@ class NotificationBroadcastReceiverHandlerTest { @Test fun `Test join room`() = runTest { - val joinRoom = lambdaRecorder> { _ -> Result.success(Unit) } + val joinRoom = lambdaRecorder> { _ -> Result.success(null) } val clearMembershipNotificationForRoomLambda = lambdaRecorder { _, _ -> } val fakeNotificationCleaner = FakeNotificationCleaner( clearMembershipNotificationForRoomLambda = clearMembershipNotificationForRoomLambda, @@ -444,7 +445,7 @@ class NotificationBroadcastReceiverHandlerTest { private fun TestScope.createNotificationBroadcastReceiverHandler( matrixRoom: FakeMatrixRoom? = FakeMatrixRoom(), - joinRoom: (RoomId) -> Result = { lambdaError() }, + joinRoom: (RoomId) -> Result = { lambdaError() }, matrixClient: MatrixClient? = FakeMatrixClient().apply { givenGetRoomResult(A_ROOM_ID, matrixRoom) joinRoomLambda = joinRoom diff --git a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_1_en.png b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_1_en.png index 1392d3232b..0c9e5f8beb 100644 --- a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9d124615503207b29dbd03c3e3831215f374f650cd45328b26c1313bddc48971 -size 107818 +oid sha256:b6319cbc723b9f3cf0b89c4ec5dc6098a7b6a2a2c053ee5bf5afc21830b8d3f6 +size 110855 diff --git a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_1_en.png b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_1_en.png index 311a5b26cb..9c14a8b24a 100644 --- a/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_1_en.png +++ b/tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_1_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8f57044edb93c4f599a02eee238d4cb5b070a4ca4dd34e66f2d4d55c71e0c044 -size 92785 +oid sha256:59666c1ca888ed089d7cf08fd75cc2f3e993b59c255317ad1af652aadfac8b4e +size 97026