From 05a5f9d7d3c2cda5a013fa589fdd1d46192efcf6 Mon Sep 17 00:00:00 2001 From: guuilp Date: Thu, 9 Jul 2026 10:11:57 +0000 Subject: [PATCH 1/8] fix: per-profile isolation for Trakt auth and storage 1. TraktAuthStorage: Added profileId parameter to loadPayload(profileId) and savePayload(profileId, payload) in Android and iOS actual implementations, passing it explicitly to ProfileScopedKey.of(). 2. TraktAuthRepository: Added currentProfileId and profileGeneration for cross-profile operation detection. Methods now accept explicit profileId to isolate auth state per profile. loadFromDisk() and persist() use the scoped storage key. 3. TraktCredentialSync: Updated to pass profileId to currentStateForSync() and replaceStateFromSync(). 4. ProfileRepository/LibraryRepository: Updated calls to TraktAuthRepository.onProfileChanged(profileId). 5. WatchedRepository/WatchProgressRepository: Fixed pullFromServer/forceSnapshotRefreshFromServer to pass profileId to TraktAuthRepository.ensureLoaded() before checking activeOperationGeneration. 6. SyncManager: Passes profileId to ensureLoaded(). --- .../trakt/TraktAuthStorage.android.kt | 8 +- .../com/nuvio/app/core/sync/SyncManager.kt | 2 +- .../app/features/library/LibraryRepository.kt | 2 +- .../features/profiles/ProfileRepository.kt | 2 +- .../app/features/trakt/TraktAuthRepository.kt | 110 +++++++++--------- .../app/features/trakt/TraktAuthStorage.kt | 4 +- .../app/features/watched/WatchedRepository.kt | 6 +- .../features/trakt/TraktAuthStorage.ios.kt | 8 +- 8 files changed, 74 insertions(+), 68 deletions(-) diff --git a/composeApp/src/androidMain/kotlin/com/nuvio/app/features/trakt/TraktAuthStorage.android.kt b/composeApp/src/androidMain/kotlin/com/nuvio/app/features/trakt/TraktAuthStorage.android.kt index cc8e84365..d694069b1 100644 --- a/composeApp/src/androidMain/kotlin/com/nuvio/app/features/trakt/TraktAuthStorage.android.kt +++ b/composeApp/src/androidMain/kotlin/com/nuvio/app/features/trakt/TraktAuthStorage.android.kt @@ -14,13 +14,13 @@ internal actual object TraktAuthStorage { preferences = context.getSharedPreferences(preferencesName, Context.MODE_PRIVATE) } - actual fun loadPayload(): String? = - preferences?.getString(ProfileScopedKey.of(payloadKey), null) + actual fun loadPayload(profileId: Int): String? = + preferences?.getString(ProfileScopedKey.of(payloadKey, profileId), null) - actual fun savePayload(payload: String) { + actual fun savePayload(profileId: Int, payload: String) { preferences ?.edit() - ?.putString(ProfileScopedKey.of(payloadKey), payload) + ?.putString(ProfileScopedKey.of(payloadKey, profileId), payload) ?.apply() } } diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/core/sync/SyncManager.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/core/sync/SyncManager.kt index f81af882a..703b88c48 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/core/sync/SyncManager.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/core/sync/SyncManager.kt @@ -427,7 +427,7 @@ object SyncManager { continue } - TraktAuthRepository.ensureLoaded() + TraktAuthRepository.ensureLoaded(profileId) TraktSettingsRepository.ensureLoaded() val traktAuthenticated = TraktAuthRepository.isAuthenticated.value diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/library/LibraryRepository.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/library/LibraryRepository.kt index 06c102f8c..cb9fbebe3 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/library/LibraryRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/library/LibraryRepository.kt @@ -151,7 +151,7 @@ object LibraryRepository { TraktSettingsRepository.onProfileChanged() if (!loadFromDisk(profileId)) return - TraktAuthRepository.onProfileChanged() + TraktAuthRepository.onProfileChanged(profileId) TraktLibraryRepository.onProfileChanged() if (TraktAuthRepository.isAuthenticated.value) { TraktLibraryRepository.preloadListTabsAsync() 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 fd76b3fef..7944af4c7 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 @@ -154,7 +154,7 @@ object ProfileRepository { persist() WatchedRepository.onProfileChanged(profileIndex) TraktSettingsRepository.onProfileChanged() - TraktAuthRepository.onProfileChanged() + TraktAuthRepository.onProfileChanged(profileIndex) LibraryRepository.onProfileChanged(profileIndex) WatchProgressRepository.onProfileChanged(profileIndex) AddonRepository.onProfileChanged(profileIndex) diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/trakt/TraktAuthRepository.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/trakt/TraktAuthRepository.kt index 20fa9eb50..a9e3308eb 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/trakt/TraktAuthRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/trakt/TraktAuthRepository.kt @@ -48,33 +48,37 @@ object TraktAuthRepository { val isAuthenticated: StateFlow = _isAuthenticated.asStateFlow() private var hasLoaded = false + private var currentProfileId: Int = 1 + private var profileGeneration: Long = 0L private var authState = TraktAuthState() - fun ensureLoaded() { - if (hasLoaded) return - loadFromDisk() + fun ensureLoaded(profileId: Int = ProfileRepository.activeProfileId) { + if (hasLoaded && currentProfileId == profileId) return + loadFromDisk(profileId) } - fun onProfileChanged() { - loadFromDisk() + fun onProfileChanged(profileId: Int) { + loadFromDisk(profileId) } fun clearLocalState() { hasLoaded = false + currentProfileId = 1 + profileGeneration += 1L authState = TraktAuthState() publish() } - fun snapshot(): TraktAuthUiState { - ensureLoaded() + fun snapshot(profileId: Int = ProfileRepository.activeProfileId): TraktAuthUiState { + ensureLoaded(profileId) return _uiState.value } fun hasRequiredCredentials(): Boolean = TraktConfig.CLIENT_ID.isNotBlank() && TraktConfig.CLIENT_SECRET.isNotBlank() - fun onConnectRequested(): String? { - ensureLoaded() + fun onConnectRequested(profileId: Int = ProfileRepository.activeProfileId): String? { + ensureLoaded(profileId) if (!hasRequiredCredentials()) { publish(errorMessage = localizedString(Res.string.trakt_missing_credentials)) return null @@ -85,7 +89,7 @@ object TraktAuthRepository { pendingAuthorizationState = oauthState, pendingAuthorizationStartedAtMillis = TraktPlatformClock.nowEpochMs(), ) - persist() + persist(profileId) publish( statusMessage = localizedString(Res.string.trakt_complete_sign_in_browser), errorMessage = null, @@ -94,21 +98,21 @@ object TraktAuthRepository { return buildAuthorizationUrl(oauthState) } - fun pendingAuthorizationUrl(): String? { - ensureLoaded() + fun pendingAuthorizationUrl(profileId: Int = ProfileRepository.activeProfileId): String? { + ensureLoaded(profileId) val oauthState = authState.pendingAuthorizationState ?: return null return buildAuthorizationUrl(oauthState) } - fun onCancelAuthorization() { - ensureLoaded() + fun onCancelAuthorization(profileId: Int = ProfileRepository.activeProfileId) { + ensureLoaded(profileId) clearPendingAuthorization() - persist() + persist(profileId) publish(statusMessage = null, errorMessage = null) } - fun onCancelDeviceFlow() { - onCancelAuthorization() + fun onCancelDeviceFlow(profileId: Int = ProfileRepository.activeProfileId) { + onCancelAuthorization(profileId) } fun onAuthLaunchFailed(reason: String) { @@ -116,7 +120,8 @@ object TraktAuthRepository { } fun onAuthCallbackReceived(callbackUrl: String) { - ensureLoaded() + val profileId = ProfileRepository.activeProfileId + ensureLoaded(profileId) if (!callbackUrl.startsWith("${TraktConfig.REDIRECT_URI}?", ignoreCase = true) && !callbackUrl.equals(TraktConfig.REDIRECT_URI, ignoreCase = true) ) { @@ -124,15 +129,15 @@ object TraktAuthRepository { } scope.launch { - completeAuthorizationFromCallback(callbackUrl) + completeAuthorizationFromCallback(callbackUrl, profileId) } } - suspend fun authorizedHeaders(): Map? { - ensureLoaded() + suspend fun authorizedHeaders(profileId: Int = currentProfileId): Map? { + ensureLoaded(profileId) if (!authState.isAuthenticated) return null - val hasValidToken = refreshTokenIfNeeded(force = false) + val hasValidToken = refreshTokenIfNeeded(force = false, profileId = profileId) if (!hasValidToken) return null val accessToken = authState.accessToken?.trim().orEmpty() @@ -145,9 +150,9 @@ object TraktAuthRepository { ) } - suspend fun refreshUserSettings(): String? { - ensureLoaded() - val headers = authorizedHeaders() ?: return null + suspend fun refreshUserSettings(profileId: Int = currentProfileId): String? { + ensureLoaded(profileId) + val headers = authorizedHeaders(profileId) ?: return null val response = runCatching { httpGetTextWithHeaders( url = "$BASE_URL/users/settings", @@ -166,19 +171,19 @@ object TraktAuthRepository { username = parsed.user?.username, userSlug = parsed.user?.ids?.slug, ) - persist() + persist(profileId) publish() return authState.username } - fun onDisconnectRequested() { - ensureLoaded() + fun onDisconnectRequested(profileId: Int = currentProfileId) { + ensureLoaded(profileId) scope.launch { - disconnect() + disconnect(profileId) } } - private suspend fun completeAuthorizationFromCallback(callbackUrl: String) { + private suspend fun completeAuthorizationFromCallback(callbackUrl: String, profileId: Int = currentProfileId) { publish(isLoading = true, errorMessage = null) val parsedUrl = runCatching { Url(callbackUrl) } @@ -189,7 +194,7 @@ object TraktAuthRepository { if (parsedUrl == null) { clearPendingAuthorization() - persist() + persist(profileId) publish( isLoading = false, errorMessage = localizedString(Res.string.trakt_invalid_callback), @@ -202,7 +207,7 @@ object TraktAuthRepository { val errorDescription = parsedUrl.parameters["error_description"] ?: localizedString(Res.string.trakt_authorization_denied) clearPendingAuthorization() - persist() + persist(profileId) publish( isLoading = false, errorMessage = errorDescription, @@ -213,7 +218,7 @@ object TraktAuthRepository { val code = parsedUrl.parameters["code"].orEmpty().trim() if (code.isBlank()) { clearPendingAuthorization() - persist() + persist(profileId) publish( isLoading = false, errorMessage = localizedString(Res.string.trakt_missing_auth_code), @@ -225,7 +230,7 @@ object TraktAuthRepository { val callbackState = parsedUrl.parameters["state"].orEmpty().trim() if (!expectedState.isNullOrBlank() && callbackState != expectedState) { clearPendingAuthorization() - persist() + persist(profileId) publish( isLoading = false, errorMessage = localizedString(Res.string.trakt_invalid_callback_state), @@ -233,10 +238,10 @@ object TraktAuthRepository { return } - exchangeAuthorizationCode(code) + exchangeAuthorizationCode(code, profileId) } - private suspend fun exchangeAuthorizationCode(code: String) { + private suspend fun exchangeAuthorizationCode(code: String, profileId: Int = currentProfileId) { val body = json.encodeToString( TraktAuthorizationCodeRequest( code = code, @@ -259,7 +264,7 @@ object TraktAuthRepository { if (response == null) { clearPendingAuthorization() - persist() + persist(profileId) publish(isLoading = false, errorMessage = localizedString(Res.string.trakt_sign_in_complete_failed)) return } @@ -270,7 +275,7 @@ object TraktAuthRepository { if (parsed == null) { clearPendingAuthorization() - persist() + persist(profileId) publish(isLoading = false, errorMessage = localizedString(Res.string.trakt_invalid_token_response)) return } @@ -284,8 +289,8 @@ object TraktAuthRepository { pendingAuthorizationState = null, pendingAuthorizationStartedAtMillis = null, ) - persist() - refreshUserSettings() + persist(profileId) + refreshUserSettings(profileId) publish( isLoading = false, statusMessage = localizedString(Res.string.trakt_connected_status), @@ -293,7 +298,7 @@ object TraktAuthRepository { ) } - private suspend fun disconnect() { + private suspend fun disconnect(profileId: Int = currentProfileId) { publish(isLoading = true, errorMessage = null) val token = authState.accessToken?.takeIf { it.isNotBlank() } @@ -317,9 +322,9 @@ object TraktAuthRepository { } } - TraktCredentialSync.deleteRemote() + TraktCredentialSync.deleteRemote(profileId) authState = TraktAuthState() - persist() + persist(profileId) publish( isLoading = false, statusMessage = localizedString(Res.string.trakt_disconnected_status), @@ -327,9 +332,8 @@ object TraktAuthRepository { ) } - private suspend fun refreshTokenIfNeeded(force: Boolean): Boolean = refreshMutex.withLock { + private suspend fun refreshTokenIfNeeded(force: Boolean, profileId: Int = currentProfileId): Boolean = refreshMutex.withLock { if (!hasRequiredCredentials()) return@withLock false - val profileId = ProfileRepository.activeProfileId val refreshToken = authState.refreshToken?.takeIf { it.isNotBlank() } ?: return@withLock false @@ -391,14 +395,14 @@ object TraktAuthRepository { createdAt = parsed.createdAt, expiresIn = parsed.expiresIn, ) - persist() + persist(profileId) publish() true } private suspend fun invalidateCredentials(profileId: Int) { authState = TraktAuthState() - persist() + persist(profileId) publish( isLoading = false, statusMessage = null, @@ -407,9 +411,11 @@ object TraktAuthRepository { TraktCredentialSync.deleteRemote(profileId) } - private fun loadFromDisk() { + private fun loadFromDisk(profileId: Int) { + currentProfileId = profileId + profileGeneration += 1L hasLoaded = true - val payload = TraktAuthStorage.loadPayload().orEmpty().trim() + val payload = TraktAuthStorage.loadPayload(profileId).orEmpty().trim() authState = if (payload.isBlank()) { TraktAuthState() } else { @@ -460,8 +466,8 @@ object TraktAuthRepository { ) } - private fun persist() { - TraktAuthStorage.savePayload(json.encodeToString(authState)) + private fun persist(profileId: Int = currentProfileId) { + TraktAuthStorage.savePayload(profileId, json.encodeToString(authState)) } private fun buildAuthorizationUrl(state: String): String { @@ -486,6 +492,7 @@ object TraktAuthRepository { return nowSeconds >= (expiresAtSeconds - 60) } + private fun localizedString(resource: StringResource): String = runBlocking { getString(resource) } } internal enum class TraktTokenRefreshResponseAction { @@ -549,4 +556,3 @@ private data class TraktUserDto( private data class TraktUserIdsDto( val slug: String? = null, ) - private fun localizedString(resource: StringResource): String = runBlocking { getString(resource) } diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/trakt/TraktAuthStorage.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/trakt/TraktAuthStorage.kt index 3527be204..12cb2420f 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/trakt/TraktAuthStorage.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/trakt/TraktAuthStorage.kt @@ -1,6 +1,6 @@ package com.nuvio.app.features.trakt internal expect object TraktAuthStorage { - fun loadPayload(): String? - fun savePayload(payload: String) + fun loadPayload(profileId: Int): String? + fun savePayload(profileId: Int, payload: String) } diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/watched/WatchedRepository.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/watched/WatchedRepository.kt index 27ee20761..cb2cae032 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/watched/WatchedRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/watched/WatchedRepository.kt @@ -270,7 +270,7 @@ object WatchedRepository { ) suspend fun pullFromServer(profileId: Int) { - TraktAuthRepository.ensureLoaded() + TraktAuthRepository.ensureLoaded(profileId) TraktSettingsRepository.ensureLoaded() refreshForSource( profileId = profileId, @@ -283,7 +283,7 @@ object WatchedRepository { } suspend fun forceSnapshotRefreshFromServer(profileId: Int) { - TraktAuthRepository.ensureLoaded() + TraktAuthRepository.ensureLoaded(profileId) TraktSettingsRepository.ensureLoaded() refreshForSource( profileId = profileId, @@ -300,7 +300,7 @@ object WatchedRepository { source: WatchProgressSource, forceSnapshot: Boolean = true, ): Boolean { - TraktAuthRepository.ensureLoaded() + TraktAuthRepository.ensureLoaded(profileId) TraktSettingsRepository.ensureLoaded() if (ProfileRepository.activeProfileId != profileId) { log.d { "Skipping watched refresh for inactive profile $profileId" } diff --git a/composeApp/src/iosMain/kotlin/com/nuvio/app/features/trakt/TraktAuthStorage.ios.kt b/composeApp/src/iosMain/kotlin/com/nuvio/app/features/trakt/TraktAuthStorage.ios.kt index 3ccbaff4e..916fedb0f 100644 --- a/composeApp/src/iosMain/kotlin/com/nuvio/app/features/trakt/TraktAuthStorage.ios.kt +++ b/composeApp/src/iosMain/kotlin/com/nuvio/app/features/trakt/TraktAuthStorage.ios.kt @@ -6,10 +6,10 @@ import platform.Foundation.NSUserDefaults internal actual object TraktAuthStorage { private const val payloadKey = "trakt_auth_payload" - actual fun loadPayload(): String? = - NSUserDefaults.standardUserDefaults.stringForKey(ProfileScopedKey.of(payloadKey)) + actual fun loadPayload(profileId: Int): String? = + NSUserDefaults.standardUserDefaults.stringForKey(ProfileScopedKey.of(payloadKey, profileId)) - actual fun savePayload(payload: String) { - NSUserDefaults.standardUserDefaults.setObject(payload, forKey = ProfileScopedKey.of(payloadKey)) + actual fun savePayload(profileId: Int, payload: String) { + NSUserDefaults.standardUserDefaults.setObject(payload, forKey = ProfileScopedKey.of(payloadKey, profileId)) } } From a8e64fdfe269ae4926162b8a85fc8f4846712698 Mon Sep 17 00:00:00 2001 From: guuilp Date: Mon, 20 Jul 2026 01:37:34 +0000 Subject: [PATCH 2/8] feat(mdblist): add MyAnimeList (MAL) rating from MDBList API --- .../features/mdblist/MdbListSettingsStorage.android.kt | 10 ++++++++++ .../src/commonMain/composeResources/values/strings.xml | 1 + .../app/features/mdblist/MdbListMetadataService.kt | 2 ++ .../com/nuvio/app/features/mdblist/MdbListSettings.kt | 2 ++ .../app/features/mdblist/MdbListSettingsRepository.kt | 7 +++++++ .../app/features/mdblist/MdbListSettingsStorage.kt | 2 ++ .../nuvio/app/features/settings/MdbListSettingsPage.kt | 1 + .../storage/PlatformLocalAccountDataCleaner.ios.kt | 1 + .../app/features/mdblist/MdbListSettingsStorage.ios.kt | 10 ++++++++++ 9 files changed, 36 insertions(+) diff --git a/composeApp/src/androidMain/kotlin/com/nuvio/app/features/mdblist/MdbListSettingsStorage.android.kt b/composeApp/src/androidMain/kotlin/com/nuvio/app/features/mdblist/MdbListSettingsStorage.android.kt index 45e63d432..2d77ab611 100644 --- a/composeApp/src/androidMain/kotlin/com/nuvio/app/features/mdblist/MdbListSettingsStorage.android.kt +++ b/composeApp/src/androidMain/kotlin/com/nuvio/app/features/mdblist/MdbListSettingsStorage.android.kt @@ -22,6 +22,7 @@ actual object MdbListSettingsStorage { private const val useTraktKey = "mdblist_use_trakt" private const val useLetterboxdKey = "mdblist_use_letterboxd" private const val useAudienceKey = "mdblist_use_audience" + private const val useMalKey = "mdblist_use_mal" private val syncKeys = listOf( enabledKey, apiKey, @@ -32,6 +33,7 @@ actual object MdbListSettingsStorage { useTraktKey, useLetterboxdKey, useAudienceKey, + useMalKey, ) private var preferences: SharedPreferences? = null @@ -98,6 +100,12 @@ actual object MdbListSettingsStorage { saveBoolean(useAudienceKey, enabled) } + actual fun loadUseMal(): Boolean? = loadBoolean(useMalKey) + + actual fun saveUseMal(enabled: Boolean) { + saveBoolean(useMalKey, enabled) + } + private fun loadBoolean(key: String): Boolean? = preferences?.let { sharedPreferences -> val scopedKey = ProfileScopedKey.of(key) @@ -125,6 +133,7 @@ actual object MdbListSettingsStorage { loadUseTrakt()?.let { put(useTraktKey, encodeSyncBoolean(it)) } loadUseLetterboxd()?.let { put(useLetterboxdKey, encodeSyncBoolean(it)) } loadUseAudience()?.let { put(useAudienceKey, encodeSyncBoolean(it)) } + loadUseMal()?.let { put(useMalKey, encodeSyncBoolean(it)) } } actual fun replaceFromSyncPayload(payload: JsonObject) { @@ -141,5 +150,6 @@ actual object MdbListSettingsStorage { payload.decodeSyncBoolean(useTraktKey)?.let(::saveUseTrakt) payload.decodeSyncBoolean(useLetterboxdKey)?.let(::saveUseLetterboxd) payload.decodeSyncBoolean(useAudienceKey)?.let(::saveUseAudience) + payload.decodeSyncBoolean(useMalKey)?.let(::saveUseMal) } } diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml index eafc76341..4a7626a7d 100644 --- a/composeApp/src/commonMain/composeResources/values/strings.xml +++ b/composeApp/src/commonMain/composeResources/values/strings.xml @@ -1144,6 +1144,7 @@ Rotten Tomatoes TMDB Trakt + MyAnimeList Unknown Amber Crimson diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/mdblist/MdbListMetadataService.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/mdblist/MdbListMetadataService.kt index b1ca1c1e1..1316e3720 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/mdblist/MdbListMetadataService.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/mdblist/MdbListMetadataService.kt @@ -23,6 +23,7 @@ object MdbListMetadataService { const val PROVIDER_TRAKT = "trakt" const val PROVIDER_LETTERBOXD = "letterboxd" const val PROVIDER_AUDIENCE = "audience" + const val PROVIDER_MAL = "mal" val PROVIDER_PRIORITY_ORDER = listOf( PROVIDER_IMDB, @@ -32,6 +33,7 @@ object MdbListMetadataService { PROVIDER_TRAKT, PROVIDER_LETTERBOXD, PROVIDER_AUDIENCE, + PROVIDER_MAL, ) private val log = Logger.withTag("MdbListMetadata") diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/mdblist/MdbListSettings.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/mdblist/MdbListSettings.kt index e9002303e..31435d398 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/mdblist/MdbListSettings.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/mdblist/MdbListSettings.kt @@ -10,6 +10,7 @@ data class MdbListSettings( val useTrakt: Boolean = true, val useLetterboxd: Boolean = true, val useAudience: Boolean = true, + val useMal: Boolean = true, ) { val hasApiKey: Boolean get() = apiKey.isNotBlank() @@ -23,6 +24,7 @@ data class MdbListSettings( MdbListMetadataService.PROVIDER_TRAKT -> useTrakt MdbListMetadataService.PROVIDER_LETTERBOXD -> useLetterboxd MdbListMetadataService.PROVIDER_AUDIENCE -> useAudience + MdbListMetadataService.PROVIDER_MAL -> useMal else -> false } diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/mdblist/MdbListSettingsRepository.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/mdblist/MdbListSettingsRepository.kt index 6f48eb800..4a7f44e27 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/mdblist/MdbListSettingsRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/mdblist/MdbListSettingsRepository.kt @@ -19,6 +19,7 @@ object MdbListSettingsRepository { private var useTrakt = true private var useLetterboxd = true private var useAudience = true + private var useMal = true fun ensureLoaded() { if (hasLoaded) return @@ -88,6 +89,10 @@ object MdbListSettingsRepository { useAudience = value MdbListSettingsStorage.saveUseAudience(value) } else return + MdbListMetadataService.PROVIDER_MAL -> if (useMal != value) { + useMal = value + MdbListSettingsStorage.saveUseMal(value) + } else return else -> return } publish() @@ -105,6 +110,7 @@ object MdbListSettingsRepository { useTrakt = MdbListSettingsStorage.loadUseTrakt() ?: true useLetterboxd = MdbListSettingsStorage.loadUseLetterboxd() ?: true useAudience = MdbListSettingsStorage.loadUseAudience() ?: true + useMal = MdbListSettingsStorage.loadUseMal() ?: true publish() } @@ -119,6 +125,7 @@ object MdbListSettingsRepository { useTrakt = useTrakt, useLetterboxd = useLetterboxd, useAudience = useAudience, + useMal = useMal, ) } } diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/mdblist/MdbListSettingsStorage.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/mdblist/MdbListSettingsStorage.kt index c1a6f51e1..ff9cd57fd 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/mdblist/MdbListSettingsStorage.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/mdblist/MdbListSettingsStorage.kt @@ -21,6 +21,8 @@ internal expect object MdbListSettingsStorage { fun saveUseLetterboxd(enabled: Boolean) fun loadUseAudience(): Boolean? fun saveUseAudience(enabled: Boolean) + fun loadUseMal(): Boolean? + fun saveUseMal(enabled: Boolean) fun exportToSyncPayload(): JsonObject fun replaceFromSyncPayload(payload: JsonObject) } diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/MdbListSettingsPage.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/MdbListSettingsPage.kt index 3a7a2c40a..e56d1a0b3 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/MdbListSettingsPage.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/MdbListSettingsPage.kt @@ -117,6 +117,7 @@ private fun ProviderRows( MdbListMetadataService.PROVIDER_TRAKT to Res.string.source_trakt, MdbListMetadataService.PROVIDER_LETTERBOXD to Res.string.source_letterboxd, MdbListMetadataService.PROVIDER_AUDIENCE to Res.string.source_audience_score, + MdbListMetadataService.PROVIDER_MAL to Res.string.source_mal, ) providers.forEachIndexed { index, (providerId, providerLabelRes) -> diff --git a/composeApp/src/iosMain/kotlin/com/nuvio/app/core/storage/PlatformLocalAccountDataCleaner.ios.kt b/composeApp/src/iosMain/kotlin/com/nuvio/app/core/storage/PlatformLocalAccountDataCleaner.ios.kt index c9a439664..b6c5373fb 100644 --- a/composeApp/src/iosMain/kotlin/com/nuvio/app/core/storage/PlatformLocalAccountDataCleaner.ios.kt +++ b/composeApp/src/iosMain/kotlin/com/nuvio/app/core/storage/PlatformLocalAccountDataCleaner.ios.kt @@ -51,6 +51,7 @@ internal actual object PlatformLocalAccountDataCleaner { "mdblist_use_trakt", "mdblist_use_letterboxd", "mdblist_use_audience", + "mdblist_use_mal", "trakt_auth_payload", "trakt_library_payload", "trakt_settings_payload", diff --git a/composeApp/src/iosMain/kotlin/com/nuvio/app/features/mdblist/MdbListSettingsStorage.ios.kt b/composeApp/src/iosMain/kotlin/com/nuvio/app/features/mdblist/MdbListSettingsStorage.ios.kt index d05e79f14..3c4297207 100644 --- a/composeApp/src/iosMain/kotlin/com/nuvio/app/features/mdblist/MdbListSettingsStorage.ios.kt +++ b/composeApp/src/iosMain/kotlin/com/nuvio/app/features/mdblist/MdbListSettingsStorage.ios.kt @@ -20,6 +20,7 @@ actual object MdbListSettingsStorage { private const val useTraktKey = "mdblist_use_trakt" private const val useLetterboxdKey = "mdblist_use_letterboxd" private const val useAudienceKey = "mdblist_use_audience" + private const val useMalKey = "mdblist_use_mal" private val syncKeys = listOf( enabledKey, apiKey, @@ -30,6 +31,7 @@ actual object MdbListSettingsStorage { useTraktKey, useLetterboxdKey, useAudienceKey, + useMalKey, ) actual fun loadEnabled(): Boolean? = loadBoolean(enabledKey) @@ -87,6 +89,12 @@ actual object MdbListSettingsStorage { saveBoolean(useAudienceKey, enabled) } + actual fun loadUseMal(): Boolean? = loadBoolean(useMalKey) + + actual fun saveUseMal(enabled: Boolean) { + saveBoolean(useMalKey, enabled) + } + private fun loadBoolean(key: String): Boolean? { val defaults = NSUserDefaults.standardUserDefaults val scopedKey = ProfileScopedKey.of(key) @@ -111,6 +119,7 @@ actual object MdbListSettingsStorage { loadUseTrakt()?.let { put(useTraktKey, encodeSyncBoolean(it)) } loadUseLetterboxd()?.let { put(useLetterboxdKey, encodeSyncBoolean(it)) } loadUseAudience()?.let { put(useAudienceKey, encodeSyncBoolean(it)) } + loadUseMal()?.let { put(useMalKey, encodeSyncBoolean(it)) } } actual fun replaceFromSyncPayload(payload: JsonObject) { @@ -127,5 +136,6 @@ actual object MdbListSettingsStorage { payload.decodeSyncBoolean(useTraktKey)?.let(::saveUseTrakt) payload.decodeSyncBoolean(useLetterboxdKey)?.let(::saveUseLetterboxd) payload.decodeSyncBoolean(useAudienceKey)?.let(::saveUseAudience) + payload.decodeSyncBoolean(useMalKey)?.let(::saveUseMal) } } From 397d0e2ce02fd5b43f574d13b8c8d0f2f8262510 Mon Sep 17 00:00:00 2001 From: guuilp Date: Mon, 20 Jul 2026 01:54:59 +0000 Subject: [PATCH 3/8] feat(mdblist): add MAL rating icon and display in detail screen --- .../commonMain/composeResources/drawable/rating_mal.svg | 7 +++++++ .../app/features/details/components/DetailMetaInfo.kt | 9 +++++++++ 2 files changed, 16 insertions(+) create mode 100644 composeApp/src/commonMain/composeResources/drawable/rating_mal.svg diff --git a/composeApp/src/commonMain/composeResources/drawable/rating_mal.svg b/composeApp/src/commonMain/composeResources/drawable/rating_mal.svg new file mode 100644 index 000000000..9d1220e97 --- /dev/null +++ b/composeApp/src/commonMain/composeResources/drawable/rating_mal.svg @@ -0,0 +1,7 @@ + + MyAnimeList + + + + + diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/components/DetailMetaInfo.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/components/DetailMetaInfo.kt index bbf2e7233..43ede06db 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/components/DetailMetaInfo.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/components/DetailMetaInfo.kt @@ -50,6 +50,7 @@ import com.nuvio.app.features.mdblist.MdbListMetadataService.PROVIDER_AUDIENCE import com.nuvio.app.features.mdblist.MdbListMetadataService.PROVIDER_IMDB import com.nuvio.app.features.mdblist.MdbListMetadataService.PROVIDER_LETTERBOXD import com.nuvio.app.features.mdblist.MdbListMetadataService.PROVIDER_METACRITIC +import com.nuvio.app.features.mdblist.MdbListMetadataService.PROVIDER_MAL import com.nuvio.app.features.mdblist.MdbListMetadataService.PROVIDER_TMDB import com.nuvio.app.features.mdblist.MdbListMetadataService.PROVIDER_TOMATOES import com.nuvio.app.features.mdblist.MdbListMetadataService.PROVIDER_TRAKT @@ -390,6 +391,14 @@ private val ratingVisuals = listOf( valueColor = Color(0xFF00E054), format = ::formatOneDecimal, ), + RatingVisuals( + source = PROVIDER_MAL, + displayName = "MyAnimeList", + logo = Res.drawable.rating_mal, + logoWidth = 16.dp, + valueColor = Color(0xFF2E51A2), + format = ::formatOneDecimal, + ), RatingVisuals( source = PROVIDER_AUDIENCE, displayName = runBlocking { getString(Res.string.rating_audience_score) }, From 6366142e0b378b968688dcf1cb9bcd038c023fa5 Mon Sep 17 00:00:00 2001 From: guuilp Date: Mon, 20 Jul 2026 02:01:07 +0000 Subject: [PATCH 4/8] =?UTF-8?q?fix(mdblist):=20invert=20MAL=20SVG=20colors?= =?UTF-8?q?=20=E2=80=94=20blue=20bg,=20white=20text?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/commonMain/composeResources/drawable/rating_mal.svg | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/composeApp/src/commonMain/composeResources/drawable/rating_mal.svg b/composeApp/src/commonMain/composeResources/drawable/rating_mal.svg index 9d1220e97..efac149cd 100644 --- a/composeApp/src/commonMain/composeResources/drawable/rating_mal.svg +++ b/composeApp/src/commonMain/composeResources/drawable/rating_mal.svg @@ -1,7 +1,8 @@ + MyAnimeList - + - + From 720c6d8b6edfe96a2645f91a05b53ec2a97d9247 Mon Sep 17 00:00:00 2001 From: guuilp Date: Mon, 20 Jul 2026 21:41:57 +0000 Subject: [PATCH 5/8] =?UTF-8?q?fix(mdblist):=20reorder=20ratings=20?= =?UTF-8?q?=E2=80=94=20MAL=20before=20Rotten=20Tomatoes=20pair,=20fix=20so?= =?UTF-8?q?urce=5Fmal=20import?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../details/components/DetailMetaInfo.kt | 32 +++++++++---------- .../features/settings/MdbListSettingsPage.kt | 1 + 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/components/DetailMetaInfo.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/components/DetailMetaInfo.kt index 43ede06db..65da8ae65 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/components/DetailMetaInfo.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/components/DetailMetaInfo.kt @@ -359,22 +359,6 @@ private val ratingVisuals = listOf( valueColor = Color(0xFF01B4E4), format = ::formatWhole, ), - RatingVisuals( - source = PROVIDER_TOMATOES, - displayName = "Rotten Tomatoes", - logo = Res.drawable.rating_rotten_tomatoes, - logoWidth = 16.dp, - valueColor = Color(0xFFFA320A), - format = ::formatPercent, - ), - RatingVisuals( - source = PROVIDER_METACRITIC, - displayName = "Metacritic", - logo = Res.drawable.rating_metacritic, - logoWidth = 16.dp, - valueColor = Color(0xFFFFCC33), - format = ::formatWhole, - ), RatingVisuals( source = PROVIDER_TRAKT, displayName = "Trakt", @@ -399,6 +383,14 @@ private val ratingVisuals = listOf( valueColor = Color(0xFF2E51A2), format = ::formatOneDecimal, ), + RatingVisuals( + source = PROVIDER_TOMATOES, + displayName = "Rotten Tomatoes", + logo = Res.drawable.rating_rotten_tomatoes, + logoWidth = 16.dp, + valueColor = Color(0xFFFA320A), + format = ::formatPercent, + ), RatingVisuals( source = PROVIDER_AUDIENCE, displayName = runBlocking { getString(Res.string.rating_audience_score) }, @@ -407,6 +399,14 @@ private val ratingVisuals = listOf( valueColor = Color(0xFFFA320A), format = ::formatPercent, ), + RatingVisuals( + source = PROVIDER_METACRITIC, + displayName = "Metacritic", + logo = Res.drawable.rating_metacritic, + logoWidth = 16.dp, + valueColor = Color(0xFFFFCC33), + format = ::formatWhole, + ), ) private fun formatOneDecimal(value: Double): String { diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/MdbListSettingsPage.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/MdbListSettingsPage.kt index e56d1a0b3..49842bf34 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/MdbListSettingsPage.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/MdbListSettingsPage.kt @@ -34,6 +34,7 @@ import nuvio.composeapp.generated.resources.settings_mdb_section_title import nuvio.composeapp.generated.resources.source_audience_score import nuvio.composeapp.generated.resources.source_imdb import nuvio.composeapp.generated.resources.source_letterboxd +import nuvio.composeapp.generated.resources.source_mal import nuvio.composeapp.generated.resources.source_metacritic import nuvio.composeapp.generated.resources.source_rotten_tomatoes import nuvio.composeapp.generated.resources.source_tmdb From 9ac946e4ea787560bfad0b602d2775337d4f73a2 Mon Sep 17 00:00:00 2001 From: guuilp Date: Mon, 20 Jul 2026 21:51:53 +0000 Subject: [PATCH 6/8] =?UTF-8?q?fix(mdblist):=20replace=20MAL=20SVG=20with?= =?UTF-8?q?=20PNG=20=E2=80=94=20Android=20does=20not=20support=20SVG=20in?= =?UTF-8?q?=20painterResource?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../composeResources/drawable/rating_mal.png | Bin 0 -> 19017 bytes .../composeResources/drawable/rating_mal.svg | 8 -------- 2 files changed, 8 deletions(-) create mode 100644 composeApp/src/commonMain/composeResources/drawable/rating_mal.png delete mode 100644 composeApp/src/commonMain/composeResources/drawable/rating_mal.svg diff --git a/composeApp/src/commonMain/composeResources/drawable/rating_mal.png b/composeApp/src/commonMain/composeResources/drawable/rating_mal.png new file mode 100644 index 0000000000000000000000000000000000000000..9a2d5e970123508480585e7400850dd28148e658 GIT binary patch literal 19017 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 - - MyAnimeList - - - - - From b5bbed9e6618e3adf2c7aa9ff68941cc03999cb0 Mon Sep 17 00:00:00 2001 From: guuilp Date: Mon, 20 Jul 2026 21:52:55 +0000 Subject: [PATCH 7/8] =?UTF-8?q?fix(mdblist):=20replace=20PNG=20with=20XML?= =?UTF-8?q?=20VectorDrawable=20=E2=80=94=20native=20Android=20vector=20for?= =?UTF-8?q?mat?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../composeResources/drawable/rating_mal.png | Bin 19017 -> 0 bytes .../composeResources/drawable/rating_mal.xml | 19 ++++++++++++++++++ 2 files changed, 19 insertions(+) delete mode 100644 composeApp/src/commonMain/composeResources/drawable/rating_mal.png create mode 100644 composeApp/src/commonMain/composeResources/drawable/rating_mal.xml diff --git a/composeApp/src/commonMain/composeResources/drawable/rating_mal.png b/composeApp/src/commonMain/composeResources/drawable/rating_mal.png deleted file mode 100644 index 9a2d5e970123508480585e7400850dd28148e658..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19017 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 + + + + + + From 5037f265326567f425e511c75d10b88a0783f9a3 Mon Sep 17 00:00:00 2001 From: guuilp Date: Mon, 20 Jul 2026 21:56:07 +0000 Subject: [PATCH 8/8] =?UTF-8?q?fix(mdblist):=20replace=20PNG=20with=20cair?= =?UTF-8?q?osvg=20render=20=E2=80=94=20correct=20centering,=203.2KB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../composeResources/drawable/rating_mal.png | Bin 0 -> 3258 bytes .../composeResources/drawable/rating_mal.xml | 19 ------------------ 2 files changed, 19 deletions(-) create mode 100644 composeApp/src/commonMain/composeResources/drawable/rating_mal.png delete mode 100644 composeApp/src/commonMain/composeResources/drawable/rating_mal.xml diff --git a/composeApp/src/commonMain/composeResources/drawable/rating_mal.png b/composeApp/src/commonMain/composeResources/drawable/rating_mal.png new file mode 100644 index 0000000000000000000000000000000000000000..1bbf12ce08bacb333117935ad39a562ddda91e9b GIT binary patch literal 3258 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 Q`7;8TT(>~IH*k&rKh5Y0qW}N^ literal 0 HcmV?d00001 diff --git a/composeApp/src/commonMain/composeResources/drawable/rating_mal.xml b/composeApp/src/commonMain/composeResources/drawable/rating_mal.xml deleted file mode 100644 index 4ecc0f246..000000000 --- a/composeApp/src/commonMain/composeResources/drawable/rating_mal.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - -