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 082120c6f3..6c8b4d533c 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 @@ -94,8 +94,8 @@ class AcceptDeclineInvitePresenter @Inject constructor( private fun CoroutineScope.declineInvite(roomId: RoomId, declinedAction: MutableState>) = launch { suspend { - client.getInvitedRoom(roomId)?.use { - it.declineInvite().getOrThrow() + client.getPendingRoom(roomId)?.use { + it.leave().getOrThrow() notificationCleaner.clearMembershipNotificationForRoom(client.sessionId, roomId) } roomId 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 6c1057638f..620f899997 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 @@ -22,7 +22,7 @@ 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 import io.element.android.libraries.matrix.test.FakeMatrixClient -import io.element.android.libraries.matrix.test.room.FakeInvitedRoom +import io.element.android.libraries.matrix.test.room.FakePendingRoom import io.element.android.libraries.matrix.test.room.join.FakeJoinRoom import io.element.android.libraries.push.api.notifications.NotificationCleaner import io.element.android.libraries.push.test.notifications.FakeNotificationCleaner @@ -78,7 +78,7 @@ class AcceptDeclineInvitePresenterTest { Result.failure(RuntimeException("Failed to leave room")) } val client = FakeMatrixClient().apply { - getInvitedRoomResults[A_ROOM_ID] = FakeInvitedRoom(declineInviteResult = declineInviteFailure) + getPendingRoomResults[A_ROOM_ID] = FakePendingRoom(declineInviteResult = declineInviteFailure) } val presenter = createAcceptDeclineInvitePresenter(client = client) presenter.test { @@ -121,7 +121,7 @@ class AcceptDeclineInvitePresenterTest { Result.success(Unit) } val client = FakeMatrixClient().apply { - getInvitedRoomResults[A_ROOM_ID] = FakeInvitedRoom(declineInviteResult = declineInviteSuccess) + getPendingRoomResults[A_ROOM_ID] = FakePendingRoom(declineInviteResult = declineInviteSuccess) } val presenter = createAcceptDeclineInvitePresenter( client = client, 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 ea26e29719..d021f9733d 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 @@ -21,7 +21,7 @@ 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.InvitedRoom +import io.element.android.libraries.matrix.api.room.PendingRoom import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoomInfo import io.element.android.libraries.matrix.api.room.RoomMembershipObserver @@ -52,7 +52,7 @@ interface MatrixClient : Closeable { val sessionCoroutineScope: CoroutineScope val ignoredUsersFlow: StateFlow> suspend fun getRoom(roomId: RoomId): MatrixRoom? - suspend fun getInvitedRoom(roomId: RoomId): InvitedRoom? + suspend fun getPendingRoom(roomId: RoomId): PendingRoom? suspend fun findDM(userId: UserId): RoomId? suspend fun ignoreUser(userId: UserId): Result suspend fun unignoreUser(userId: UserId): Result diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/InvitedRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/PendingRoom.kt similarity index 59% rename from libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/InvitedRoom.kt rename to libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/PendingRoom.kt index 7e1dd5d10d..273e038313 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/InvitedRoom.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/PendingRoom.kt @@ -10,11 +10,11 @@ package io.element.android.libraries.matrix.api.room import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId -/** A reference to a room the current user has been invited to, with the ability to decline the invite. */ -interface InvitedRoom : AutoCloseable { +/** A reference to a room the current user has knocked to or has been invited to, with the ability to leave the room. */ +interface PendingRoom : AutoCloseable { val sessionId: SessionId val roomId: RoomId - /** Decline the invite to this room. */ - suspend fun declineInvite(): Result + /** Leave the room ie.decline invite or cancel knock. */ + suspend fun leave(): Result } 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 1abe47a362..02f7f3d71d 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 @@ -30,7 +30,7 @@ import io.element.android.libraries.matrix.api.notificationsettings.Notification 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.CurrentUserMembership -import io.element.android.libraries.matrix.api.room.InvitedRoom +import io.element.android.libraries.matrix.api.room.PendingRoom import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.RoomMembershipObserver import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias @@ -251,19 +251,21 @@ class RustMatrixClient( return roomFactory.create(roomId) } - override suspend fun getInvitedRoom(roomId: RoomId): InvitedRoom? { - return roomFactory.createInvitedRoom(roomId) + override suspend fun getPendingRoom(roomId: RoomId): PendingRoom? { + return roomFactory.createPendingRoom(roomId) } /** - * Wait for the room to be available in the room list, with a membership for the current user of [CurrentUserMembership.JOINED]. + * 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 * @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 awaitJoinedRoom( + private suspend fun awaitRoom( roomIdOrAlias: RoomIdOrAlias, - timeout: Duration + timeout: Duration, + currentUserMembership: CurrentUserMembership, ): RoomSummary { return withTimeout(timeout) { getRoomSummaryFlow(roomIdOrAlias) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustInvitedRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustPendingRoom.kt similarity index 58% rename from libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustInvitedRoom.kt rename to libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustPendingRoom.kt index 67e9e5e7a5..46df9bed80 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustInvitedRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustPendingRoom.kt @@ -9,20 +9,20 @@ package io.element.android.libraries.matrix.impl.room 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.room.InvitedRoom +import io.element.android.libraries.matrix.api.room.PendingRoom import org.matrix.rustcomponents.sdk.Room -class RustInvitedRoom( +class RustPendingRoom( override val sessionId: SessionId, - private val invitedRoom: Room, -) : InvitedRoom { - override val roomId = RoomId(invitedRoom.id()) + private val inner: Room, +) : PendingRoom { + override val roomId = RoomId(inner.id()) - override suspend fun declineInvite(): Result = runCatching { - invitedRoom.leave() + override suspend fun leave(): Result = runCatching { + inner.leave() } override fun close() { - invitedRoom.destroy() + inner.destroy() } } 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 2e41e36183..0656cb9cf9 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 @@ -14,8 +14,8 @@ import io.element.android.libraries.matrix.api.core.DeviceId 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.notificationsettings.NotificationSettingsService -import io.element.android.libraries.matrix.api.room.InvitedRoom import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.api.room.PendingRoom import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.api.roomlist.awaitLoaded import io.element.android.libraries.matrix.impl.roomlist.fullRoomWithTimeline @@ -35,6 +35,7 @@ import timber.log.Timber import org.matrix.rustcomponents.sdk.RoomListService as InnerRoomListService private const val CACHE_SIZE = 16 +private val PENDING_MEMBERSHIPS = setOf(Membership.INVITED, Membership.KNOCKED) class RustRoomFactory( private val sessionId: SessionId, @@ -49,6 +50,7 @@ class RustRoomFactory( private val roomSyncSubscriber: RoomSyncSubscriber, private val timelineEventTypeFilterFactory: TimelineEventTypeFilterFactory, ) { + @OptIn(ExperimentalCoroutinesApi::class) private val dispatcher = dispatchers.io.limitedParallelism(1) private val mutex = Mutex() @@ -120,7 +122,7 @@ class RustRoomFactory( } } - suspend fun createInvitedRoom(roomId: RoomId): InvitedRoom? = withContext(dispatcher) { + suspend fun createPendingRoom(roomId: RoomId): PendingRoom? = withContext(dispatcher) { if (isDestroyed) { Timber.d("Room factory is destroyed, returning null for $roomId") return@withContext null @@ -130,20 +132,19 @@ class RustRoomFactory( Timber.d("Room not found for $roomId") return@withContext null } - if (roomListItem.membership() != Membership.INVITED) { - Timber.d("Room $roomId is not in invited state") + if (roomListItem.membership() !in PENDING_MEMBERSHIPS) { + Timber.d("Room $roomId is not in pending state") return@withContext null } - val invitedRoom = try { - roomListItem.invitedRoom() + val innerRoom = try { + roomListItem.roomWithoutTimeline() } catch (e: RoomListException) { - Timber.e(e, "Failed to get invited room for $roomId") + Timber.e(e, "Failed to get pending room for $roomId") return@withContext null } - - RustInvitedRoom( + RustPendingRoom( sessionId = sessionId, - invitedRoom = invitedRoom, + inner = innerRoom, ) } 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 d784bba502..b71fb6b668 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 @@ -22,7 +22,7 @@ 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.InvitedRoom +import io.element.android.libraries.matrix.api.room.PendingRoom import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.RoomMembershipObserver import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias @@ -101,7 +101,7 @@ class FakeMatrixClient( private var createDmResult: Result = Result.success(A_ROOM_ID) private var findDmResult: RoomId? = A_ROOM_ID private val getRoomResults = mutableMapOf() - val getInvitedRoomResults = mutableMapOf() + val getPendingRoomResults = mutableMapOf() private val searchUserResults = mutableMapOf>() private val getProfileResults = mutableMapOf>() private var uploadMediaResult: Result = Result.success(AN_AVATAR_URL) @@ -128,8 +128,8 @@ class FakeMatrixClient( return getRoomResults[roomId] } - override suspend fun getInvitedRoom(roomId: RoomId): InvitedRoom? { - return getInvitedRoomResults[roomId] + override suspend fun getPendingRoom(roomId: RoomId): PendingRoom? { + return getPendingRoomResults[roomId] } override suspend fun findDM(userId: UserId): RoomId? { diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeInvitedRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakePendingRoom.kt similarity index 81% rename from libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeInvitedRoom.kt rename to libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakePendingRoom.kt index 06416e2c80..c8523b31a6 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeInvitedRoom.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakePendingRoom.kt @@ -9,18 +9,18 @@ package io.element.android.libraries.matrix.test.room 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.room.InvitedRoom +import io.element.android.libraries.matrix.api.room.PendingRoom import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.tests.testutils.lambda.lambdaError import io.element.android.tests.testutils.simulateLongTask -class FakeInvitedRoom( +class FakePendingRoom( override val sessionId: SessionId = A_SESSION_ID, override val roomId: RoomId = A_ROOM_ID, private val declineInviteResult: () -> Result = { lambdaError() } -) : InvitedRoom { - override suspend fun declineInvite(): Result = simulateLongTask { +) : PendingRoom { + override suspend fun leave(): Result = simulateLongTask { declineInviteResult() }