Skip to content

Commit 7238af7

Browse files
authored
Fix not being able to decline an invite from the room list (#3466)
* Add `InvitedRoom` to wrap Rust SDK Rooms in 'invited' membership state. At the moment, this is a wrapper that allows us to call `Room.leave()` without having to initialise the room's timeline (which is impossible). * Add `MatrixRoom.getInvitedRoom(roomId)` to get one of these rooms. Also, `RustRoomFactory` now has a `createInvitedRoom` method for this. * Adapt `AcceptDeclineInvitePresenter` to use the new APIs.
1 parent 764692b commit 7238af7

File tree

9 files changed

+122
-15
lines changed

9 files changed

+122
-15
lines changed

features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenter.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,8 @@ class AcceptDeclineInvitePresenter @Inject constructor(
112112

113113
private fun CoroutineScope.declineInvite(roomId: RoomId, declinedAction: MutableState<AsyncAction<RoomId>>) = launch {
114114
suspend {
115-
client.getRoom(roomId)?.use {
116-
it.leave().getOrThrow()
115+
client.getInvitedRoom(roomId)?.use {
116+
it.declineInvite().getOrThrow()
117117
notificationCleaner.clearMembershipNotificationForRoom(client.sessionId, roomId)
118118
}
119119
roomId

features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenterTest.kt

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import io.element.android.libraries.matrix.test.A_ROOM_ID
2121
import io.element.android.libraries.matrix.test.A_ROOM_NAME
2222
import io.element.android.libraries.matrix.test.A_SESSION_ID
2323
import io.element.android.libraries.matrix.test.FakeMatrixClient
24-
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
24+
import io.element.android.libraries.matrix.test.room.FakeInvitedRoom
2525
import io.element.android.libraries.matrix.test.room.join.FakeJoinRoom
2626
import io.element.android.libraries.push.api.notifications.NotificationCleaner
2727
import io.element.android.libraries.push.test.notifications.FakeNotificationCleaner
@@ -83,12 +83,7 @@ class AcceptDeclineInvitePresenterTest {
8383
Result.failure<Unit>(RuntimeException("Failed to leave room"))
8484
}
8585
val client = FakeMatrixClient().apply {
86-
givenGetRoomResult(
87-
roomId = A_ROOM_ID,
88-
result = FakeMatrixRoom(
89-
leaveRoomLambda = declineInviteFailure
90-
)
91-
)
86+
getInvitedRoomResults[A_ROOM_ID] = FakeInvitedRoom(declineInviteResult = declineInviteFailure)
9287
}
9388
val presenter = createAcceptDeclineInvitePresenter(client = client)
9489
presenter.test {
@@ -133,12 +128,7 @@ class AcceptDeclineInvitePresenterTest {
133128
Result.success(Unit)
134129
}
135130
val client = FakeMatrixClient().apply {
136-
givenGetRoomResult(
137-
roomId = A_ROOM_ID,
138-
result = FakeMatrixRoom(
139-
leaveRoomLambda = declineInviteSuccess
140-
)
141-
)
131+
getInvitedRoomResults[A_ROOM_ID] = FakeInvitedRoom(declineInviteResult = declineInviteSuccess)
142132
}
143133
val presenter = createAcceptDeclineInvitePresenter(
144134
client = client,

libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import io.element.android.libraries.matrix.api.notification.NotificationService
2121
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
2222
import io.element.android.libraries.matrix.api.oidc.AccountManagementAction
2323
import io.element.android.libraries.matrix.api.pusher.PushersService
24+
import io.element.android.libraries.matrix.api.room.InvitedRoom
2425
import io.element.android.libraries.matrix.api.room.MatrixRoom
2526
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
2627
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
@@ -49,6 +50,7 @@ interface MatrixClient : Closeable {
4950
val sessionCoroutineScope: CoroutineScope
5051
val ignoredUsersFlow: StateFlow<ImmutableList<UserId>>
5152
suspend fun getRoom(roomId: RoomId): MatrixRoom?
53+
suspend fun getInvitedRoom(roomId: RoomId): InvitedRoom?
5254
suspend fun findDM(userId: UserId): RoomId?
5355
suspend fun ignoreUser(userId: UserId): Result<Unit>
5456
suspend fun unignoreUser(userId: UserId): Result<Unit>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright 2024 New Vector Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only
5+
* Please see LICENSE in the repository root for full details.
6+
*/
7+
8+
package io.element.android.libraries.matrix.api.room
9+
10+
import io.element.android.libraries.matrix.api.core.RoomId
11+
import io.element.android.libraries.matrix.api.core.SessionId
12+
13+
/** A reference to a room the current user has been invited to, with the ability to decline the invite. */
14+
interface InvitedRoom : AutoCloseable {
15+
val sessionId: SessionId
16+
val roomId: RoomId
17+
18+
/** Decline the invite to this room. */
19+
suspend fun declineInvite(): Result<Unit>
20+
}

libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import io.element.android.libraries.matrix.api.notificationsettings.Notification
2929
import io.element.android.libraries.matrix.api.oidc.AccountManagementAction
3030
import io.element.android.libraries.matrix.api.pusher.PushersService
3131
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
32+
import io.element.android.libraries.matrix.api.room.InvitedRoom
3233
import io.element.android.libraries.matrix.api.room.MatrixRoom
3334
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
3435
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
@@ -245,6 +246,10 @@ class RustMatrixClient(
245246
return roomFactory.create(roomId)
246247
}
247248

249+
override suspend fun getInvitedRoom(roomId: RoomId): InvitedRoom? {
250+
return roomFactory.createInvitedRoom(roomId)
251+
}
252+
248253
/**
249254
* Wait for the room to be available in the room list, with a membership for the current user of [CurrentUserMembership.JOINED].
250255
* @param roomIdOrAlias the room id or alias to wait for
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright 2024 New Vector Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only
5+
* Please see LICENSE in the repository root for full details.
6+
*/
7+
8+
package io.element.android.libraries.matrix.impl.room
9+
10+
import io.element.android.libraries.matrix.api.core.RoomId
11+
import io.element.android.libraries.matrix.api.core.SessionId
12+
import io.element.android.libraries.matrix.api.room.InvitedRoom
13+
import org.matrix.rustcomponents.sdk.Room
14+
15+
class RustInvitedRoom(
16+
override val sessionId: SessionId,
17+
private val invitedRoom: Room,
18+
) : InvitedRoom {
19+
override val roomId = RoomId(invitedRoom.id())
20+
21+
override suspend fun declineInvite(): Result<Unit> = runCatching {
22+
invitedRoom.leave()
23+
}
24+
25+
override fun close() {
26+
invitedRoom.destroy()
27+
}
28+
}

libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustRoomFactory.kt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import io.element.android.libraries.matrix.api.core.DeviceId
1414
import io.element.android.libraries.matrix.api.core.RoomId
1515
import io.element.android.libraries.matrix.api.core.SessionId
1616
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
17+
import io.element.android.libraries.matrix.api.room.InvitedRoom
1718
import io.element.android.libraries.matrix.api.room.MatrixRoom
1819
import io.element.android.libraries.matrix.api.roomlist.RoomListService
1920
import io.element.android.libraries.matrix.api.roomlist.awaitLoaded
@@ -27,6 +28,7 @@ import kotlinx.coroutines.sync.Mutex
2728
import kotlinx.coroutines.sync.withLock
2829
import kotlinx.coroutines.withContext
2930
import org.matrix.rustcomponents.sdk.FilterTimelineEventType
31+
import org.matrix.rustcomponents.sdk.Membership
3032
import org.matrix.rustcomponents.sdk.Room
3133
import org.matrix.rustcomponents.sdk.RoomListException
3234
import org.matrix.rustcomponents.sdk.RoomListItem
@@ -123,6 +125,33 @@ class RustRoomFactory(
123125
}
124126
}
125127

128+
suspend fun createInvitedRoom(roomId: RoomId): InvitedRoom? = withContext(dispatcher) {
129+
if (isDestroyed) {
130+
Timber.d("Room factory is destroyed, returning null for $roomId")
131+
return@withContext null
132+
}
133+
val roomListItem = innerRoomListService.roomOrNull(roomId.value)
134+
if (roomListItem == null) {
135+
Timber.d("Room not found for $roomId")
136+
return@withContext null
137+
}
138+
if (roomListItem.membership() != Membership.INVITED) {
139+
Timber.d("Room $roomId is not in invited state")
140+
return@withContext null
141+
}
142+
val invitedRoom = try {
143+
roomListItem.invitedRoom()
144+
} catch (e: RoomListException) {
145+
Timber.e(e, "Failed to get invited room for $roomId")
146+
return@withContext null
147+
}
148+
149+
RustInvitedRoom(
150+
sessionId = sessionId,
151+
invitedRoom = invitedRoom,
152+
)
153+
}
154+
126155
private suspend fun getRoomReferences(roomId: RoomId): RustRoomReferences? {
127156
cache[roomId]?.let {
128157
Timber.d("Room found in cache for $roomId")

libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import io.element.android.libraries.matrix.api.notification.NotificationService
2222
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
2323
import io.element.android.libraries.matrix.api.oidc.AccountManagementAction
2424
import io.element.android.libraries.matrix.api.pusher.PushersService
25+
import io.element.android.libraries.matrix.api.room.InvitedRoom
2526
import io.element.android.libraries.matrix.api.room.MatrixRoom
2627
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
2728
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
@@ -99,6 +100,7 @@ class FakeMatrixClient(
99100
private var createDmResult: Result<RoomId> = Result.success(A_ROOM_ID)
100101
private var findDmResult: RoomId? = A_ROOM_ID
101102
private val getRoomResults = mutableMapOf<RoomId, MatrixRoom>()
103+
val getInvitedRoomResults = mutableMapOf<RoomId, InvitedRoom>()
102104
private val searchUserResults = mutableMapOf<String, Result<MatrixSearchUserResults>>()
103105
private val getProfileResults = mutableMapOf<UserId, Result<MatrixUser>>()
104106
private var uploadMediaResult: Result<String> = Result.success(AN_AVATAR_URL)
@@ -125,6 +127,10 @@ class FakeMatrixClient(
125127
return getRoomResults[roomId]
126128
}
127129

130+
override suspend fun getInvitedRoom(roomId: RoomId): InvitedRoom? {
131+
return getInvitedRoomResults[roomId]
132+
}
133+
128134
override suspend fun findDM(userId: UserId): RoomId? {
129135
return findDmResult
130136
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2024 New Vector Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only
5+
* Please see LICENSE in the repository root for full details.
6+
*/
7+
8+
package io.element.android.libraries.matrix.test.room
9+
10+
import io.element.android.libraries.matrix.api.core.RoomId
11+
import io.element.android.libraries.matrix.api.core.SessionId
12+
import io.element.android.libraries.matrix.api.room.InvitedRoom
13+
import io.element.android.libraries.matrix.test.A_ROOM_ID
14+
import io.element.android.libraries.matrix.test.A_SESSION_ID
15+
import io.element.android.tests.testutils.lambda.lambdaError
16+
17+
class FakeInvitedRoom(
18+
override val sessionId: SessionId = A_SESSION_ID,
19+
override val roomId: RoomId = A_ROOM_ID,
20+
private val declineInviteResult: () -> Result<Unit> = { lambdaError() }
21+
) : InvitedRoom {
22+
override suspend fun declineInvite(): Result<Unit> {
23+
return declineInviteResult()
24+
}
25+
26+
override fun close() = Unit
27+
}

0 commit comments

Comments
 (0)