From 7fb0b36a9edd4f54aaf3114d64e89cdee35181f0 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 26 Jun 2025 21:44:03 +0200 Subject: [PATCH] change (media preview config) : manage migration of local data --- .../android/appnav/LoggedInFlowNode.kt | 3 + .../loggedin/MediaPreviewConfigMigration.kt | 56 +++++++++++++++++++ .../api/store/AppPreferencesStore.kt | 7 ++- .../impl/store/DefaultAppPreferencesStore.kt | 28 ++++++++-- .../test/InMemoryAppPreferencesStore.kt | 16 ++++-- 5 files changed, 99 insertions(+), 11 deletions(-) create mode 100644 appnav/src/main/kotlin/io/element/android/appnav/loggedin/MediaPreviewConfigMigration.kt diff --git a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt index 9a07c04f57..12b61b2ee6 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/LoggedInFlowNode.kt @@ -41,6 +41,7 @@ import dagger.assisted.AssistedInject import im.vector.app.features.analytics.plan.JoinedRoom import io.element.android.anvilannotations.ContributesNode import io.element.android.appnav.loggedin.LoggedInNode +import io.element.android.appnav.loggedin.MediaPreviewConfigMigration import io.element.android.appnav.loggedin.SendQueues import io.element.android.appnav.room.RoomFlowNode import io.element.android.appnav.room.RoomNavigationTarget @@ -114,6 +115,7 @@ class LoggedInFlowNode @AssistedInject constructor( private val sendingQueue: SendQueues, private val logoutEntryPoint: LogoutEntryPoint, private val incomingVerificationEntryPoint: IncomingVerificationEntryPoint, + private val mediaPreviewConfigMigration: MediaPreviewConfigMigration, snackbarDispatcher: SnackbarDispatcher, ) : BaseFlowNode( backstack = BackStack( @@ -179,6 +181,7 @@ class LoggedInFlowNode @AssistedInject constructor( appNavigationStateService.onNavigateToSpace(id, MAIN_SPACE) loggedInFlowProcessor.observeEvents(sessionCoroutineScope) matrixClient.sessionVerificationService().setListener(verificationListener) + mediaPreviewConfigMigration() ftueService.state .onEach { ftueState -> diff --git a/appnav/src/main/kotlin/io/element/android/appnav/loggedin/MediaPreviewConfigMigration.kt b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/MediaPreviewConfigMigration.kt new file mode 100644 index 0000000000..ac7a1c6145 --- /dev/null +++ b/appnav/src/main/kotlin/io/element/android/appnav/loggedin/MediaPreviewConfigMigration.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2025 New Vector Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +package io.element.android.appnav.loggedin + +import io.element.android.libraries.di.annotations.SessionCoroutineScope +import io.element.android.libraries.matrix.api.media.MediaPreviewService +import io.element.android.libraries.preferences.api.store.AppPreferencesStore +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch +import timber.log.Timber +import javax.inject.Inject + +/** + * This migration is temporary, will be safe to remove after some time. + * The goal is to set the server config if it's not set, and remove the local data. + */ +class MediaPreviewConfigMigration @Inject constructor( + private val mediaPreviewService: MediaPreviewService, + private val appPreferencesStore: AppPreferencesStore, + @SessionCoroutineScope + private val sessionCoroutineScope: CoroutineScope, +) { + operator fun invoke() = sessionCoroutineScope.launch { + val hideInviteAvatars = appPreferencesStore.getHideInviteAvatarsFlow().first() + val mediaPreviewValue = appPreferencesStore.getTimelineMediaPreviewValueFlow().first() + if (hideInviteAvatars == null && mediaPreviewValue == null) { + // No local data, abort. + return@launch + } + mediaPreviewService + .fetchMediaPreviewConfig() + .onSuccess { config -> + if (config != null) { + appPreferencesStore.setHideInviteAvatars(null) + appPreferencesStore.setTimelineMediaPreviewValue(null) + } else { + if (hideInviteAvatars != null) { + mediaPreviewService.setHideInviteAvatars(hideInviteAvatars) + appPreferencesStore.setHideInviteAvatars(null) + } + if (mediaPreviewValue != null) { + mediaPreviewService.setMediaPreviewValue(mediaPreviewValue) + appPreferencesStore.setTimelineMediaPreviewValue(null) + } + } + }.onFailure { + Timber.d("Couldn't perform migration, failed to fetch media preview config.") + } + } +} diff --git a/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/AppPreferencesStore.kt b/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/AppPreferencesStore.kt index 2434823b58..59a074c99b 100644 --- a/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/AppPreferencesStore.kt +++ b/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/AppPreferencesStore.kt @@ -22,9 +22,10 @@ interface AppPreferencesStore { suspend fun setTheme(theme: String) fun getThemeFlow(): Flow - fun getHideInviteAvatarsFlow(): Flow - - fun getTimelineMediaPreviewValueFlow(): Flow + suspend fun setHideInviteAvatars(hide: Boolean?) + fun getHideInviteAvatarsFlow(): Flow + suspend fun setTimelineMediaPreviewValue(mediaPreviewValue: MediaPreviewValue?) + fun getTimelineMediaPreviewValueFlow(): Flow suspend fun setTracingLogLevel(logLevel: LogLevel) fun getTracingLogLevelFlow(): Flow diff --git a/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultAppPreferencesStore.kt b/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultAppPreferencesStore.kt index abdd481f67..1ea7c1f874 100644 --- a/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultAppPreferencesStore.kt +++ b/libraries/preferences/impl/src/main/kotlin/io/element/android/libraries/preferences/impl/store/DefaultAppPreferencesStore.kt @@ -85,15 +85,35 @@ class DefaultAppPreferencesStore @Inject constructor( } } - override fun getHideInviteAvatarsFlow(): Flow { + override fun getHideInviteAvatarsFlow(): Flow { return store.data.map { prefs -> - prefs[hideInviteAvatarsKey] == true + prefs[hideInviteAvatarsKey] } } - override fun getTimelineMediaPreviewValueFlow(): Flow { + override suspend fun setHideInviteAvatars(hide: Boolean?) { + store.edit { prefs -> + if (hide != null) { + prefs[hideInviteAvatarsKey] = hide + } else { + prefs.remove(hideInviteAvatarsKey) + } + } + } + + override suspend fun setTimelineMediaPreviewValue(mediaPreviewValue: MediaPreviewValue?) { + store.edit { prefs -> + if (mediaPreviewValue != null) { + prefs[timelineMediaPreviewValueKey] = mediaPreviewValue.name + } else { + prefs.remove(timelineMediaPreviewValueKey) + } + } + } + + override fun getTimelineMediaPreviewValueFlow(): Flow { return store.data.map { prefs -> - prefs[timelineMediaPreviewValueKey]?.let { MediaPreviewValue.valueOf(it) } ?: MediaPreviewValue.On + prefs[timelineMediaPreviewValueKey]?.let { MediaPreviewValue.valueOf(it) } } } diff --git a/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/InMemoryAppPreferencesStore.kt b/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/InMemoryAppPreferencesStore.kt index 33b69dc8ac..b474ec63ea 100644 --- a/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/InMemoryAppPreferencesStore.kt +++ b/libraries/preferences/test/src/main/kotlin/io/element/android/libraries/preferences/test/InMemoryAppPreferencesStore.kt @@ -17,8 +17,8 @@ import kotlinx.coroutines.flow.MutableStateFlow class InMemoryAppPreferencesStore( isDeveloperModeEnabled: Boolean = false, customElementCallBaseUrl: String? = null, - hideInviteAvatars: Boolean = false, - timelineMediaPreviewValue: MediaPreviewValue = MediaPreviewValue.On, + hideInviteAvatars: Boolean? = null, + timelineMediaPreviewValue: MediaPreviewValue? = null, theme: String? = null, logLevel: LogLevel = LogLevel.INFO, traceLockPacks: Set = emptySet(), @@ -55,14 +55,22 @@ class InMemoryAppPreferencesStore( return theme } - override fun getHideInviteAvatarsFlow(): Flow { + override fun getHideInviteAvatarsFlow(): Flow { return hideInviteAvatars } - override fun getTimelineMediaPreviewValueFlow(): Flow { + override fun getTimelineMediaPreviewValueFlow(): Flow { return timelineMediaPreviewValue } + override suspend fun setHideInviteAvatars(hide: Boolean?) { + hideInviteAvatars.value = hide + } + + override suspend fun setTimelineMediaPreviewValue(mediaPreviewValue: MediaPreviewValue?) { + timelineMediaPreviewValue.value = mediaPreviewValue + } + override suspend fun setTracingLogLevel(logLevel: LogLevel) { this.logLevel.value = logLevel }