From dce27b3620b07bf91ec9b36a006d2136c3663db6 Mon Sep 17 00:00:00 2001 From: tapframe <85391825+tapframe@users.noreply.github.com> Date: Sun, 29 Mar 2026 13:50:52 +0530 Subject: [PATCH] feat: Implement profile-scoped storage keys; update settings storage and repositories to support profile-specific data --- .../HomeCatalogSettingsStorage.android.kt | 5 ++-- .../player/PlayerSettingsStorage.android.kt | 8 +++--- .../settings/ThemeSettingsStorage.android.kt | 10 ++++--- ...tinueWatchingPreferencesStorage.android.kt | 5 ++-- .../app/core/storage/ProfileScopedKey.kt | 8 ++++++ .../home/HomeCatalogSettingsRepository.kt | 7 +++++ .../player/PlayerSettingsRepository.kt | 8 ++++++ .../features/profiles/ProfileRepository.kt | 8 ++++++ .../settings/ThemeSettingsRepository.kt | 8 ++++++ .../ContinueWatchingPreferencesRepository.kt | 27 ++++++++++++++----- .../home/HomeCatalogSettingsStorage.ios.kt | 5 ++-- .../player/PlayerSettingsStorage.ios.kt | 8 +++--- .../settings/ThemeSettingsStorage.ios.kt | 12 +++++---- .../ContinueWatchingPreferencesStorage.ios.kt | 5 ++-- 14 files changed, 95 insertions(+), 29 deletions(-) create mode 100644 composeApp/src/commonMain/kotlin/com/nuvio/app/core/storage/ProfileScopedKey.kt diff --git a/composeApp/src/androidMain/kotlin/com/nuvio/app/features/home/HomeCatalogSettingsStorage.android.kt b/composeApp/src/androidMain/kotlin/com/nuvio/app/features/home/HomeCatalogSettingsStorage.android.kt index 0d69b0abf..36bcae1c9 100644 --- a/composeApp/src/androidMain/kotlin/com/nuvio/app/features/home/HomeCatalogSettingsStorage.android.kt +++ b/composeApp/src/androidMain/kotlin/com/nuvio/app/features/home/HomeCatalogSettingsStorage.android.kt @@ -2,6 +2,7 @@ package com.nuvio.app.features.home import android.content.Context import android.content.SharedPreferences +import com.nuvio.app.core.storage.ProfileScopedKey actual object HomeCatalogSettingsStorage { private const val preferencesName = "nuvio_home_catalog_settings" @@ -14,12 +15,12 @@ actual object HomeCatalogSettingsStorage { } actual fun loadPayload(): String? = - preferences?.getString(payloadKey, null) + preferences?.getString(ProfileScopedKey.of(payloadKey), null) actual fun savePayload(payload: String) { preferences ?.edit() - ?.putString(payloadKey, payload) + ?.putString(ProfileScopedKey.of(payloadKey), payload) ?.apply() } } diff --git a/composeApp/src/androidMain/kotlin/com/nuvio/app/features/player/PlayerSettingsStorage.android.kt b/composeApp/src/androidMain/kotlin/com/nuvio/app/features/player/PlayerSettingsStorage.android.kt index 5710aa0bf..8fad45f06 100644 --- a/composeApp/src/androidMain/kotlin/com/nuvio/app/features/player/PlayerSettingsStorage.android.kt +++ b/composeApp/src/androidMain/kotlin/com/nuvio/app/features/player/PlayerSettingsStorage.android.kt @@ -2,6 +2,7 @@ package com.nuvio.app.features.player import android.content.Context import android.content.SharedPreferences +import com.nuvio.app.core.storage.ProfileScopedKey actual object PlayerSettingsStorage { private const val preferencesName = "nuvio_player_settings" @@ -15,8 +16,9 @@ actual object PlayerSettingsStorage { actual fun loadShowLoadingOverlay(): Boolean? = preferences?.let { sharedPreferences -> - if (sharedPreferences.contains(showLoadingOverlayKey)) { - sharedPreferences.getBoolean(showLoadingOverlayKey, true) + val key = ProfileScopedKey.of(showLoadingOverlayKey) + if (sharedPreferences.contains(key)) { + sharedPreferences.getBoolean(key, true) } else { null } @@ -25,7 +27,7 @@ actual object PlayerSettingsStorage { actual fun saveShowLoadingOverlay(enabled: Boolean) { preferences ?.edit() - ?.putBoolean(showLoadingOverlayKey, enabled) + ?.putBoolean(ProfileScopedKey.of(showLoadingOverlayKey), enabled) ?.apply() } } diff --git a/composeApp/src/androidMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsStorage.android.kt b/composeApp/src/androidMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsStorage.android.kt index c24329e5f..6ad2a035d 100644 --- a/composeApp/src/androidMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsStorage.android.kt +++ b/composeApp/src/androidMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsStorage.android.kt @@ -2,6 +2,7 @@ package com.nuvio.app.features.settings import android.content.Context import android.content.SharedPreferences +import com.nuvio.app.core.storage.ProfileScopedKey actual object ThemeSettingsStorage { private const val preferencesName = "nuvio_theme_settings" @@ -15,24 +16,25 @@ actual object ThemeSettingsStorage { } actual fun loadSelectedTheme(): String? = - preferences?.getString(selectedThemeKey, null) + preferences?.getString(ProfileScopedKey.of(selectedThemeKey), null) actual fun saveSelectedTheme(themeName: String) { preferences ?.edit() - ?.putString(selectedThemeKey, themeName) + ?.putString(ProfileScopedKey.of(selectedThemeKey), themeName) ?.apply() } actual fun loadAmoledEnabled(): Boolean? = preferences?.let { prefs -> - if (prefs.contains(amoledEnabledKey)) prefs.getBoolean(amoledEnabledKey, false) else null + val key = ProfileScopedKey.of(amoledEnabledKey) + if (prefs.contains(key)) prefs.getBoolean(key, false) else null } actual fun saveAmoledEnabled(enabled: Boolean) { preferences ?.edit() - ?.putBoolean(amoledEnabledKey, enabled) + ?.putBoolean(ProfileScopedKey.of(amoledEnabledKey), enabled) ?.apply() } } diff --git a/composeApp/src/androidMain/kotlin/com/nuvio/app/features/watchprogress/ContinueWatchingPreferencesStorage.android.kt b/composeApp/src/androidMain/kotlin/com/nuvio/app/features/watchprogress/ContinueWatchingPreferencesStorage.android.kt index b169b3dc5..68ad60c56 100644 --- a/composeApp/src/androidMain/kotlin/com/nuvio/app/features/watchprogress/ContinueWatchingPreferencesStorage.android.kt +++ b/composeApp/src/androidMain/kotlin/com/nuvio/app/features/watchprogress/ContinueWatchingPreferencesStorage.android.kt @@ -2,6 +2,7 @@ package com.nuvio.app.features.watchprogress import android.content.Context import android.content.SharedPreferences +import com.nuvio.app.core.storage.ProfileScopedKey actual object ContinueWatchingPreferencesStorage { private const val preferencesName = "nuvio_continue_watching_preferences" @@ -14,12 +15,12 @@ actual object ContinueWatchingPreferencesStorage { } actual fun loadPayload(): String? = - preferences?.getString(payloadKey, null) + preferences?.getString(ProfileScopedKey.of(payloadKey), null) actual fun savePayload(payload: String) { preferences ?.edit() - ?.putString(payloadKey, payload) + ?.putString(ProfileScopedKey.of(payloadKey), payload) ?.apply() } } diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/core/storage/ProfileScopedKey.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/core/storage/ProfileScopedKey.kt new file mode 100644 index 000000000..cdc75ad36 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/core/storage/ProfileScopedKey.kt @@ -0,0 +1,8 @@ +package com.nuvio.app.core.storage + +import com.nuvio.app.features.profiles.ProfileRepository + + +object ProfileScopedKey { + fun of(baseKey: String): String = "${baseKey}_${ProfileRepository.activeProfileId}" +} diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/home/HomeCatalogSettingsRepository.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/home/HomeCatalogSettingsRepository.kt index 95eeb63ad..be2ec980a 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/home/HomeCatalogSettingsRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/home/HomeCatalogSettingsRepository.kt @@ -79,6 +79,13 @@ object HomeCatalogSettingsRepository { private var preferences: MutableMap = mutableMapOf() private var heroEnabled = true + fun onProfileChanged() { + hasLoaded = false + preferences.clear() + heroEnabled = true + _uiState.value = HomeCatalogSettingsUiState() + } + fun syncCatalogs(addons: List) { ensureLoaded() definitions = buildHomeCatalogDefinitions(addons) diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerSettingsRepository.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerSettingsRepository.kt index 04ec45a9b..d103f8729 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerSettingsRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/player/PlayerSettingsRepository.kt @@ -17,6 +17,14 @@ object PlayerSettingsRepository { fun ensureLoaded() { if (hasLoaded) return + loadFromDisk() + } + + fun onProfileChanged() { + loadFromDisk() + } + + private fun loadFromDisk() { hasLoaded = true showLoadingOverlay = PlayerSettingsStorage.loadShowLoadingOverlay() ?: true publish() diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/profiles/ProfileRepository.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/profiles/ProfileRepository.kt index 49d447325..45e323c56 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/profiles/ProfileRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/profiles/ProfileRepository.kt @@ -5,8 +5,12 @@ import com.nuvio.app.core.auth.AuthRepository import com.nuvio.app.core.auth.AuthState import com.nuvio.app.core.network.SupabaseProvider import com.nuvio.app.features.addons.AddonRepository +import com.nuvio.app.features.home.HomeCatalogSettingsRepository import com.nuvio.app.features.library.LibraryRepository +import com.nuvio.app.features.player.PlayerSettingsRepository +import com.nuvio.app.features.settings.ThemeSettingsRepository import com.nuvio.app.features.watched.WatchedRepository +import com.nuvio.app.features.watchprogress.ContinueWatchingPreferencesRepository import com.nuvio.app.features.watchprogress.WatchProgressRepository import io.github.jan.supabase.postgrest.postgrest import io.github.jan.supabase.postgrest.rpc @@ -114,6 +118,10 @@ object ProfileRepository { LibraryRepository.onProfileChanged(profileIndex) WatchProgressRepository.onProfileChanged(profileIndex) AddonRepository.onProfileChanged(profileIndex) + ThemeSettingsRepository.onProfileChanged() + PlayerSettingsRepository.onProfileChanged() + HomeCatalogSettingsRepository.onProfileChanged() + ContinueWatchingPreferencesRepository.onProfileChanged() } suspend fun pushProfiles(profiles: List) { diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsRepository.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsRepository.kt index 2af59e9df..42c650b0f 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsRepository.kt @@ -16,6 +16,14 @@ object ThemeSettingsRepository { fun ensureLoaded() { if (hasLoaded) return + loadFromDisk() + } + + fun onProfileChanged() { + loadFromDisk() + } + + private fun loadFromDisk() { hasLoaded = true val stored = ThemeSettingsStorage.loadSelectedTheme() val theme = if (stored != null) { diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/watchprogress/ContinueWatchingPreferencesRepository.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/watchprogress/ContinueWatchingPreferencesRepository.kt index e9ef71161..4ca55fc59 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/watchprogress/ContinueWatchingPreferencesRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/watchprogress/ContinueWatchingPreferencesRepository.kt @@ -27,19 +27,34 @@ object ContinueWatchingPreferencesRepository { fun ensureLoaded() { if (hasLoaded) return + loadFromDisk() + } + + fun onProfileChanged() { + loadFromDisk() + } + + private fun loadFromDisk() { hasLoaded = true val payload = ContinueWatchingPreferencesStorage.loadPayload().orEmpty().trim() - if (payload.isEmpty()) return + if (payload.isEmpty()) { + _uiState.value = ContinueWatchingPreferencesUiState() + return + } val stored = runCatching { json.decodeFromString(payload) - }.getOrNull() ?: return + }.getOrNull() - _uiState.value = ContinueWatchingPreferencesUiState( - isVisible = stored.isVisible, - style = stored.style, - ) + _uiState.value = if (stored != null) { + ContinueWatchingPreferencesUiState( + isVisible = stored.isVisible, + style = stored.style, + ) + } else { + ContinueWatchingPreferencesUiState() + } } fun setVisible(isVisible: Boolean) { diff --git a/composeApp/src/iosMain/kotlin/com/nuvio/app/features/home/HomeCatalogSettingsStorage.ios.kt b/composeApp/src/iosMain/kotlin/com/nuvio/app/features/home/HomeCatalogSettingsStorage.ios.kt index 41a4b8ad6..a8e4de30b 100644 --- a/composeApp/src/iosMain/kotlin/com/nuvio/app/features/home/HomeCatalogSettingsStorage.ios.kt +++ b/composeApp/src/iosMain/kotlin/com/nuvio/app/features/home/HomeCatalogSettingsStorage.ios.kt @@ -1,14 +1,15 @@ package com.nuvio.app.features.home +import com.nuvio.app.core.storage.ProfileScopedKey import platform.Foundation.NSUserDefaults actual object HomeCatalogSettingsStorage { private const val payloadKey = "catalog_settings_payload" actual fun loadPayload(): String? = - NSUserDefaults.standardUserDefaults.stringForKey(payloadKey) + NSUserDefaults.standardUserDefaults.stringForKey(ProfileScopedKey.of(payloadKey)) actual fun savePayload(payload: String) { - NSUserDefaults.standardUserDefaults.setObject(payload, forKey = payloadKey) + NSUserDefaults.standardUserDefaults.setObject(payload, forKey = ProfileScopedKey.of(payloadKey)) } } diff --git a/composeApp/src/iosMain/kotlin/com/nuvio/app/features/player/PlayerSettingsStorage.ios.kt b/composeApp/src/iosMain/kotlin/com/nuvio/app/features/player/PlayerSettingsStorage.ios.kt index 1f5ba76bb..aa4272820 100644 --- a/composeApp/src/iosMain/kotlin/com/nuvio/app/features/player/PlayerSettingsStorage.ios.kt +++ b/composeApp/src/iosMain/kotlin/com/nuvio/app/features/player/PlayerSettingsStorage.ios.kt @@ -1,5 +1,6 @@ package com.nuvio.app.features.player +import com.nuvio.app.core.storage.ProfileScopedKey import platform.Foundation.NSUserDefaults actual object PlayerSettingsStorage { @@ -7,14 +8,15 @@ actual object PlayerSettingsStorage { actual fun loadShowLoadingOverlay(): Boolean? { val defaults = NSUserDefaults.standardUserDefaults - return if (defaults.objectForKey(showLoadingOverlayKey) != null) { - defaults.boolForKey(showLoadingOverlayKey) + val key = ProfileScopedKey.of(showLoadingOverlayKey) + return if (defaults.objectForKey(key) != null) { + defaults.boolForKey(key) } else { null } } actual fun saveShowLoadingOverlay(enabled: Boolean) { - NSUserDefaults.standardUserDefaults.setBool(enabled, forKey = showLoadingOverlayKey) + NSUserDefaults.standardUserDefaults.setBool(enabled, forKey = ProfileScopedKey.of(showLoadingOverlayKey)) } } diff --git a/composeApp/src/iosMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsStorage.ios.kt b/composeApp/src/iosMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsStorage.ios.kt index 82f3e5bd8..7c72ebce7 100644 --- a/composeApp/src/iosMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsStorage.ios.kt +++ b/composeApp/src/iosMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsStorage.ios.kt @@ -1,5 +1,6 @@ package com.nuvio.app.features.settings +import com.nuvio.app.core.storage.ProfileScopedKey import platform.Foundation.NSUserDefaults actual object ThemeSettingsStorage { @@ -7,22 +8,23 @@ actual object ThemeSettingsStorage { private const val amoledEnabledKey = "amoled_enabled" actual fun loadSelectedTheme(): String? = - NSUserDefaults.standardUserDefaults.stringForKey(selectedThemeKey) + NSUserDefaults.standardUserDefaults.stringForKey(ProfileScopedKey.of(selectedThemeKey)) actual fun saveSelectedTheme(themeName: String) { - NSUserDefaults.standardUserDefaults.setObject(themeName, forKey = selectedThemeKey) + NSUserDefaults.standardUserDefaults.setObject(themeName, forKey = ProfileScopedKey.of(selectedThemeKey)) } actual fun loadAmoledEnabled(): Boolean? { val defaults = NSUserDefaults.standardUserDefaults - return if (defaults.objectForKey(amoledEnabledKey) != null) { - defaults.boolForKey(amoledEnabledKey) + val key = ProfileScopedKey.of(amoledEnabledKey) + return if (defaults.objectForKey(key) != null) { + defaults.boolForKey(key) } else { null } } actual fun saveAmoledEnabled(enabled: Boolean) { - NSUserDefaults.standardUserDefaults.setBool(enabled, forKey = amoledEnabledKey) + NSUserDefaults.standardUserDefaults.setBool(enabled, forKey = ProfileScopedKey.of(amoledEnabledKey)) } } diff --git a/composeApp/src/iosMain/kotlin/com/nuvio/app/features/watchprogress/ContinueWatchingPreferencesStorage.ios.kt b/composeApp/src/iosMain/kotlin/com/nuvio/app/features/watchprogress/ContinueWatchingPreferencesStorage.ios.kt index c9b99e5ab..9fabaceb5 100644 --- a/composeApp/src/iosMain/kotlin/com/nuvio/app/features/watchprogress/ContinueWatchingPreferencesStorage.ios.kt +++ b/composeApp/src/iosMain/kotlin/com/nuvio/app/features/watchprogress/ContinueWatchingPreferencesStorage.ios.kt @@ -1,14 +1,15 @@ package com.nuvio.app.features.watchprogress +import com.nuvio.app.core.storage.ProfileScopedKey import platform.Foundation.NSUserDefaults actual object ContinueWatchingPreferencesStorage { private const val payloadKey = "continue_watching_preferences_payload" actual fun loadPayload(): String? = - NSUserDefaults.standardUserDefaults.stringForKey(payloadKey) + NSUserDefaults.standardUserDefaults.stringForKey(ProfileScopedKey.of(payloadKey)) actual fun savePayload(payload: String) { - NSUserDefaults.standardUserDefaults.setObject(payload, forKey = payloadKey) + NSUserDefaults.standardUserDefaults.setObject(payload, forKey = ProfileScopedKey.of(payloadKey)) } }