mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-05-17 15:32:01 +00:00
feat: adding seperate preference key for collections
This commit is contained in:
parent
0ce89650c2
commit
c16711ebb8
16 changed files with 314 additions and 12 deletions
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()),
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
package com.nuvio.app.features.collection
|
||||||
|
|
||||||
|
internal expect object CollectionMobileSettingsStorage {
|
||||||
|
fun loadPayload(): String?
|
||||||
|
fun savePayload(payload: String)
|
||||||
|
}
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue