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

@ -7,8 +7,8 @@
package io.element.android.appnav.di
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.room.JoinedRoom
interface RoomComponentFactory {
fun create(room: MatrixRoom): Any
fun create(room: JoinedRoom): Any
}

View file

@ -20,6 +20,7 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
@VisibleForTesting
@ -42,7 +43,9 @@ class SendQueues @Inject constructor(
) { syncState, _ -> syncState }
.debounce(SEND_QUEUES_RETRY_DELAY_MILLIS)
.onEach { syncState ->
Timber.tag("SendQueues").d("Sync state changed: $syncState")
if (syncState == SyncState.Running) {
Timber.tag("SendQueues").d("Enabling send queues again")
matrixClient.setAllSendQueuesEnabled(enabled = true)
}
}

View file

@ -35,7 +35,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.UserId
import io.element.android.libraries.matrix.api.permalink.PermalinkData
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.room.JoinedRoom
import io.element.android.services.appnavstate.api.AppNavigationStateService
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@ -72,7 +72,7 @@ class JoinedRoomLoadedFlowNode @AssistedInject constructor(
}
data class Inputs(
val room: MatrixRoom,
val room: JoinedRoom,
val initialElement: RoomNavigationTarget,
) : NodeInputs
@ -95,6 +95,7 @@ class JoinedRoomLoadedFlowNode @AssistedInject constructor(
},
onDestroy = {
Timber.v("OnDestroy")
inputs.room.destroy()
appNavigationStateService.onLeavingRoom(id)
}
)

View file

@ -13,7 +13,7 @@ import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.di.SingleIn
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.room.JoinedRoom
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
@ -27,7 +27,7 @@ import javax.inject.Inject
sealed interface LoadingRoomState {
data object Loading : LoadingRoomState
data object Error : LoadingRoomState
data class Loaded(val room: MatrixRoom) : LoadingRoomState
data class Loaded(val room: JoinedRoom) : LoadingRoomState
}
open class LoadingRoomStateProvider : PreviewParameterProvider<LoadingRoomState> {
@ -41,7 +41,7 @@ open class LoadingRoomStateProvider : PreviewParameterProvider<LoadingRoomState>
@SingleIn(SessionScope::class)
class LoadingRoomStateFlowFactory @Inject constructor(private val matrixClient: MatrixClient) {
fun create(lifecycleScope: CoroutineScope, roomId: RoomId): StateFlow<LoadingRoomState> =
getRoomFlow(roomId)
getJoinedRoomFlow(roomId)
.map { room ->
if (room != null) {
LoadingRoomState.Loaded(room)
@ -51,8 +51,8 @@ class LoadingRoomStateFlowFactory @Inject constructor(private val matrixClient:
}
.stateIn(lifecycleScope, SharingStarted.Eagerly, LoadingRoomState.Loading)
private fun getRoomFlow(roomId: RoomId): Flow<MatrixRoom?> = suspend {
matrixClient.getRoom(roomId = roomId)
private fun getJoinedRoomFlow(roomId: RoomId): Flow<JoinedRoom?> = suspend {
matrixClient.getJoinedRoom(roomId = roomId)
}
.asFlow()
}

View file

@ -23,16 +23,17 @@ import io.element.android.appnav.room.joined.JoinedRoomLoadedFlowNode
import io.element.android.features.messages.api.MessagesEntryPoint
import io.element.android.features.roomdetails.api.RoomDetailsEntryPoint
import io.element.android.libraries.architecture.childNode
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.room.JoinedRoom
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.FakeBaseRoom
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
import io.element.android.services.appnavstate.test.FakeAppNavigationStateService
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
class JoinRoomLoadedFlowNodeTest {
class JoinBaseRoomLoadedFlowNodeTest {
@get:Rule
val instantTaskExecutorRule = InstantTaskExecutorRule()
@ -68,7 +69,7 @@ class JoinRoomLoadedFlowNodeTest {
}
private class FakeRoomComponentFactory : RoomComponentFactory {
override fun create(room: MatrixRoom): Any {
override fun create(room: JoinedRoom): Any {
return Unit
}
}
@ -114,9 +115,7 @@ class JoinRoomLoadedFlowNodeTest {
@Test
fun `given a room flow node when initialized then it loads messages entry point`() = runTest {
// GIVEN
val room = FakeMatrixRoom(
updateMembersResult = { }
)
val room = FakeJoinedRoom(baseRoom = FakeBaseRoom(updateMembersResult = {}))
val fakeMessagesEntryPoint = FakeMessagesEntryPoint()
val inputs = JoinedRoomLoadedFlowNode.Inputs(room, RoomNavigationTarget.Messages())
val roomFlowNode = createJoinedRoomLoadedFlowNode(
@ -137,9 +136,7 @@ class JoinRoomLoadedFlowNodeTest {
@Test
fun `given a room flow node when callback on room details is triggered then it loads room details entry point`() = runTest {
// GIVEN
val room = FakeMatrixRoom(
updateMembersResult = { }
)
val room = FakeJoinedRoom(baseRoom = FakeBaseRoom(updateMembersResult = {}))
val fakeMessagesEntryPoint = FakeMessagesEntryPoint()
val fakeRoomDetailsEntryPoint = FakeRoomDetailsEntryPoint()
val inputs = JoinedRoomLoadedFlowNode.Inputs(room, RoomNavigationTarget.Messages())

View file

@ -10,7 +10,7 @@ package io.element.android.appnav.loggedin
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.sync.SyncState
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.FakeJoinedRoom
import io.element.android.libraries.matrix.test.sync.FakeSyncService
import io.element.android.tests.testutils.lambda.assert
import io.element.android.tests.testutils.lambda.lambdaRecorder
@ -35,8 +35,8 @@ class SendQueuesTest {
matrixClient.sendQueueDisabledFlow = sendQueueDisabledFlow
matrixClient.setAllSendQueuesEnabledLambda = setAllSendQueuesEnabledLambda
val setRoomSendQueueEnabledLambda = lambdaRecorder { _: Boolean -> }
val room = FakeMatrixRoom(
setSendQueueEnabledLambda = setRoomSendQueueEnabledLambda
val room = FakeJoinedRoom(
setSendQueueEnabledResult = setRoomSendQueueEnabledLambda
)
matrixClient.givenGetRoomResult(room.roomId, room)
sut.launchIn(backgroundScope)
@ -61,8 +61,8 @@ class SendQueuesTest {
matrixClient.setAllSendQueuesEnabledLambda = setAllSendQueuesEnabledLambda
syncService.emitSyncState(SyncState.Offline)
val setRoomSendQueueEnabledLambda = lambdaRecorder { _: Boolean -> }
val room = FakeMatrixRoom(
setSendQueueEnabledLambda = setRoomSendQueueEnabledLambda
val room = FakeJoinedRoom(
setSendQueueEnabledResult = setRoomSendQueueEnabledLambda
)
matrixClient.givenGetRoomResult(room.roomId, room)

View file

@ -15,15 +15,16 @@ import io.element.android.libraries.matrix.api.roomlist.RoomList
import io.element.android.libraries.matrix.test.A_ROOM_ID
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.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.roomlist.FakeRoomListService
import kotlinx.coroutines.test.runTest
import org.junit.Test
class LoadingRoomStateFlowFactoryTest {
class LoadingBaseRoomStateFlowFactoryTest {
@Test
fun `flow should emit Loading and then Loaded when there is a room in cache`() = runTest {
val room = FakeMatrixRoom(sessionId = A_SESSION_ID, roomId = A_ROOM_ID)
val room = FakeJoinedRoom(baseRoom = FakeBaseRoom(sessionId = A_SESSION_ID, roomId = A_ROOM_ID))
val matrixClient = FakeMatrixClient(A_SESSION_ID).apply {
givenGetRoomResult(A_ROOM_ID, room)
}
@ -38,7 +39,7 @@ class LoadingRoomStateFlowFactoryTest {
@Test
fun `flow should emit Loading and then Loaded when there is a room in cache after SS is loaded`() = runTest {
val room = FakeMatrixRoom(sessionId = A_SESSION_ID, roomId = A_ROOM_ID)
val room = FakeJoinedRoom(baseRoom = FakeBaseRoom(sessionId = A_SESSION_ID, roomId = A_ROOM_ID))
val roomListService = FakeRoomListService()
val matrixClient = FakeMatrixClient(A_SESSION_ID, roomListService = roomListService)
val flowFactory = LoadingRoomStateFlowFactory(matrixClient)