Split MatrixRoom into BaseRoom and JoinedRoom (#4561)

`JoinedRoom` will now contain both a mandatory live timeline reference and all the functionality associated to it.

`BaseRoom` on the other hand will contain only functionality that's shared for both joined and not joined rooms.

`NotJoinedRoom` is a wrapper around `RoomPreviewInfo` data and a possible local `BaseRoom`, if it exists.

The `RustRoomFactory` cache is now gone since the persistent event cache should have the same effect.
This commit is contained in:
Jorge Martin Espinosa 2025-04-23 15:53:40 +02:00 committed by GitHub
parent 91cb84ce8d
commit 619aa6f2de
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
193 changed files with 2921 additions and 2567 deletions

View file

@ -14,7 +14,7 @@ import io.element.android.libraries.matrix.api.core.EventId
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.MatrixRoom
import io.element.android.libraries.matrix.api.room.JoinedRoom
import io.element.android.libraries.matrix.api.room.isDm
import io.element.android.libraries.matrix.api.room.message.replyInThread
import io.element.android.libraries.matrix.api.timeline.ReceiptType
@ -117,7 +117,7 @@ class NotificationBroadcastReceiverHandler @Inject constructor(
return@launch
}
val client = matrixClientProvider.getOrRestore(sessionId).getOrNull() ?: return@launch
client.getRoom(roomId)?.let { room ->
client.getJoinedRoom(roomId)?.let { room ->
sendMatrixEvent(
sessionId = sessionId,
roomId = roomId,
@ -134,7 +134,7 @@ class NotificationBroadcastReceiverHandler @Inject constructor(
roomId: RoomId,
threadId: ThreadId?,
replyToEventId: EventId?,
room: MatrixRoom,
room: JoinedRoom,
message: String,
) {
// Create a new event to be displayed in the notification drawer, right now

View file

@ -12,7 +12,8 @@ import io.element.android.libraries.featureflag.api.FeatureFlagService
import io.element.android.libraries.featureflag.api.FeatureFlags
import io.element.android.libraries.matrix.api.MatrixClientProvider
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.room.MatrixRoom
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.timeline.MatrixTimelineItem
import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent
import io.element.android.libraries.push.impl.notifications.model.NotifiableRingingCallEvent
@ -37,7 +38,7 @@ class SyncOnNotifiableEvent @Inject constructor(
}
val client = matrixClientProvider.getOrRestore(notifiableEvent.sessionId).getOrNull() ?: return@withContext
client.getRoom(notifiableEvent.roomId)?.use { room ->
client.getJoinedRoom(notifiableEvent.roomId)?.use { room ->
room.subscribeToSync()
// If the app is in foreground, sync is already running, so we just add the subscription above.
@ -60,7 +61,7 @@ class SyncOnNotifiableEvent @Inject constructor(
* User can be in the call if they answer using another session.
* If the user does not join the call, the timeout will be reached.
*/
private suspend fun MatrixRoom.waitsUntilUserIsInTheCall(timeout: Duration) {
private suspend fun BaseRoom.waitsUntilUserIsInTheCall(timeout: Duration) {
withTimeoutOrNull(timeout) {
roomInfoFlow.first {
sessionId in it.activeRoomCallParticipants
@ -68,7 +69,7 @@ class SyncOnNotifiableEvent @Inject constructor(
}
}
private suspend fun MatrixRoom.waitsUntilEventIsKnown(eventId: EventId, timeout: Duration) {
private suspend fun JoinedRoom.waitsUntilEventIsKnown(eventId: EventId, timeout: Duration) {
withTimeoutOrNull(timeout) {
liveTimeline.timelineItems.first { timelineItems ->
timelineItems.any { timelineItem ->

View file

@ -37,7 +37,7 @@ private const val A_USER_AVATAR_1 = "mxc://userAvatar1"
private const val A_USER_AVATAR_2 = "mxc://userAvatar2"
@RunWith(RobolectricTestRunner::class)
class DefaultRoomGroupMessageCreatorTest {
class DefaultBaseRoomGroupMessageCreatorTest {
@Test
fun `test createRoomMessage with one Event`() = runTest {
val sut = createRoomGroupMessageCreator()

View file

@ -27,7 +27,8 @@ import io.element.android.libraries.matrix.test.A_THREAD_ID
import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.test.FakeMatrixClientProvider
import io.element.android.libraries.matrix.test.core.aBuildMeta
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
import io.element.android.libraries.matrix.test.room.FakeBaseRoom
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
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.timeline.FakeTimeline
@ -50,6 +51,7 @@ import io.element.android.tests.testutils.lambda.value
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
@ -221,13 +223,13 @@ class NotificationBroadcastReceiverHandlerTest {
getLambda = getLambda
)
val clearMessagesForRoomLambda = lambdaRecorder<SessionId, RoomId, Unit> { _, _ -> }
val matrixRoom = FakeMatrixRoom()
val joinedRoom = FakeJoinedRoom()
val fakeNotificationCleaner = FakeNotificationCleaner(
clearMessagesForRoomLambda = clearMessagesForRoomLambda,
)
val sut = createNotificationBroadcastReceiverHandler(
sessionPreferencesStore = sessionPreferencesStore,
matrixRoom = matrixRoom,
joinedRoom = joinedRoom,
notificationCleaner = fakeNotificationCleaner
)
sut.onReceive(
@ -240,7 +242,7 @@ class NotificationBroadcastReceiverHandlerTest {
clearMessagesForRoomLambda.assertions()
.isCalledOnce()
.with(value(A_SESSION_ID), value(A_ROOM_ID))
assertThat(matrixRoom.markAsReadCalls).isEqualTo(listOf(expectedReceiptType))
assertThat(joinedRoom.baseRoom.markAsReadCalls).isEqualTo(listOf(expectedReceiptType))
}
@Test
@ -292,15 +294,15 @@ class NotificationBroadcastReceiverHandlerTest {
@Test
fun `Test reject room`() = runTest {
val leaveRoom = lambdaRecorder<Result<Unit>> { Result.success(Unit) }
val matrixRoom = FakeMatrixRoom(
leaveRoomLambda = leaveRoom
val joinedRoom = FakeJoinedRoom(
baseRoom = FakeBaseRoom(leaveRoomLambda = leaveRoom),
)
val clearMembershipNotificationForRoomLambda = lambdaRecorder<SessionId, RoomId, Unit> { _, _ -> }
val fakeNotificationCleaner = FakeNotificationCleaner(
clearMembershipNotificationForRoomLambda = clearMembershipNotificationForRoomLambda,
)
val sut = createNotificationBroadcastReceiverHandler(
matrixRoom = matrixRoom,
joinedRoom = joinedRoom,
notificationCleaner = fakeNotificationCleaner
)
sut.onReceive(
@ -313,6 +315,9 @@ class NotificationBroadcastReceiverHandlerTest {
clearMembershipNotificationForRoomLambda.assertions()
.isCalledOnce()
.with(value(A_SESSION_ID), value(A_ROOM_ID))
advanceUntilIdle()
leaveRoom.assertions()
.isCalledOnce()
.with()
@ -337,9 +342,9 @@ class NotificationBroadcastReceiverHandlerTest {
sendMessageLambda = sendMessage
replyMessageLambda = replyMessage
}
val matrixRoom = FakeMatrixRoom(
val joinedRoom = FakeJoinedRoom(
liveTimeline = liveTimeline,
getUpdatedMemberResult = { Result.success(aRoomMember()) },
baseRoom = FakeBaseRoom(getUpdatedMemberResult = { Result.success(aRoomMember()) }),
).apply {
givenRoomInfo(
aRoomInfo(
@ -351,7 +356,7 @@ class NotificationBroadcastReceiverHandlerTest {
val onNotifiableEventReceivedResult = lambdaRecorder<NotifiableEvent, Unit> { _ -> }
val onNotifiableEventReceived = FakeOnNotifiableEventReceived(onNotifiableEventReceivedResult = onNotifiableEventReceivedResult)
val sut = createNotificationBroadcastReceiverHandler(
matrixRoom = matrixRoom,
joinedRoom = joinedRoom,
onNotifiableEventReceived = onNotifiableEventReceived,
replyMessageExtractor = FakeReplyMessageExtractor(A_MESSAGE)
)
@ -377,11 +382,11 @@ class NotificationBroadcastReceiverHandlerTest {
val liveTimeline = FakeTimeline().apply {
sendMessageLambda = sendMessage
}
val matrixRoom = FakeMatrixRoom(
val joinedRoom = FakeJoinedRoom(
liveTimeline = liveTimeline
)
val sut = createNotificationBroadcastReceiverHandler(
matrixRoom = matrixRoom,
joinedRoom = joinedRoom,
replyMessageExtractor = FakeReplyMessageExtractor(" "),
)
sut.onReceive(
@ -404,9 +409,9 @@ class NotificationBroadcastReceiverHandlerTest {
sendMessageLambda = sendMessage
replyMessageLambda = replyMessage
}
val matrixRoom = FakeMatrixRoom(
val joinedRoom = FakeJoinedRoom(
liveTimeline = liveTimeline,
getUpdatedMemberResult = { Result.success(aRoomMember()) },
baseRoom = FakeBaseRoom(getUpdatedMemberResult = { Result.success(aRoomMember()) }),
).apply {
givenRoomInfo(
aRoomInfo(
@ -418,7 +423,7 @@ class NotificationBroadcastReceiverHandlerTest {
val onNotifiableEventReceivedResult = lambdaRecorder<NotifiableEvent, Unit> { _ -> }
val onNotifiableEventReceived = FakeOnNotifiableEventReceived(onNotifiableEventReceivedResult = onNotifiableEventReceivedResult)
val sut = createNotificationBroadcastReceiverHandler(
matrixRoom = matrixRoom,
joinedRoom = joinedRoom,
onNotifiableEventReceived = onNotifiableEventReceived,
replyMessageExtractor = FakeReplyMessageExtractor(A_MESSAGE)
)
@ -460,10 +465,10 @@ class NotificationBroadcastReceiverHandlerTest {
}
private fun TestScope.createNotificationBroadcastReceiverHandler(
matrixRoom: FakeMatrixRoom? = FakeMatrixRoom(),
joinedRoom: FakeJoinedRoom? = FakeJoinedRoom(),
joinRoom: (RoomId) -> Result<RoomSummary?> = { lambdaError() },
matrixClient: MatrixClient? = FakeMatrixClient().apply {
givenGetRoomResult(A_ROOM_ID, matrixRoom)
givenGetRoomResult(A_ROOM_ID, joinedRoom)
joinRoomLambda = joinRoom
},
sessionPreferencesStore: SessionPreferencesStoreFactory = FakeSessionPreferencesStoreFactory(),

View file

@ -18,7 +18,8 @@ import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.matrix.test.A_UNIQUE_ID
import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.test.FakeMatrixClientProvider
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
import io.element.android.libraries.matrix.test.room.FakeBaseRoom
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
import io.element.android.libraries.matrix.test.room.aRoomInfo
import io.element.android.libraries.matrix.test.sync.FakeSyncService
import io.element.android.libraries.matrix.test.timeline.FakeTimeline
@ -49,10 +50,12 @@ class SyncOnNotifiableEventTest {
private val liveTimeline = FakeTimeline(
timelineItems = timelineItems,
)
private val room = FakeMatrixRoom(
roomId = A_ROOM_ID,
private val room = FakeJoinedRoom(
liveTimeline = liveTimeline,
subscribeToSyncLambda = subscribeToSyncLambda
baseRoom = FakeBaseRoom(
roomId = A_ROOM_ID,
subscribeToSyncLambda = subscribeToSyncLambda,
),
)
private val syncService = FakeSyncService(SyncState.Idle).also {
it.startSyncLambda = startSyncLambda