From c48cab9481958c6d346cfaed1067e0fb5775f5d8 Mon Sep 17 00:00:00 2001 From: SpiritCroc Date: Sun, 29 Sep 2024 12:44:13 +0200 Subject: [PATCH] Update sort order in the SDK without recreating duplicate room lists Change-Id: Ieffbfc5199d8c9c2d06d32b52993497f17aac339 --- .../element/android/x/ElementXApplication.kt | 1 + .../io/element/android/x/MainActivity.kt | 2 - .../x/ScElementXApplicationExtensions.kt | 22 +++++++ .../roomlist/ScInboxSettingsSource.kt | 32 ++++++++++ .../roomlist/ScRoomSortOrderSource.kt | 42 -------------- .../impl/datasource/RoomListDataSource.kt | 9 +-- .../impl/filters/RoomListFiltersPresenter.kt | 3 +- gradle/libs.versions.toml | 2 +- .../matrix/api/roomlist/DynamicRoomList.kt | 2 + .../matrix/api/roomlist/RoomListService.kt | 12 ---- ...RoomSortOrder.kt => ScSdkRoomSortOrder.kt} | 5 +- libraries/matrix/impl/build.gradle.kts | 1 + .../libraries/matrix/impl/RustMatrixClient.kt | 3 + .../matrix/impl/RustMatrixClientFactory.kt | 3 + .../impl/roomlist/RoomListDynamicEvents.kt | 2 + .../impl/roomlist/RoomListExtensions.kt | 30 ++++++++-- .../matrix/impl/roomlist/RoomListFactory.kt | 11 +++- .../impl/roomlist/RustRoomListService.kt | 58 +++++-------------- .../roomlist/ScRoomListFactoryExtensions.kt | 21 ++++--- .../test/roomlist/FakeRoomListService.kt | 6 -- .../test/roomlist/SimplePagedRoomList.kt | 3 + 21 files changed, 141 insertions(+), 129 deletions(-) create mode 100644 app/src/main/kotlin/io/element/android/x/ScElementXApplicationExtensions.kt create mode 100644 features/roomlist/impl/src/main/kotlin/chat/schildi/features/roomlist/ScInboxSettingsSource.kt delete mode 100644 features/roomlist/impl/src/main/kotlin/chat/schildi/features/roomlist/ScRoomSortOrderSource.kt rename libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/{ScRoomSortOrder.kt => ScSdkRoomSortOrder.kt} (68%) diff --git a/app/src/main/kotlin/io/element/android/x/ElementXApplication.kt b/app/src/main/kotlin/io/element/android/x/ElementXApplication.kt index a27ffd3a34..87203a5a7d 100644 --- a/app/src/main/kotlin/io/element/android/x/ElementXApplication.kt +++ b/app/src/main/kotlin/io/element/android/x/ElementXApplication.kt @@ -28,6 +28,7 @@ class ElementXApplication : Application(), DaggerComponentOwner { initializeComponent(CrashInitializer::class.java) initializeComponent(TracingInitializer::class.java) initializeComponent(CacheCleanerInitializer::class.java) + initializeComponent(ScInitializer::class.java) // SC } EmojiCompat.init(BundledEmojiCompatConfig(this)) // SC logApplicationInfo(this) diff --git a/app/src/main/kotlin/io/element/android/x/MainActivity.kt b/app/src/main/kotlin/io/element/android/x/MainActivity.kt index f508666aed..09b98b9221 100644 --- a/app/src/main/kotlin/io/element/android/x/MainActivity.kt +++ b/app/src/main/kotlin/io/element/android/x/MainActivity.kt @@ -17,7 +17,6 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalUriHandler import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen @@ -68,7 +67,6 @@ class MainActivity : NodeActivity() { LocalUriHandler provides SafeUriHandler(this), LocalAnalyticsService provides appBindings.analyticsService(), ) { - LaunchedEffect(Unit) { appBindings.scPreferencesStore().prefetch() } Box( modifier = Modifier .fillMaxSize() diff --git a/app/src/main/kotlin/io/element/android/x/ScElementXApplicationExtensions.kt b/app/src/main/kotlin/io/element/android/x/ScElementXApplicationExtensions.kt new file mode 100644 index 0000000000..c697665794 --- /dev/null +++ b/app/src/main/kotlin/io/element/android/x/ScElementXApplicationExtensions.kt @@ -0,0 +1,22 @@ +package io.element.android.x + +import android.content.Context +import androidx.startup.Initializer +import io.element.android.libraries.architecture.bindings +import io.element.android.x.di.AppBindings +import kotlinx.coroutines.runBlocking +import timber.log.Timber + +class ScInitializer : Initializer { + override fun create(context: Context) { + val ts = System.currentTimeMillis() + val appBindings = context.bindings() + // runBlocking is not nice, but we want to make sure that preferences are ready before creating room list service + runBlocking { + appBindings.scPreferencesStore().prefetch() + } + Timber.d("Initialized SC dependencies in ${System.currentTimeMillis() - ts} ms") + } + + override fun dependencies(): List>> = emptyList() +} diff --git a/features/roomlist/impl/src/main/kotlin/chat/schildi/features/roomlist/ScInboxSettingsSource.kt b/features/roomlist/impl/src/main/kotlin/chat/schildi/features/roomlist/ScInboxSettingsSource.kt new file mode 100644 index 0000000000..7da3c329ac --- /dev/null +++ b/features/roomlist/impl/src/main/kotlin/chat/schildi/features/roomlist/ScInboxSettingsSource.kt @@ -0,0 +1,32 @@ +package chat.schildi.features.roomlist + +import chat.schildi.lib.preferences.ScPreferencesStore +import chat.schildi.lib.preferences.ScPrefs +import io.element.android.libraries.matrix.api.roomlist.RoomListService +import io.element.android.libraries.matrix.api.roomlist.ScSdkInboxSettings +import io.element.android.libraries.matrix.api.roomlist.ScSdkRoomSortOrder +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import javax.inject.Inject + +class ScInboxSettingsSource @Inject constructor( + private val scPreferencesStore: ScPreferencesStore, + private val roomListService: RoomListService, +) { + fun launchIn(scope: CoroutineScope) { + scPreferencesStore.combinedSettingFlow { lookup -> + ScSdkInboxSettings( + sortOrder = ScSdkRoomSortOrder( + byUnread = ScPrefs.SORT_BY_UNREAD.let { it.ensureType(lookup(it)) ?: it.defaultValue }, + pinFavourites = ScPrefs.PIN_FAVORITES.let { it.ensureType(lookup(it)) ?: it.defaultValue }, + buryLowPriority = ScPrefs.BURY_LOW_PRIORITY.let { it.ensureType(lookup(it)) ?: it.defaultValue }, + clientSideUnreadCounts = ScPrefs.CLIENT_GENERATED_UNREAD_COUNTS.let { it.ensureType(lookup(it)) ?: it.defaultValue }, + withSilentUnread = ScPrefs.SORT_WITH_SILENT_UNREAD.let { it.ensureType(lookup(it)) ?: it.defaultValue }, + ) + ) + }.onEach { inboxSettings -> + roomListService.allRooms.updateSettings(inboxSettings) + }.launchIn(scope) + } +} diff --git a/features/roomlist/impl/src/main/kotlin/chat/schildi/features/roomlist/ScRoomSortOrderSource.kt b/features/roomlist/impl/src/main/kotlin/chat/schildi/features/roomlist/ScRoomSortOrderSource.kt deleted file mode 100644 index c1779358a7..0000000000 --- a/features/roomlist/impl/src/main/kotlin/chat/schildi/features/roomlist/ScRoomSortOrderSource.kt +++ /dev/null @@ -1,42 +0,0 @@ -package chat.schildi.features.roomlist - -import chat.schildi.lib.preferences.ScPreferencesStore -import chat.schildi.lib.preferences.ScPrefs -import io.element.android.libraries.matrix.api.roomlist.RoomList -import io.element.android.libraries.matrix.api.roomlist.RoomListFilter -import io.element.android.libraries.matrix.api.roomlist.RoomListService -import io.element.android.libraries.matrix.api.roomlist.RoomSummary -import io.element.android.libraries.matrix.api.roomlist.ScRoomSortOrder -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flatMapLatest -import timber.log.Timber -import javax.inject.Inject - -class ScRoomSortOrderSource @Inject constructor( - private val scPreferencesStore: ScPreferencesStore, - private val roomListService: RoomListService, -) { - @OptIn(ExperimentalCoroutinesApi::class) - fun filteredSummaries(): Flow> { - // Listen to preferences relevant to sort order, then apply these to the dynamic room list - return scPreferencesStore.combinedSettingFlow { lookup -> - ScRoomSortOrder( - byUnread = ScPrefs.SORT_BY_UNREAD.let { it.ensureType(lookup(it)) ?: it.defaultValue }, - pinFavourites = ScPrefs.PIN_FAVORITES.let { it.ensureType(lookup(it)) ?: it.defaultValue }, - buryLowPriority = ScPrefs.BURY_LOW_PRIORITY.let { it.ensureType(lookup(it)) ?: it.defaultValue }, - clientSideUnreadCounts = ScPrefs.CLIENT_GENERATED_UNREAD_COUNTS.let { it.ensureType(lookup(it)) ?: it.defaultValue }, - withSilentUnread = ScPrefs.SORT_WITH_SILENT_UNREAD.let { it.ensureType(lookup(it)) ?: it.defaultValue }, - ) - }.flatMapLatest { sortOrder -> - // TODO: would be nice to teach the SDK to update sort order without recreating the whole list - // Cancel jobs for previous list - Timber.d("Request sorted room list for $sortOrder") - roomListService.getOrReplaceRoomListWithSortOrder( - pageSize = 30, - initialFilter = RoomListFilter.all(), - sortOrder = sortOrder, - ).filteredSummaries - } - } -} diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt index 000694ef12..4a9041952f 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListDataSource.kt @@ -7,7 +7,7 @@ package io.element.android.features.roomlist.impl.datasource -import chat.schildi.features.roomlist.ScRoomSortOrderSource +import chat.schildi.features.roomlist.ScInboxSettingsSource import io.element.android.features.roomlist.impl.model.RoomListRoomSummary import io.element.android.libraries.androidutils.diff.DiffCacheUpdater import io.element.android.libraries.androidutils.diff.MutableListDiffCache @@ -36,7 +36,7 @@ class RoomListDataSource @Inject constructor( private val roomListRoomSummaryFactory: RoomListRoomSummaryFactory, private val coroutineDispatchers: CoroutineDispatchers, private val notificationSettingsService: NotificationSettingsService, - private val scRoomSortOrderSource: ScRoomSortOrderSource, + private val scInboxSettingsSource: ScInboxSettingsSource, private val appScope: CoroutineScope, ) { init { @@ -56,12 +56,10 @@ class RoomListDataSource @Inject constructor( val loadingState = roomListService.allRooms.loadingState fun launchIn(coroutineScope: CoroutineScope) { - /* + scInboxSettingsSource.launchIn(coroutineScope) roomListService .allRooms .filteredSummaries - */ - scRoomSortOrderSource.filteredSummaries() .onEach { roomSummaries -> replaceWith(roomSummaries) } @@ -78,7 +76,6 @@ class RoomListDataSource @Inject constructor( .debounce(0.5.seconds) .onEach { roomListService.allRooms.rebuildSummaries() - roomListService.sortedRooms.rebuildSummaries() } .launchIn(appScope) } diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersPresenter.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersPresenter.kt index 8a7ecad0b6..9d935212a0 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersPresenter.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/filters/RoomListFiltersPresenter.kt @@ -58,8 +58,7 @@ class RoomListFiltersPresenter @Inject constructor( } .collect { filters -> val result = MatrixRoomListFilter.All(filters) - //roomListService.allRooms.updateFilter(result) - roomListService.sortedRooms.updateFilter(result) + roomListService.allRooms.updateFilter(result) } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e90a36c434..c7bcb463bb 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -162,7 +162,7 @@ jsoup = "org.jsoup:jsoup:1.18.1" appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" } molecule-runtime = "app.cash.molecule:molecule-runtime:2.0.0" timber = "com.jakewharton.timber:timber:5.0.1" -matrix_sdk = "chat.schildi.rustcomponents:sdk-android:0.2.32" +matrix_sdk = "chat.schildi.rustcomponents:sdk-android:0.2.33" matrix_richtexteditor = { module = "chat.schildi:wysiwyg", version.ref = "wysiwyg" } matrix_richtexteditor_compose = { module = "chat.schildi:wysiwyg-compose", version.ref = "wysiwyg" } sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/DynamicRoomList.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/DynamicRoomList.kt index 0638464d7f..b7de67b86f 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/DynamicRoomList.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/DynamicRoomList.kt @@ -41,6 +41,8 @@ interface DynamicRoomList : RoomList { * @param filter the filter to apply. */ suspend fun updateFilter(filter: RoomListFilter) + + suspend fun updateSettings(settings: ScSdkInboxSettings) } /** diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomListService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomListService.kt index eee97ba72b..da17d8468a 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomListService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/RoomListService.kt @@ -45,12 +45,6 @@ interface RoomListService { source: RoomList.Source, ): DynamicRoomList - fun getOrReplaceRoomListWithSortOrder( - pageSize: Int, - initialFilter: RoomListFilter, - sortOrder: ScRoomSortOrder = ScRoomSortOrder(), - ): DynamicRoomList - /** * Subscribes to sync requests for the visible rooms. * @param roomIds the list of visible room ids to subscribe to. @@ -63,12 +57,6 @@ interface RoomListService { */ val allRooms: DynamicRoomList - /** - * SC: don't use allRooms, sorted rooms unfortunately needs recreation when changing sort order which could break some flows, - * so only use it if we're sure we cover that - */ - val sortedRooms: DynamicRoomList - /** * Root spaces. */ diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/ScRoomSortOrder.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/ScSdkRoomSortOrder.kt similarity index 68% rename from libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/ScRoomSortOrder.kt rename to libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/ScSdkRoomSortOrder.kt index 8b9ae6b159..c784898fab 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/ScRoomSortOrder.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomlist/ScSdkRoomSortOrder.kt @@ -1,7 +1,10 @@ package io.element.android.libraries.matrix.api.roomlist +data class ScSdkInboxSettings( + val sortOrder: ScSdkRoomSortOrder = ScSdkRoomSortOrder(), +) -data class ScRoomSortOrder( +data class ScSdkRoomSortOrder( val byUnread: Boolean = false, val pinFavourites: Boolean = false, val buryLowPriority: Boolean = false, diff --git a/libraries/matrix/impl/build.gradle.kts b/libraries/matrix/impl/build.gradle.kts index 85a47e817c..55e0ddcbad 100644 --- a/libraries/matrix/impl/build.gradle.kts +++ b/libraries/matrix/impl/build.gradle.kts @@ -21,6 +21,7 @@ anvil { dependencies { implementation(projects.schildi.matrixsdk) + implementation(projects.schildi.lib) releaseImplementation(libs.matrix.sdk) if (file("${rootDir.path}/libraries/rustsdk/matrix-rust-sdk.aar").exists()) { println("\nNote: Using local binary of the Rust SDK.\n") 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 f2c09d94ea..2ae7ccaf6d 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 @@ -7,6 +7,7 @@ package io.element.android.libraries.matrix.impl +import chat.schildi.lib.preferences.ScPreferencesStore import io.element.android.libraries.androidutils.file.getSizeOfFiles import io.element.android.libraries.androidutils.file.safeDelete import io.element.android.libraries.core.bool.orFalse @@ -120,6 +121,7 @@ class RustMatrixClient( private val sessionStore: SessionStore, private val appCoroutineScope: CoroutineScope, private val sessionDelegate: RustClientSessionDelegate, + private val scPreferencesStore: ScPreferencesStore, syncService: ClientSyncService, dispatchers: CoroutineDispatchers, baseCacheDirectory: File, @@ -168,6 +170,7 @@ class RustMatrixClient( sessionCoroutineScope = sessionCoroutineScope, ), roomSyncSubscriber = roomSyncSubscriber, + scPreferencesStore = scPreferencesStore, ) private val verificationService = RustSessionVerificationService( diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt index e8c5f3dec3..52bbe7a611 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClientFactory.kt @@ -7,6 +7,7 @@ package io.element.android.libraries.matrix.impl +import chat.schildi.lib.preferences.ScPreferencesStore import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.di.CacheDirectory import io.element.android.libraries.featureflag.api.FeatureFlagService @@ -40,6 +41,7 @@ class RustMatrixClientFactory @Inject constructor( private val appCoroutineScope: CoroutineScope, private val coroutineDispatchers: CoroutineDispatchers, private val sessionStore: SessionStore, + private val scPreferencesStore: ScPreferencesStore, private val userAgentProvider: UserAgentProvider, private val userCertificatesProvider: UserCertificatesProvider, private val proxyProvider: ProxyProvider, @@ -80,6 +82,7 @@ class RustMatrixClientFactory @Inject constructor( baseCacheDirectory = cacheDirectory, clock = clock, timelineEventTypeFilterFactory = timelineEventTypeFilterFactory, + scPreferencesStore = scPreferencesStore, ).also { Timber.tag(it.toString()).d("Creating Client with access token '$anonymizedAccessToken' and refresh token '$anonymizedRefreshToken'") } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListDynamicEvents.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListDynamicEvents.kt index 6c5a5db743..14ae2d0640 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListDynamicEvents.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListDynamicEvents.kt @@ -7,10 +7,12 @@ package io.element.android.libraries.matrix.impl.roomlist +import io.element.android.libraries.matrix.api.roomlist.ScSdkInboxSettings import org.matrix.rustcomponents.sdk.RoomListEntriesDynamicFilterKind internal sealed interface RoomListDynamicEvents { data object Reset : RoomListDynamicEvents data object LoadMore : RoomListDynamicEvents data class SetFilter(val filter: RoomListEntriesDynamicFilterKind) : RoomListDynamicEvents + data class SetScInboxSettings(val settings: ScSdkInboxSettings) : RoomListDynamicEvents } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListExtensions.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListExtensions.kt index 4d7ec922cc..589f4b9927 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListExtensions.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListExtensions.kt @@ -8,7 +8,7 @@ package io.element.android.libraries.matrix.impl.roomlist import io.element.android.libraries.core.data.tryOrNull -import io.element.android.libraries.matrix.api.roomlist.ScRoomSortOrder +import io.element.android.libraries.matrix.api.roomlist.ScSdkInboxSettings import io.element.android.libraries.matrix.impl.util.cancelAndDestroy import io.element.android.libraries.matrix.impl.util.mxCallbackFlow import kotlinx.coroutines.channels.Channel @@ -59,7 +59,7 @@ internal fun RoomListInterface.entriesFlow( pageSize: Int, roomListDynamicEvents: Flow, initialFilterKind: RoomListEntriesDynamicFilterKind, - sortOrder: ScRoomSortOrder, + initialInboxSettings: ScSdkInboxSettings? = null, ): Flow> = callbackFlow { val listener = object : RoomListEntriesListener { @@ -69,11 +69,19 @@ internal fun RoomListInterface.entriesFlow( } val result = entriesWithDynamicAdapters(pageSize.toUInt(), listener) val controller = result.controller() - controller.setFilter(initialFilterKind) - controller.setSortOrder(sortOrder.toSdkSortOrder()) + if (initialInboxSettings == null) { + controller.setFilter(initialFilterKind) + } else { + controller.setScInboxSettings(initialFilterKind, initialInboxSettings.toSdkSettings()) + } + var previousScInboxSettings = initialInboxSettings roomListDynamicEvents.onEach { controllerEvents -> when (controllerEvents) { is RoomListDynamicEvents.SetFilter -> { + if (initialInboxSettings != null) { + Timber.e("Setting filter while using SC inbox settings not supported") + return@onEach + } controller.setFilter(controllerEvents.filter) } is RoomListDynamicEvents.LoadMore -> { @@ -82,6 +90,20 @@ internal fun RoomListInterface.entriesFlow( is RoomListDynamicEvents.Reset -> { controller.resetToOnePage() } + is RoomListDynamicEvents.SetScInboxSettings -> { + if (initialInboxSettings == null) { + Timber.w("Setting SC inbox settings after initializing without") + } + if (controllerEvents.settings == previousScInboxSettings) { + Timber.i("Ignore inbox settings update without change") + return@onEach + } + previousScInboxSettings = controllerEvents.settings + controller.setScInboxSettings( + initialFilterKind, + controllerEvents.settings.toSdkSettings() + ) + } } }.launchIn(this) awaitClose { diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFactory.kt index b57944f031..5d805cac36 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFactory.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFactory.kt @@ -11,7 +11,8 @@ import io.element.android.libraries.matrix.api.roomlist.DynamicRoomList import io.element.android.libraries.matrix.api.roomlist.RoomList import io.element.android.libraries.matrix.api.roomlist.RoomListFilter import io.element.android.libraries.matrix.api.roomlist.RoomSummary -import io.element.android.libraries.matrix.api.roomlist.ScRoomSortOrder +import io.element.android.libraries.matrix.api.roomlist.ScSdkInboxSettings +import io.element.android.libraries.matrix.api.roomlist.ScSdkRoomSortOrder import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow @@ -42,7 +43,7 @@ internal class RoomListFactory( coroutineContext: CoroutineContext, coroutineScope: CoroutineScope = sessionCoroutineScope, initialFilter: RoomListFilter = RoomListFilter.all(), - sortOrder: ScRoomSortOrder = ScRoomSortOrder(), + initialInboxSettings: ScSdkInboxSettings? = null, innerProvider: suspend () -> InnerRoomList ): DynamicRoomList { val loadingStateFlow: MutableStateFlow = MutableStateFlow(RoomList.LoadingState.NotLoaded) @@ -62,7 +63,7 @@ internal class RoomListFactory( pageSize = pageSize, roomListDynamicEvents = dynamicEvents, initialFilterKind = RoomListEntriesDynamicFilterKind.NonLeft, - sortOrder = sortOrder, + initialInboxSettings = initialInboxSettings, ).onEach { update -> processor.postUpdate(update) }.launchIn(this) @@ -117,6 +118,10 @@ private class RustDynamicRoomList( currentFilter.emit(filter) } + override suspend fun updateSettings(settings: ScSdkInboxSettings) { + dynamicEvents.emit(RoomListDynamicEvents.SetScInboxSettings(settings)) + } + override suspend fun loadMore() { dynamicEvents.emit(RoomListDynamicEvents.LoadMore) loadedPages.getAndUpdate { it + 1 } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomListService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomListService.kt index d463a41771..aed9c75bbe 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomListService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RustRoomListService.kt @@ -7,30 +7,29 @@ package io.element.android.libraries.matrix.impl.roomlist -import io.element.android.libraries.core.coroutine.childScope +import chat.schildi.lib.preferences.ScPreferencesStore +import chat.schildi.lib.preferences.ScPrefs import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.roomlist.DynamicRoomList import io.element.android.libraries.matrix.api.roomlist.RoomList import io.element.android.libraries.matrix.api.roomlist.RoomListFilter import io.element.android.libraries.matrix.api.roomlist.RoomListService -import io.element.android.libraries.matrix.api.roomlist.ScRoomSortOrder +import io.element.android.libraries.matrix.api.roomlist.ScSdkInboxSettings +import io.element.android.libraries.matrix.api.roomlist.ScSdkRoomSortOrder import io.element.android.libraries.matrix.api.roomlist.loadAllIncrementally import io.element.android.libraries.matrix.impl.room.RoomSyncSubscriber +import io.element.android.libraries.preferences.api.store.AppPreferencesStore import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.isActive import org.matrix.rustcomponents.sdk.RoomListServiceState import org.matrix.rustcomponents.sdk.RoomListServiceSyncIndicator import timber.log.Timber -import java.util.concurrent.atomic.AtomicReference import org.matrix.rustcomponents.sdk.RoomListService as InnerRustRoomListService private const val DEFAULT_PAGE_SIZE = 20 @@ -40,9 +39,9 @@ internal class RustRoomListService( private val sessionDispatcher: CoroutineDispatcher, private val roomListFactory: RoomListFactory, private val roomSyncSubscriber: RoomSyncSubscriber, - private val sessionCoroutineScope: CoroutineScope, + private val scPreferencesStore: ScPreferencesStore, + sessionCoroutineScope: CoroutineScope, ) : RoomListService { - private val lastListWithSortOrder = AtomicReference?>(null) override fun createRoomList( pageSize: Int, @@ -61,36 +60,6 @@ internal class RustRoomListService( } } - override fun getOrReplaceRoomListWithSortOrder( - pageSize: Int, - initialFilter: RoomListFilter, - sortOrder: ScRoomSortOrder - ): DynamicRoomList { - return lastListWithSortOrder.updateAndGet { previous -> - if (previous?.third == sortOrder && previous.second.isActive) { - previous - } else { - val scope = sessionCoroutineScope.childScope(sessionDispatcher, "sc-room-list") - Triple( - roomListFactory.createRoomList( - pageSize = pageSize, - coroutineContext = sessionDispatcher, - coroutineScope = scope, - initialFilter = previous?.first?.currentFilter?.value ?: initialFilter, - sortOrder = sortOrder, - ) { - innerRoomListService.allRooms() - }.also { roomList -> - previous?.second?.cancel("Sorted room list being replaced") - roomList.loadAllIncrementally(scope) - }, - scope, - sortOrder - ) - } - }!!.first - } - override suspend fun subscribeToVisibleRooms(roomIds: List) { roomSyncSubscriber.batchSubscribe(roomIds) } @@ -98,10 +67,18 @@ internal class RustRoomListService( override val allRooms: DynamicRoomList = roomListFactory.createRoomList( pageSize = DEFAULT_PAGE_SIZE, coroutineContext = sessionDispatcher, + initialInboxSettings = ScSdkInboxSettings( + sortOrder = ScSdkRoomSortOrder( + byUnread = scPreferencesStore.getCachedOrDefaultValue(ScPrefs.SORT_BY_UNREAD), + pinFavourites = scPreferencesStore.getCachedOrDefaultValue(ScPrefs.PIN_FAVORITES), + buryLowPriority = scPreferencesStore.getCachedOrDefaultValue(ScPrefs.BURY_LOW_PRIORITY), + clientSideUnreadCounts = scPreferencesStore.getCachedOrDefaultValue(ScPrefs.CLIENT_GENERATED_UNREAD_COUNTS), + withSilentUnread = scPreferencesStore.getCachedOrDefaultValue(ScPrefs.SORT_WITH_SILENT_UNREAD), + ) + ), ) { innerRoomListService.allRooms() } - get() = lastListWithSortOrder.get()?.first ?: field override val allSpaces: DynamicRoomList = roomListFactory.createRoomList( pageSize = DEFAULT_PAGE_SIZE, @@ -110,9 +87,6 @@ internal class RustRoomListService( innerRoomListService.allSpaces() } - override val sortedRooms: DynamicRoomList - get() = lastListWithSortOrder.get()?.first ?: allRooms - init { allRooms.loadAllIncrementally(sessionCoroutineScope) allSpaces.loadAllIncrementally(sessionCoroutineScope) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/ScRoomListFactoryExtensions.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/ScRoomListFactoryExtensions.kt index 977d2bef76..eda38192f3 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/ScRoomListFactoryExtensions.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/ScRoomListFactoryExtensions.kt @@ -1,11 +1,16 @@ package io.element.android.libraries.matrix.impl.roomlist -import io.element.android.libraries.matrix.api.roomlist.ScRoomSortOrder +import io.element.android.libraries.matrix.api.roomlist.ScSdkInboxSettings +import io.element.android.libraries.matrix.api.roomlist.ScSdkRoomSortOrder -fun ScRoomSortOrder.toSdkSortOrder() = uniffi.matrix_sdk_ui.ScSortOrder( - byUnread = byUnread, - pinFavorites = pinFavourites, - buryLowPriority = buryLowPriority, - clientGeneratedUnread = clientSideUnreadCounts, - withSilentUnread = withSilentUnread, - ) +fun ScSdkRoomSortOrder.toSdkSortOrder() = uniffi.matrix_sdk.ScSortOrder( + byUnread = byUnread, + pinFavorites = pinFavourites, + buryLowPriority = buryLowPriority, + clientGeneratedUnread = clientSideUnreadCounts, + withSilentUnread = withSilentUnread, +) + +fun ScSdkInboxSettings.toSdkSettings() = uniffi.matrix_sdk.ScInboxSettings( + sortOrder = sortOrder.toSdkSortOrder(), +) diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/FakeRoomListService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/FakeRoomListService.kt index e69228e7e8..e1b83e5abc 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/FakeRoomListService.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/FakeRoomListService.kt @@ -13,7 +13,6 @@ import io.element.android.libraries.matrix.api.roomlist.RoomList import io.element.android.libraries.matrix.api.roomlist.RoomListFilter import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.libraries.matrix.api.roomlist.RoomSummary -import io.element.android.libraries.matrix.api.roomlist.ScRoomSortOrder import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -54,9 +53,6 @@ class FakeRoomListService( } } - override fun getOrReplaceRoomListWithSortOrder(pageSize: Int, initialFilter: RoomListFilter, sortOrder: ScRoomSortOrder) - = createRoomList(pageSize, initialFilter, RoomList.Source.All) - override suspend fun subscribeToVisibleRooms(roomIds: List) { subscribeToVisibleRoomsLambda(roomIds) } @@ -73,8 +69,6 @@ class FakeRoomListService( MutableStateFlow(RoomListFilter.all()) ) - override val sortedRooms = allRooms - override val state: StateFlow = roomListStateFlow override val syncIndicator: StateFlow = syncIndicatorStateFlow diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/SimplePagedRoomList.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/SimplePagedRoomList.kt index d553deadfa..cea3544bac 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/SimplePagedRoomList.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/roomlist/SimplePagedRoomList.kt @@ -11,6 +11,7 @@ import io.element.android.libraries.matrix.api.roomlist.DynamicRoomList import io.element.android.libraries.matrix.api.roomlist.RoomList import io.element.android.libraries.matrix.api.roomlist.RoomListFilter import io.element.android.libraries.matrix.api.roomlist.RoomSummary +import io.element.android.libraries.matrix.api.roomlist.ScSdkInboxSettings import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow @@ -39,6 +40,8 @@ data class SimplePagedRoomList( currentFilter.emit(filter) } + override suspend fun updateSettings(settings: ScSdkInboxSettings) {} + override suspend fun rebuildSummaries() { // No-op }