feat: adding seperate preference key for collections

This commit is contained in:
tapframe 2026-05-09 00:46:55 +05:30
parent 0ce89650c2
commit c16711ebb8
16 changed files with 314 additions and 12 deletions

View file

@ -13,6 +13,7 @@ import com.nuvio.app.core.auth.AuthStorage
import com.nuvio.app.core.deeplink.handleAppUrl import com.nuvio.app.core.deeplink.handleAppUrl
import com.nuvio.app.core.storage.PlatformLocalAccountDataCleaner import com.nuvio.app.core.storage.PlatformLocalAccountDataCleaner
import com.nuvio.app.features.addons.AddonStorage import com.nuvio.app.features.addons.AddonStorage
import com.nuvio.app.features.collection.CollectionMobileSettingsStorage
import com.nuvio.app.features.collection.CollectionStorage import com.nuvio.app.features.collection.CollectionStorage
import com.nuvio.app.features.downloads.DownloadsLiveStatusPlatform import com.nuvio.app.features.downloads.DownloadsLiveStatusPlatform
import com.nuvio.app.features.downloads.DownloadsPlatformDownloader import com.nuvio.app.features.downloads.DownloadsPlatformDownloader
@ -83,6 +84,7 @@ class MainActivity : AppCompatActivity() {
WatchProgressStorage.initialize(applicationContext) WatchProgressStorage.initialize(applicationContext)
StreamLinkCacheStorage.initialize(applicationContext) StreamLinkCacheStorage.initialize(applicationContext)
PluginStorage.initialize(applicationContext) PluginStorage.initialize(applicationContext)
CollectionMobileSettingsStorage.initialize(applicationContext)
CollectionStorage.initialize(applicationContext) CollectionStorage.initialize(applicationContext)
DownloadsStorage.initialize(applicationContext) DownloadsStorage.initialize(applicationContext)
DownloadsPlatformDownloader.initialize(applicationContext) DownloadsPlatformDownloader.initialize(applicationContext)

View file

@ -23,6 +23,7 @@ internal actual object PlatformLocalAccountDataCleaner {
"nuvio_episode_release_notifications", "nuvio_episode_release_notifications",
"nuvio_episode_release_notifications_platform", "nuvio_episode_release_notifications_platform",
"nuvio_watch_progress", "nuvio_watch_progress",
"nuvio_collection_mobile_settings",
"nuvio_collections", "nuvio_collections",
"nuvio_plugins", "nuvio_plugins",
) )

View file

@ -0,0 +1,26 @@
package com.nuvio.app.features.collection
import android.content.Context
import android.content.SharedPreferences
import com.nuvio.app.core.storage.ProfileScopedKey
actual object CollectionMobileSettingsStorage {
private const val preferencesName = "nuvio_collection_mobile_settings"
private const val payloadKey = "collection_mobile_settings_payload"
private var preferences: SharedPreferences? = null
fun initialize(context: Context) {
preferences = context.getSharedPreferences(preferencesName, Context.MODE_PRIVATE)
}
actual fun loadPayload(): String? =
preferences?.getString(ProfileScopedKey.of(payloadKey), null)
actual fun savePayload(payload: String) {
preferences
?.edit()
?.putString(ProfileScopedKey.of(payloadKey), payload)
?.apply()
}
}

View file

@ -3,6 +3,7 @@ package com.nuvio.app.core.storage
import com.nuvio.app.core.build.AppFeaturePolicy import com.nuvio.app.core.build.AppFeaturePolicy
import com.nuvio.app.features.addons.AddonRepository import com.nuvio.app.features.addons.AddonRepository
import com.nuvio.app.features.catalog.CatalogRepository import com.nuvio.app.features.catalog.CatalogRepository
import com.nuvio.app.features.collection.CollectionMobileSettingsRepository
import com.nuvio.app.features.collection.CollectionRepository import com.nuvio.app.features.collection.CollectionRepository
import com.nuvio.app.features.details.MetaDetailsRepository import com.nuvio.app.features.details.MetaDetailsRepository
import com.nuvio.app.features.details.MetaScreenSettingsRepository import com.nuvio.app.features.details.MetaScreenSettingsRepository
@ -44,6 +45,7 @@ internal object LocalAccountDataCleaner {
WatchedRepository.clearLocalState() WatchedRepository.clearLocalState()
ContinueWatchingPreferencesRepository.clearLocalState() ContinueWatchingPreferencesRepository.clearLocalState()
EpisodeReleaseNotificationsRepository.clearLocalState() EpisodeReleaseNotificationsRepository.clearLocalState()
CollectionMobileSettingsRepository.clearLocalState()
CollectionRepository.clearLocalState() CollectionRepository.clearLocalState()
ThemeSettingsRepository.clearLocalState() ThemeSettingsRepository.clearLocalState()
PosterCardStyleRepository.clearLocalState() PosterCardStyleRepository.clearLocalState()

View file

@ -4,6 +4,8 @@ import co.touchlab.kermit.Logger
import com.nuvio.app.core.auth.AuthRepository import com.nuvio.app.core.auth.AuthRepository
import com.nuvio.app.core.auth.AuthState import com.nuvio.app.core.auth.AuthState
import com.nuvio.app.core.network.SupabaseProvider import com.nuvio.app.core.network.SupabaseProvider
import com.nuvio.app.features.collection.CollectionMobileSettingsRepository
import com.nuvio.app.features.collection.CollectionMobileSettingsStorage
import com.nuvio.app.features.details.MetaScreenSettingsStorage import com.nuvio.app.features.details.MetaScreenSettingsStorage
import com.nuvio.app.features.details.MetaScreenSettingsRepository import com.nuvio.app.features.details.MetaScreenSettingsRepository
import com.nuvio.app.features.mdblist.MdbListMetadataService import com.nuvio.app.features.mdblist.MdbListMetadataService
@ -158,6 +160,7 @@ object ProfileSettingsSync {
TmdbSettingsRepository.uiState.map { "tmdb" }, TmdbSettingsRepository.uiState.map { "tmdb" },
MdbListSettingsRepository.uiState.map { "mdblist" }, MdbListSettingsRepository.uiState.map { "mdblist" },
MetaScreenSettingsRepository.uiState.map { "meta" }, MetaScreenSettingsRepository.uiState.map { "meta" },
CollectionMobileSettingsRepository.uiState.map { "collection_mobile_settings" },
ContinueWatchingPreferencesRepository.uiState.map { "continue_watching" }, ContinueWatchingPreferencesRepository.uiState.map { "continue_watching" },
TraktSettingsRepository.uiState.map { "trakt_settings" }, TraktSettingsRepository.uiState.map { "trakt_settings" },
TraktCommentsSettings.enabled.map { "trakt_comments" }, TraktCommentsSettings.enabled.map { "trakt_comments" },
@ -202,6 +205,7 @@ object ProfileSettingsSync {
tmdbSettings = TmdbSettingsStorage.exportToSyncPayload(), tmdbSettings = TmdbSettingsStorage.exportToSyncPayload(),
mdbListSettings = MdbListSettingsStorage.exportToSyncPayload(), mdbListSettings = MdbListSettingsStorage.exportToSyncPayload(),
metaScreenSettingsPayload = MetaScreenSettingsStorage.loadPayload().orEmpty().trim(), metaScreenSettingsPayload = MetaScreenSettingsStorage.loadPayload().orEmpty().trim(),
collectionMobileSettingsPayload = CollectionMobileSettingsStorage.loadPayload().orEmpty().trim(),
continueWatchingSettingsPayload = ContinueWatchingPreferencesStorage.loadPayload().orEmpty().trim(), continueWatchingSettingsPayload = ContinueWatchingPreferencesStorage.loadPayload().orEmpty().trim(),
traktSettingsPayload = TraktSettingsStorage.loadPayload().orEmpty().trim(), traktSettingsPayload = TraktSettingsStorage.loadPayload().orEmpty().trim(),
traktCommentsSettings = TraktCommentsStorage.exportToSyncPayload(), traktCommentsSettings = TraktCommentsStorage.exportToSyncPayload(),
@ -232,6 +236,9 @@ object ProfileSettingsSync {
MetaScreenSettingsStorage.savePayload(blob.features.metaScreenSettingsPayload) MetaScreenSettingsStorage.savePayload(blob.features.metaScreenSettingsPayload)
MetaScreenSettingsRepository.onProfileChanged() MetaScreenSettingsRepository.onProfileChanged()
CollectionMobileSettingsStorage.savePayload(blob.features.collectionMobileSettingsPayload)
CollectionMobileSettingsRepository.onProfileChanged()
ContinueWatchingPreferencesStorage.savePayload(blob.features.continueWatchingSettingsPayload) ContinueWatchingPreferencesStorage.savePayload(blob.features.continueWatchingSettingsPayload)
ContinueWatchingPreferencesRepository.onProfileChanged() ContinueWatchingPreferencesRepository.onProfileChanged()
@ -251,6 +258,7 @@ object ProfileSettingsSync {
TmdbSettingsRepository.ensureLoaded() TmdbSettingsRepository.ensureLoaded()
MdbListSettingsRepository.ensureLoaded() MdbListSettingsRepository.ensureLoaded()
MetaScreenSettingsRepository.ensureLoaded() MetaScreenSettingsRepository.ensureLoaded()
CollectionMobileSettingsRepository.ensureLoaded()
ContinueWatchingPreferencesRepository.ensureLoaded() ContinueWatchingPreferencesRepository.ensureLoaded()
TraktSettingsRepository.ensureLoaded() TraktSettingsRepository.ensureLoaded()
TraktCommentsSettings.ensureLoaded() TraktCommentsSettings.ensureLoaded()
@ -272,6 +280,7 @@ object ProfileSettingsSync {
"tmdb=${TmdbSettingsRepository.uiState.value}", "tmdb=${TmdbSettingsRepository.uiState.value}",
"mdblist=${MdbListSettingsRepository.uiState.value}", "mdblist=${MdbListSettingsRepository.uiState.value}",
"meta=${MetaScreenSettingsRepository.uiState.value}", "meta=${MetaScreenSettingsRepository.uiState.value}",
"collection_mobile_settings=${CollectionMobileSettingsRepository.uiState.value}",
"continue=${ContinueWatchingPreferencesRepository.uiState.value}", "continue=${ContinueWatchingPreferencesRepository.uiState.value}",
"trakt_settings=${TraktSettingsRepository.uiState.value}", "trakt_settings=${TraktSettingsRepository.uiState.value}",
"trakt_comments=${TraktCommentsSettings.enabled.value}", "trakt_comments=${TraktCommentsSettings.enabled.value}",
@ -293,6 +302,7 @@ private data class MobileProfileSettingsFeatures(
@SerialName("tmdb_settings") val tmdbSettings: JsonObject = JsonObject(emptyMap()), @SerialName("tmdb_settings") val tmdbSettings: JsonObject = JsonObject(emptyMap()),
@SerialName("mdblist_settings") val mdbListSettings: JsonObject = JsonObject(emptyMap()), @SerialName("mdblist_settings") val mdbListSettings: JsonObject = JsonObject(emptyMap()),
@SerialName("meta_screen_settings_payload") val metaScreenSettingsPayload: String = "", @SerialName("meta_screen_settings_payload") val metaScreenSettingsPayload: String = "",
@SerialName("collection_mobile_settings_payload") val collectionMobileSettingsPayload: String = "",
@SerialName("continue_watching_settings_payload") val continueWatchingSettingsPayload: String = "", @SerialName("continue_watching_settings_payload") val continueWatchingSettingsPayload: String = "",
@SerialName("trakt_settings_payload") val traktSettingsPayload: String = "", @SerialName("trakt_settings_payload") val traktSettingsPayload: String = "",
@SerialName("trakt_comments_settings") val traktCommentsSettings: JsonObject = JsonObject(emptyMap()), @SerialName("trakt_comments_settings") val traktCommentsSettings: JsonObject = JsonObject(emptyMap()),

View file

@ -195,10 +195,10 @@ object CollectionEditorRepository {
) )
} }
fun updateFolderFocusGifEnabled(enabled: Boolean) { fun updateFolderMobileFocusGifEnabled(enabled: Boolean) {
val folder = _uiState.value.editingFolder ?: return val folder = _uiState.value.editingFolder ?: return
_uiState.value = _uiState.value.copy( _uiState.value = _uiState.value.copy(
editingFolder = folder.copy(focusGifEnabled = enabled), editingFolder = folder.copy(mobileFocusGifEnabled = enabled),
) )
} }
@ -808,6 +808,8 @@ object CollectionEditorRepository {
folders = state.folders, folders = state.folders,
) )
CollectionMobileSettingsRepository.replaceCollectionFolderGifSettings(collection.id, collection.folders)
if (state.isNew) { if (state.isNew) {
CollectionRepository.addCollection(collection) CollectionRepository.addCollection(collection)
} else { } else {

View file

@ -702,8 +702,8 @@ private fun FolderEditorPage(
FolderEditorToggleRow( FolderEditorToggleRow(
title = stringResource(Res.string.collections_editor_show_gif_when_configured), title = stringResource(Res.string.collections_editor_show_gif_when_configured),
subtitle = stringResource(Res.string.collections_editor_show_gif_when_configured_desc), subtitle = stringResource(Res.string.collections_editor_show_gif_when_configured_desc),
checked = folder.focusGifEnabled, checked = folder.mobileFocusGifEnabled,
onCheckedChange = { CollectionEditorRepository.updateFolderFocusGifEnabled(it) }, onCheckedChange = { CollectionEditorRepository.updateFolderMobileFocusGifEnabled(it) },
) )
FolderEditorToggleRow( FolderEditorToggleRow(

View file

@ -0,0 +1,155 @@
package com.nuvio.app.features.collection
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
data class CollectionMobileSettingsUiState(
val folderGifOverrides: Map<String, Boolean> = emptyMap(),
)
object CollectionMobileSettingsRepository {
private val json = Json {
ignoreUnknownKeys = true
encodeDefaults = true
}
private val _uiState = MutableStateFlow(CollectionMobileSettingsUiState())
val uiState: StateFlow<CollectionMobileSettingsUiState> = _uiState.asStateFlow()
private var hasLoaded = false
fun ensureLoaded() {
if (hasLoaded) return
loadFromDisk()
}
fun onProfileChanged() {
loadFromDisk()
CollectionRepository.onMobileSettingsChanged()
}
fun clearLocalState() {
hasLoaded = false
_uiState.value = CollectionMobileSettingsUiState()
}
fun isFolderGifEnabled(collectionId: String, folderId: String): Boolean {
ensureLoaded()
return _uiState.value.folderGifOverrides[folderKey(collectionId, folderId)] ?: true
}
fun applyToCollections(collections: List<Collection>): List<Collection> {
ensureLoaded()
return collections.map(::applyToCollection)
}
fun applyToCollection(collection: Collection): Collection {
ensureLoaded()
return collection.copy(
folders = collection.folders.map { folder ->
folder.copy(
mobileFocusGifEnabled = isFolderGifEnabled(
collectionId = collection.id,
folderId = folder.id,
),
)
},
)
}
fun replaceCollectionFolderGifSettings(collectionId: String, folders: List<CollectionFolder>) {
ensureLoaded()
val collectionPrefix = "${collectionId.trim()}$FolderKeySeparator"
val next = _uiState.value.folderGifOverrides
.filterKeys { key -> !key.startsWith(collectionPrefix) }
.toMutableMap()
folders.forEach { folder ->
val key = folderKey(collectionId, folder.id)
if (folder.mobileFocusGifEnabled) {
next.remove(key)
} else {
next[key] = false
}
}
_uiState.value = CollectionMobileSettingsUiState(folderGifOverrides = next)
persist()
CollectionRepository.onMobileSettingsChanged()
}
private fun loadFromDisk() {
hasLoaded = true
val payload = CollectionMobileSettingsStorage.loadPayload().orEmpty().trim()
if (payload.isEmpty()) {
_uiState.value = CollectionMobileSettingsUiState()
return
}
val stored = runCatching {
json.decodeFromString<StoredCollectionMobileSettingsPayload>(payload)
}.getOrNull()
_uiState.value = CollectionMobileSettingsUiState(
folderGifOverrides = stored
?.folderGifOverrides
.orEmpty()
.mapNotNull { item ->
if (item.collectionId.isBlank() || item.folderId.isBlank()) {
null
} else {
folderKey(item.collectionId, item.folderId) to item.enabled
}
}
.toMap(),
)
}
private fun persist() {
if (_uiState.value.folderGifOverrides.isEmpty()) {
CollectionMobileSettingsStorage.savePayload("")
return
}
val payload = StoredCollectionMobileSettingsPayload(
folderGifOverrides = _uiState.value.folderGifOverrides
.mapNotNull { (key, enabled) ->
val parts = key.split(FolderKeySeparator, limit = 2)
val collectionId = parts.getOrNull(0).orEmpty()
val folderId = parts.getOrNull(1).orEmpty()
if (collectionId.isBlank() || folderId.isBlank()) {
null
} else {
StoredFolderGifOverride(
collectionId = collectionId,
folderId = folderId,
enabled = enabled,
)
}
}
.sortedWith(compareBy<StoredFolderGifOverride> { it.collectionId }.thenBy { it.folderId }),
)
CollectionMobileSettingsStorage.savePayload(json.encodeToString(payload))
}
private fun folderKey(collectionId: String, folderId: String): String =
"${collectionId.trim()}$FolderKeySeparator${folderId.trim()}"
}
private const val FolderKeySeparator = "\u001F"
@Serializable
private data class StoredCollectionMobileSettingsPayload(
@SerialName("folder_gif_overrides") val folderGifOverrides: List<StoredFolderGifOverride> = emptyList(),
)
@Serializable
private data class StoredFolderGifOverride(
@SerialName("collection_id") val collectionId: String,
@SerialName("folder_id") val folderId: String,
val enabled: Boolean = true,
)

View file

@ -0,0 +1,6 @@
package com.nuvio.app.features.collection
internal expect object CollectionMobileSettingsStorage {
fun loadPayload(): String?
fun savePayload(payload: String)
}

View file

@ -4,6 +4,7 @@ import androidx.compose.runtime.Immutable
import com.nuvio.app.features.home.PosterShape import com.nuvio.app.features.home.PosterShape
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
enum class FolderViewMode { enum class FolderViewMode {
TABBED_GRID, TABBED_GRID,
@ -168,6 +169,8 @@ data class CollectionFolder(
val coverImageUrl: String? = null, val coverImageUrl: String? = null,
val focusGifUrl: String? = null, val focusGifUrl: String? = null,
val focusGifEnabled: Boolean = true, val focusGifEnabled: Boolean = true,
@Transient
val mobileFocusGifEnabled: Boolean = true,
val coverEmoji: String? = null, val coverEmoji: String? = null,
val tileShape: String = "poster", val tileShape: String = "poster",
val hideTitle: Boolean = false, val hideTitle: Boolean = false,

View file

@ -52,7 +52,8 @@ object CollectionRepository {
runCatching { runCatching {
val parsed = json.parseToJsonElement(payload) val parsed = json.parseToJsonElement(payload)
rawCollectionsJson = parsed rawCollectionsJson = parsed
_collections.value = json.decodeFromString<List<Collection>>(payload) val decoded = json.decodeFromString<List<Collection>>(payload)
_collections.value = CollectionMobileSettingsRepository.applyToCollections(decoded)
}.onFailure { e -> }.onFailure { e ->
log.e(e) { "Failed to load collections from storage" } log.e(e) { "Failed to load collections from storage" }
} }
@ -75,14 +76,15 @@ object CollectionRepository {
fun addCollection(collection: Collection) { fun addCollection(collection: Collection) {
ensureLoaded() ensureLoaded()
_collections.value = _collections.value + collection _collections.value = _collections.value + CollectionMobileSettingsRepository.applyToCollection(collection)
persist() persist()
} }
fun updateCollection(collection: Collection) { fun updateCollection(collection: Collection) {
ensureLoaded() ensureLoaded()
val decorated = CollectionMobileSettingsRepository.applyToCollection(collection)
_collections.value = _collections.value.map { _collections.value = _collections.value.map {
if (it.id == collection.id) collection else it if (it.id == collection.id) decorated else it
} }
persist() persist()
} }
@ -95,7 +97,7 @@ object CollectionRepository {
fun setCollections(collections: List<Collection>) { fun setCollections(collections: List<Collection>) {
ensureLoaded() ensureLoaded()
_collections.value = collections _collections.value = CollectionMobileSettingsRepository.applyToCollections(collections)
persist() persist()
} }
@ -127,7 +129,7 @@ object CollectionRepository {
return runCatching { return runCatching {
rawCollectionsJson = json.parseToJsonElement(jsonString) rawCollectionsJson = json.parseToJsonElement(jsonString)
val imported = json.decodeFromString<List<Collection>>(jsonString) val imported = json.decodeFromString<List<Collection>>(jsonString)
_collections.value = imported _collections.value = CollectionMobileSettingsRepository.applyToCollections(imported)
persist() persist()
imported imported
} }
@ -262,10 +264,15 @@ object CollectionRepository {
internal fun applyFromRemote(collections: List<Collection>, rawJson: JsonElement) { internal fun applyFromRemote(collections: List<Collection>, rawJson: JsonElement) {
rawCollectionsJson = rawJson rawCollectionsJson = rawJson
_collections.value = collections _collections.value = CollectionMobileSettingsRepository.applyToCollections(collections)
persist(sync = false) persist(sync = false)
} }
internal fun onMobileSettingsChanged() {
if (!hasLoaded) return
_collections.value = CollectionMobileSettingsRepository.applyToCollections(_collections.value)
}
private fun ensureLoaded() { private fun ensureLoaded() {
if (!hasLoaded) initialize() if (!hasLoaded) initialize()
} }

View file

@ -186,7 +186,7 @@ private fun CollectionFolderCard(
} }
private fun collectionFolderCardImageUrl(folder: CollectionFolder): String? { private fun collectionFolderCardImageUrl(folder: CollectionFolder): String? {
return if (folder.focusGifEnabled) { return if (folder.mobileFocusGifEnabled) {
firstNonBlank(folder.focusGifUrl, folder.coverImageUrl) firstNonBlank(folder.focusGifUrl, folder.coverImageUrl)
} else { } else {
firstNonBlank(folder.coverImageUrl) firstNonBlank(folder.coverImageUrl)
@ -202,5 +202,5 @@ private fun isAnimatedCollectionFolderImage(
imageUrl: String, imageUrl: String,
): Boolean { ): Boolean {
val gifUrl = firstNonBlank(folder.focusGifUrl) ?: return false val gifUrl = firstNonBlank(folder.focusGifUrl) ?: return false
return folder.focusGifEnabled && imageUrl == gifUrl return folder.mobileFocusGifEnabled && imageUrl == gifUrl
} }

View file

@ -6,6 +6,7 @@ import com.nuvio.app.core.auth.AuthState
import com.nuvio.app.core.auth.isAnonymous import com.nuvio.app.core.auth.isAnonymous
import com.nuvio.app.core.network.SupabaseProvider import com.nuvio.app.core.network.SupabaseProvider
import com.nuvio.app.features.addons.AddonRepository import com.nuvio.app.features.addons.AddonRepository
import com.nuvio.app.features.collection.CollectionMobileSettingsRepository
import com.nuvio.app.features.collection.CollectionRepository import com.nuvio.app.features.collection.CollectionRepository
import com.nuvio.app.features.downloads.DownloadsRepository import com.nuvio.app.features.downloads.DownloadsRepository
import com.nuvio.app.features.details.MetaScreenSettingsRepository import com.nuvio.app.features.details.MetaScreenSettingsRepository
@ -156,6 +157,7 @@ object ProfileRepository {
TraktAuthRepository.onProfileChanged() TraktAuthRepository.onProfileChanged()
SearchHistoryRepository.onProfileChanged() SearchHistoryRepository.onProfileChanged()
CollectionRepository.onProfileChanged() CollectionRepository.onProfileChanged()
CollectionMobileSettingsRepository.onProfileChanged()
DownloadsRepository.onProfileChanged() DownloadsRepository.onProfileChanged()
} }

View file

@ -3,8 +3,13 @@ package com.nuvio.app.features.collection
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.boolean
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotNull import kotlin.test.assertNotNull
import kotlin.test.assertTrue import kotlin.test.assertTrue
@ -178,4 +183,69 @@ class CollectionSourceSerializationTest {
assertTrue(merged.contains(""""customField":"keep-me"""")) assertTrue(merged.contains(""""customField":"keep-me""""))
assertTrue(merged.contains(""""traktListId":123456""")) assertTrue(merged.contains(""""traktListId":123456"""))
} }
@Test
fun mobileGifToggleDoesNotEnterCollectionJsonOrOverwriteTvGifToggle() {
val raw = json.parseToJsonElement(
"""
[
{
"id": "collection-1",
"title": "Favorites",
"folders": [
{
"id": "folder-1",
"title": "Movies",
"coverImageUrl": "https://example.com/poster.jpg",
"focusGifUrl": "https://example.com/focus.gif",
"focusGifEnabled": true
}
]
}
]
""".trimIndent(),
)
val collection = json.decodeFromString<List<Collection>>(raw.toString()).single()
val mobileDisabled = collection.copy(
folders = collection.folders.map { folder ->
folder.copy(mobileFocusGifEnabled = false)
},
)
val merged = CollectionJsonPreserver.merge(json, raw, listOf(mobileDisabled))
val mergedFolder = merged
.single()
.jsonObject["folders"]!!
.jsonArray
.single()
.jsonObject
assertTrue(mergedFolder["focusGifEnabled"]!!.jsonPrimitive.boolean)
assertTrue(mergedFolder["mobileFocusGifEnabled"] == null)
}
@Test
fun mobileGifToggleDefaultsIndependentOfTvGifToggle() {
val payload = """
[
{
"id": "collection-1",
"title": "Favorites",
"folders": [
{
"id": "folder-1",
"title": "Movies",
"focusGifUrl": "https://example.com/focus.gif",
"focusGifEnabled": false
}
]
}
]
""".trimIndent()
val folder = json.decodeFromString<List<Collection>>(payload).single().folders.single()
assertFalse(folder.focusGifEnabled)
assertTrue(folder.mobileFocusGifEnabled)
}
} }

View file

@ -46,6 +46,7 @@ internal actual object PlatformLocalAccountDataCleaner {
"trakt_auth_payload", "trakt_auth_payload",
"trakt_library_payload", "trakt_library_payload",
"trakt_settings_payload", "trakt_settings_payload",
"collection_mobile_settings_payload",
"collections_payload", "collections_payload",
) )

View file

@ -0,0 +1,15 @@
package com.nuvio.app.features.collection
import com.nuvio.app.core.storage.ProfileScopedKey
import platform.Foundation.NSUserDefaults
actual object CollectionMobileSettingsStorage {
private const val payloadKey = "collection_mobile_settings_payload"
actual fun loadPayload(): String? =
NSUserDefaults.standardUserDefaults.stringForKey(ProfileScopedKey.of(payloadKey))
actual fun savePayload(payload: String) {
NSUserDefaults.standardUserDefaults.setObject(payload, forKey = ProfileScopedKey.of(payloadKey))
}
}