From e15a398f3ec577e28c4241490f7f6c9918b9aef7 Mon Sep 17 00:00:00 2001 From: Leonardo Montemurro <221404985+MontesanoDev@users.noreply.github.com> Date: Mon, 27 Apr 2026 15:01:08 +0200 Subject: [PATCH 1/3] fix: make app language key global, not profile-scoped The language preference was stored with a profile-scoped key (selected_app_language_{profileId}), causing it to reset to English on app restart when the active profile was not profile 1. During startup, initialize() reads the key with default activeProfileId=1 before the correct profile is loaded, resulting in a cache miss and fallback to English. App language is a device-level preference, not per-profile, so the key should not be scoped. This also fixes replaceFromSyncPayload to correctly clear both profile-scoped and global keys. --- .../settings/ThemeSettingsStorage.android.kt | 10 ++++++---- .../features/settings/ThemeSettingsStorage.ios.kt | 12 ++++++++---- 2 files changed, 14 insertions(+), 8 deletions(-) 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 32e659c1..48c3f1a7 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 @@ -18,7 +18,8 @@ actual object ThemeSettingsStorage { private const val selectedThemeKey = "selected_theme" private const val amoledEnabledKey = "amoled_enabled" private const val selectedAppLanguageKey = "selected_app_language" - private val syncKeys = listOf(selectedThemeKey, amoledEnabledKey, selectedAppLanguageKey) + private val profileScopedSyncKeys = listOf(selectedThemeKey, amoledEnabledKey) + private val globalSyncKeys = listOf(selectedAppLanguageKey) private var preferences: SharedPreferences? = null @@ -51,12 +52,12 @@ actual object ThemeSettingsStorage { } actual fun loadSelectedAppLanguage(): String? = - preferences?.getString(ProfileScopedKey.of(selectedAppLanguageKey), null) + preferences?.getString(selectedAppLanguageKey, null) actual fun saveSelectedAppLanguage(languageCode: String) { preferences ?.edit() - ?.putString(ProfileScopedKey.of(selectedAppLanguageKey), languageCode) + ?.putString(selectedAppLanguageKey, languageCode) ?.apply() } @@ -74,7 +75,8 @@ actual object ThemeSettingsStorage { actual fun replaceFromSyncPayload(payload: JsonObject) { preferences?.edit()?.apply { - syncKeys.forEach { remove(ProfileScopedKey.of(it)) } + profileScopedSyncKeys.forEach { remove(ProfileScopedKey.of(it)) } + globalSyncKeys.forEach { remove(it) } }?.apply() payload.decodeSyncString(selectedThemeKey)?.let(::saveSelectedTheme) 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 cf91655b..7f28fa7b 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 @@ -14,7 +14,8 @@ actual object ThemeSettingsStorage { private const val selectedThemeKey = "selected_theme" private const val amoledEnabledKey = "amoled_enabled" private const val selectedAppLanguageKey = "selected_app_language" - private val syncKeys = listOf(selectedThemeKey, amoledEnabledKey, selectedAppLanguageKey) + private val profileScopedSyncKeys = listOf(selectedThemeKey, amoledEnabledKey) + private val globalSyncKeys = listOf(selectedAppLanguageKey) actual fun loadSelectedTheme(): String? = NSUserDefaults.standardUserDefaults.stringForKey(ProfileScopedKey.of(selectedThemeKey)) @@ -38,10 +39,10 @@ actual object ThemeSettingsStorage { } actual fun loadSelectedAppLanguage(): String? = - NSUserDefaults.standardUserDefaults.stringForKey(ProfileScopedKey.of(selectedAppLanguageKey)) + NSUserDefaults.standardUserDefaults.stringForKey(selectedAppLanguageKey) actual fun saveSelectedAppLanguage(languageCode: String) { - NSUserDefaults.standardUserDefaults.setObject(languageCode, forKey = ProfileScopedKey.of(selectedAppLanguageKey)) + NSUserDefaults.standardUserDefaults.setObject(languageCode, forKey = selectedAppLanguageKey) } actual fun applySelectedAppLanguage(languageCode: String) = Unit @@ -53,9 +54,12 @@ actual object ThemeSettingsStorage { } actual fun replaceFromSyncPayload(payload: JsonObject) { - syncKeys.forEach { key -> + profileScopedSyncKeys.forEach { key -> NSUserDefaults.standardUserDefaults.removeObjectForKey(ProfileScopedKey.of(key)) } + globalSyncKeys.forEach { key -> + NSUserDefaults.standardUserDefaults.removeObjectForKey(key) + } payload.decodeSyncString(selectedThemeKey)?.let(::saveSelectedTheme) payload.decodeSyncBoolean(amoledEnabledKey)?.let(::saveAmoledEnabled) From ee61f2d83cc3343357f07463aa847cd76f36da18 Mon Sep 17 00:00:00 2001 From: Leonardo Montemurro <221404985+MontesanoDev@users.noreply.github.com> Date: Mon, 27 Apr 2026 15:01:23 +0200 Subject: [PATCH 2/3] fix: notify ThemeSettingsRepository when cached profiles are loaded loadCachedProfiles() sets activeProfileIndex but did not notify ThemeSettingsRepository to re-read profile-scoped settings (theme, amoled) from the correct profile key. This ensures theme and other profile-scoped preferences are loaded from the correct profile after the cached profile data is applied. --- .../kotlin/com/nuvio/app/features/profiles/ProfileRepository.kt | 1 + 1 file changed, 1 insertion(+) 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 b6637490..07a9d9c6 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 @@ -70,6 +70,7 @@ object ProfileRepository { val stored = decodeStoredPayload() ?: return false loadedCacheForUserId = stored.userId applyStoredPayload(stored) + ThemeSettingsRepository.onProfileChanged() return _state.value.profiles.isNotEmpty() } From 17cb75a4642789fa5e547cbe36c28d16e48e3047 Mon Sep 17 00:00:00 2001 From: Leonardo Montemurro <221404985+MontesanoDev@users.noreply.github.com> Date: Mon, 27 Apr 2026 15:08:31 +0200 Subject: [PATCH 3/3] fix: migrate language from legacy profile-scoped key to global key On first launch after upgrading, if the new global key is empty, fall back to the legacy profile-scoped key from the previous version. Once found, the value is saved to the global key so future reads avoid the legacy path. --- .../features/settings/ThemeSettingsStorage.android.kt | 9 +++++++-- .../app/features/settings/ThemeSettingsStorage.ios.kt | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) 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 48c3f1a7..8b1506f0 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 @@ -51,8 +51,13 @@ actual object ThemeSettingsStorage { ?.apply() } - actual fun loadSelectedAppLanguage(): String? = - preferences?.getString(selectedAppLanguageKey, null) + actual fun loadSelectedAppLanguage(): String? { + val value = preferences?.getString(selectedAppLanguageKey, null) + if (value != null) return value + val legacy = preferences?.getString(ProfileScopedKey.of(selectedAppLanguageKey), null) + if (legacy != null) saveSelectedAppLanguage(legacy) + return legacy + } actual fun saveSelectedAppLanguage(languageCode: String) { preferences 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 7f28fa7b..f71eaaea 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 @@ -38,8 +38,13 @@ actual object ThemeSettingsStorage { NSUserDefaults.standardUserDefaults.setBool(enabled, forKey = ProfileScopedKey.of(amoledEnabledKey)) } - actual fun loadSelectedAppLanguage(): String? = - NSUserDefaults.standardUserDefaults.stringForKey(selectedAppLanguageKey) + actual fun loadSelectedAppLanguage(): String? { + val value = NSUserDefaults.standardUserDefaults.stringForKey(selectedAppLanguageKey) + if (value != null) return value + val legacy = NSUserDefaults.standardUserDefaults.stringForKey(ProfileScopedKey.of(selectedAppLanguageKey)) + if (legacy != null) saveSelectedAppLanguage(legacy) + return legacy + } actual fun saveSelectedAppLanguage(languageCode: String) { NSUserDefaults.standardUserDefaults.setObject(languageCode, forKey = selectedAppLanguageKey)