Merge pull request #2977 from element-hq/feature/fga/room_factory
Room : introduce RustRoomFactory and be more robust.
This commit is contained in:
@@ -172,8 +172,11 @@ class TimelineControllerTest {
|
||||
)
|
||||
matrixRoom.givenTimelineFocusedOnEventResult(Result.success(detachedTimeline))
|
||||
val sut = TimelineController(matrixRoom)
|
||||
sut.focusOnEvent(AN_EVENT_ID)
|
||||
sut.activeTimelineFlow().test {
|
||||
awaitItem().also { state ->
|
||||
assertThat(state).isEqualTo(liveTimeline)
|
||||
}
|
||||
sut.focusOnEvent(AN_EVENT_ID)
|
||||
awaitItem().also { state ->
|
||||
assertThat(state).isEqualTo(detachedTimeline)
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package io.element.android.libraries.matrix.impl
|
||||
|
||||
import io.element.android.appconfig.TimelineConfig
|
||||
import io.element.android.libraries.androidutils.file.getSizeOfFiles
|
||||
import io.element.android.libraries.androidutils.file.safeDelete
|
||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
@@ -42,7 +41,6 @@ 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.awaitLoaded
|
||||
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
|
||||
@@ -56,17 +54,12 @@ import io.element.android.libraries.matrix.impl.notification.RustNotificationSer
|
||||
import io.element.android.libraries.matrix.impl.notificationsettings.RustNotificationSettingsService
|
||||
import io.element.android.libraries.matrix.impl.oidc.toRustAction
|
||||
import io.element.android.libraries.matrix.impl.pushers.RustPushersService
|
||||
import io.element.android.libraries.matrix.impl.room.MatrixRoomInfoMapper
|
||||
import io.element.android.libraries.matrix.impl.room.RoomContentForwarder
|
||||
import io.element.android.libraries.matrix.impl.room.RoomSyncSubscriber
|
||||
import io.element.android.libraries.matrix.impl.room.RustMatrixRoom
|
||||
import io.element.android.libraries.matrix.impl.room.map
|
||||
import io.element.android.libraries.matrix.impl.room.RustRoomFactory
|
||||
import io.element.android.libraries.matrix.impl.room.preview.RoomPreviewMapper
|
||||
import io.element.android.libraries.matrix.impl.roomdirectory.RustRoomDirectoryService
|
||||
import io.element.android.libraries.matrix.impl.roomlist.RoomListFactory
|
||||
import io.element.android.libraries.matrix.impl.roomlist.RustRoomListService
|
||||
import io.element.android.libraries.matrix.impl.roomlist.fullRoomWithTimeline
|
||||
import io.element.android.libraries.matrix.impl.roomlist.roomOrNull
|
||||
import io.element.android.libraries.matrix.impl.sync.RustSyncService
|
||||
import io.element.android.libraries.matrix.impl.usersearch.UserProfileMapper
|
||||
import io.element.android.libraries.matrix.impl.usersearch.UserSearchResultMapper
|
||||
@@ -102,14 +95,10 @@ import kotlinx.coroutines.withTimeout
|
||||
import org.matrix.rustcomponents.sdk.BackupState
|
||||
import org.matrix.rustcomponents.sdk.Client
|
||||
import org.matrix.rustcomponents.sdk.ClientDelegate
|
||||
import org.matrix.rustcomponents.sdk.FilterTimelineEventType
|
||||
import org.matrix.rustcomponents.sdk.IgnoredUsersListener
|
||||
import org.matrix.rustcomponents.sdk.NotificationProcessSetup
|
||||
import org.matrix.rustcomponents.sdk.PowerLevels
|
||||
import org.matrix.rustcomponents.sdk.Room
|
||||
import org.matrix.rustcomponents.sdk.RoomListItem
|
||||
import org.matrix.rustcomponents.sdk.TaskHandle
|
||||
import org.matrix.rustcomponents.sdk.TimelineEventTypeFilter
|
||||
import org.matrix.rustcomponents.sdk.use
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
@@ -150,7 +139,6 @@ class RustMatrixClient(
|
||||
private val notificationService = RustNotificationService(sessionId, notificationClient, dispatchers, clock)
|
||||
private val notificationSettingsService = RustNotificationSettingsService(client, dispatchers)
|
||||
.apply { start() }
|
||||
private val roomSyncSubscriber = RoomSyncSubscriber(innerRoomListService, dispatchers)
|
||||
private val encryptionService = RustEncryptionService(
|
||||
client = client,
|
||||
syncService = rustSyncService,
|
||||
@@ -237,15 +225,18 @@ class RustMatrixClient(
|
||||
sessionCoroutineScope = sessionCoroutineScope,
|
||||
)
|
||||
|
||||
private val eventFilters = TimelineConfig.excludedEvents
|
||||
.takeIf { it.isNotEmpty() }
|
||||
?.let { listStateEventType ->
|
||||
TimelineEventTypeFilter.exclude(
|
||||
listStateEventType.map { stateEventType ->
|
||||
FilterTimelineEventType.State(stateEventType.map())
|
||||
}
|
||||
)
|
||||
}
|
||||
private val roomFactory = RustRoomFactory(
|
||||
roomListService = roomListService,
|
||||
innerRoomListService = innerRoomListService,
|
||||
sessionId = sessionId,
|
||||
notificationSettingsService = notificationSettingsService,
|
||||
sessionCoroutineScope = sessionCoroutineScope,
|
||||
dispatchers = dispatchers,
|
||||
systemClock = clock,
|
||||
roomContentForwarder = RoomContentForwarder(innerRoomListService),
|
||||
isKeyBackupEnabled = { client.encryption().use { it.backupState() == BackupState.ENABLED } },
|
||||
getSessionData = { sessionStore.getSession(sessionId.value)!! },
|
||||
)
|
||||
|
||||
override val mediaLoader: MatrixMediaLoader = RustMediaLoader(
|
||||
baseCacheDirectory = baseCacheDirectory,
|
||||
@@ -255,8 +246,6 @@ class RustMatrixClient(
|
||||
|
||||
private val roomMembershipObserver = RoomMembershipObserver()
|
||||
|
||||
private val roomContentForwarder = RoomContentForwarder(innerRoomListService)
|
||||
|
||||
private val clientDelegateTaskHandle: TaskHandle? = client.setDelegate(clientDelegate)
|
||||
|
||||
private val _userProfile: MutableStateFlow<MatrixUser> = MutableStateFlow(
|
||||
@@ -287,31 +276,8 @@ class RustMatrixClient(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getRoom(roomId: RoomId): MatrixRoom? = withContext(sessionDispatcher) {
|
||||
// Check if already in memory...
|
||||
var cachedPairOfRoom = pairOfRoom(roomId)
|
||||
if (cachedPairOfRoom == null) {
|
||||
// ... otherwise, lets wait for the SS to load all rooms and check again.
|
||||
roomListService.allRooms.awaitLoaded()
|
||||
cachedPairOfRoom = pairOfRoom(roomId)
|
||||
}
|
||||
cachedPairOfRoom?.let { (roomListItem, fullRoom) ->
|
||||
RustMatrixRoom(
|
||||
sessionId = sessionId,
|
||||
isKeyBackupEnabled = client.encryption().backupState() == BackupState.ENABLED,
|
||||
roomListItem = roomListItem,
|
||||
innerRoom = fullRoom,
|
||||
innerTimeline = fullRoom.timeline(),
|
||||
roomNotificationSettingsService = notificationSettingsService,
|
||||
sessionCoroutineScope = sessionCoroutineScope,
|
||||
coroutineDispatchers = dispatchers,
|
||||
systemClock = clock,
|
||||
roomContentForwarder = roomContentForwarder,
|
||||
sessionData = sessionStore.getSession(sessionId.value)!!,
|
||||
roomSyncSubscriber = roomSyncSubscriber,
|
||||
matrixRoomInfoMapper = MatrixRoomInfoMapper(),
|
||||
)
|
||||
}
|
||||
override suspend fun getRoom(roomId: RoomId): MatrixRoom? {
|
||||
return roomFactory.create(roomId)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -330,18 +296,6 @@ class RustMatrixClient(
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun pairOfRoom(roomId: RoomId): Pair<RoomListItem, Room>? {
|
||||
val cachedRoomListItem = innerRoomListService.roomOrNull(roomId.value)
|
||||
val fullRoom = cachedRoomListItem?.fullRoomWithTimeline(filter = eventFilters)
|
||||
return if (cachedRoomListItem == null || fullRoom == null) {
|
||||
Timber.d("No room cached for $roomId")
|
||||
null
|
||||
} else {
|
||||
Timber.d("Found room cached for $roomId")
|
||||
Pair(cachedRoomListItem, fullRoom)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun findDM(userId: UserId): RoomId? {
|
||||
return client.getDmRoom(userId.value)?.use { RoomId(it.id()) }
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import io.element.android.libraries.matrix.api.media.FileInfo
|
||||
import io.element.android.libraries.matrix.api.media.ImageInfo
|
||||
import io.element.android.libraries.matrix.api.media.MediaUploadHandler
|
||||
import io.element.android.libraries.matrix.api.media.VideoInfo
|
||||
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
|
||||
import io.element.android.libraries.matrix.api.poll.PollKind
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
|
||||
@@ -48,7 +49,6 @@ import io.element.android.libraries.matrix.api.timeline.ReceiptType
|
||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver
|
||||
import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings
|
||||
import io.element.android.libraries.matrix.impl.notificationsettings.RustNotificationSettingsService
|
||||
import io.element.android.libraries.matrix.impl.room.member.RoomMemberListFetcher
|
||||
import io.element.android.libraries.matrix.impl.room.member.RoomMemberMapper
|
||||
import io.element.android.libraries.matrix.impl.room.powerlevels.RoomPowerLevelsMapper
|
||||
@@ -96,7 +96,7 @@ class RustMatrixRoom(
|
||||
private val roomListItem: RoomListItem,
|
||||
private val innerRoom: InnerRoom,
|
||||
innerTimeline: InnerTimeline,
|
||||
private val roomNotificationSettingsService: RustNotificationSettingsService,
|
||||
private val notificationSettingsService: NotificationSettingsService,
|
||||
sessionCoroutineScope: CoroutineScope,
|
||||
private val coroutineDispatchers: CoroutineDispatchers,
|
||||
private val systemClock: SystemClock,
|
||||
@@ -262,7 +262,7 @@ class RustMatrixRoom(
|
||||
val currentRoomNotificationSettings = currentState.roomNotificationSettings()
|
||||
_roomNotificationSettingsStateFlow.value = MatrixRoomNotificationSettingsState.Pending(prevRoomNotificationSettings = currentRoomNotificationSettings)
|
||||
runCatching {
|
||||
roomNotificationSettingsService.getRoomNotificationSettings(roomId, isEncrypted, isOneToOne).getOrThrow()
|
||||
notificationSettingsService.getRoomNotificationSettings(roomId, isEncrypted, isOneToOne).getOrThrow()
|
||||
}.map {
|
||||
_roomNotificationSettingsStateFlow.value = MatrixRoomNotificationSettingsState.Ready(it)
|
||||
}.onFailure {
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (c) 2024 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.matrix.impl.room
|
||||
|
||||
import io.element.android.appconfig.TimelineConfig
|
||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
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.MatrixRoom
|
||||
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
|
||||
import io.element.android.libraries.matrix.impl.roomlist.roomOrNull
|
||||
import io.element.android.libraries.sessionstorage.api.SessionData
|
||||
import io.element.android.services.toolbox.api.systemclock.SystemClock
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.rustcomponents.sdk.FilterTimelineEventType
|
||||
import org.matrix.rustcomponents.sdk.Room
|
||||
import org.matrix.rustcomponents.sdk.RoomListException
|
||||
import org.matrix.rustcomponents.sdk.RoomListItem
|
||||
import org.matrix.rustcomponents.sdk.TimelineEventTypeFilter
|
||||
import timber.log.Timber
|
||||
import org.matrix.rustcomponents.sdk.RoomListService as InnerRoomListService
|
||||
|
||||
class RustRoomFactory(
|
||||
private val sessionId: SessionId,
|
||||
private val notificationSettingsService: NotificationSettingsService,
|
||||
private val sessionCoroutineScope: CoroutineScope,
|
||||
private val dispatchers: CoroutineDispatchers,
|
||||
private val systemClock: SystemClock,
|
||||
private val roomContentForwarder: RoomContentForwarder,
|
||||
private val roomListService: RoomListService,
|
||||
private val innerRoomListService: InnerRoomListService,
|
||||
private val isKeyBackupEnabled: suspend () -> Boolean,
|
||||
private val getSessionData: suspend () -> SessionData,
|
||||
) {
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
private val createRoomDispatcher = dispatchers.io.limitedParallelism(1)
|
||||
private val mutex = Mutex()
|
||||
|
||||
private val matrixRoomInfoMapper = MatrixRoomInfoMapper()
|
||||
|
||||
private val roomSyncSubscriber: RoomSyncSubscriber = RoomSyncSubscriber(innerRoomListService, dispatchers)
|
||||
|
||||
private val eventFilters = TimelineConfig.excludedEvents
|
||||
.takeIf { it.isNotEmpty() }
|
||||
?.let { listStateEventType ->
|
||||
TimelineEventTypeFilter.exclude(
|
||||
listStateEventType.map { stateEventType ->
|
||||
FilterTimelineEventType.State(stateEventType.map())
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun create(roomId: RoomId): MatrixRoom? = withContext(createRoomDispatcher) {
|
||||
var cachedPairOfRoom: Pair<RoomListItem, Room>?
|
||||
mutex.withLock {
|
||||
// Check if already in memory...
|
||||
cachedPairOfRoom = pairOfRoom(roomId)
|
||||
if (cachedPairOfRoom == null) {
|
||||
// ... otherwise, lets wait for the SS to load all rooms and check again.
|
||||
roomListService.allRooms.awaitLoaded()
|
||||
cachedPairOfRoom = pairOfRoom(roomId)
|
||||
}
|
||||
}
|
||||
if (cachedPairOfRoom == null) {
|
||||
Timber.d("No room found for $roomId")
|
||||
return@withContext null
|
||||
}
|
||||
cachedPairOfRoom?.let { (roomListItem, fullRoom) ->
|
||||
RustMatrixRoom(
|
||||
sessionId = sessionId,
|
||||
isKeyBackupEnabled = isKeyBackupEnabled(),
|
||||
roomListItem = roomListItem,
|
||||
innerRoom = fullRoom,
|
||||
innerTimeline = fullRoom.timeline(),
|
||||
notificationSettingsService = notificationSettingsService,
|
||||
sessionCoroutineScope = sessionCoroutineScope,
|
||||
coroutineDispatchers = dispatchers,
|
||||
systemClock = systemClock,
|
||||
roomContentForwarder = roomContentForwarder,
|
||||
sessionData = getSessionData(),
|
||||
roomSyncSubscriber = roomSyncSubscriber,
|
||||
matrixRoomInfoMapper = matrixRoomInfoMapper,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun pairOfRoom(roomId: RoomId): Pair<RoomListItem, Room>? {
|
||||
val cachedRoomListItem = innerRoomListService.roomOrNull(roomId.value)
|
||||
val fullRoom = try {
|
||||
cachedRoomListItem?.fullRoomWithTimeline(filter = eventFilters)
|
||||
} catch (e: RoomListException) {
|
||||
Timber.e(e, "Failed to get full room with timeline for $roomId")
|
||||
null
|
||||
}
|
||||
return if (cachedRoomListItem == null || fullRoom == null) {
|
||||
Timber.d("No room cached for $roomId")
|
||||
null
|
||||
} else {
|
||||
Timber.d("Found room cached for $roomId")
|
||||
Pair(cachedRoomListItem, fullRoom)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ import kotlinx.coroutines.flow.onEach
|
||||
import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind
|
||||
import org.matrix.rustcomponents.sdk.RoomListEntriesListener
|
||||
import org.matrix.rustcomponents.sdk.RoomListEntriesUpdate
|
||||
import org.matrix.rustcomponents.sdk.RoomListException
|
||||
import org.matrix.rustcomponents.sdk.RoomListInterface
|
||||
import org.matrix.rustcomponents.sdk.RoomListItem
|
||||
import org.matrix.rustcomponents.sdk.RoomListLoadingState
|
||||
@@ -129,7 +130,7 @@ internal fun RoomListServiceInterface.syncIndicator(): Flow<RoomListServiceSyncI
|
||||
internal suspend fun RoomListServiceInterface.roomOrNull(roomId: String): RoomListItem? {
|
||||
return try {
|
||||
room(roomId)
|
||||
} catch (exception: Exception) {
|
||||
} catch (exception: RoomListException) {
|
||||
Timber.d(exception, "Failed finding room with id=$roomId.")
|
||||
return null
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user