mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-05-16 23:12:12 +00:00
feat: poster customization
This commit is contained in:
parent
d9695acec9
commit
1a59fc0a20
24 changed files with 544 additions and 21 deletions
|
|
@ -32,6 +32,7 @@ import com.nuvio.app.features.settings.ThemeSettingsStorage
|
|||
import com.nuvio.app.features.trakt.TraktAuthStorage
|
||||
import com.nuvio.app.features.trakt.TraktCommentsStorage
|
||||
import com.nuvio.app.features.tmdb.TmdbSettingsStorage
|
||||
import com.nuvio.app.core.ui.PosterCardStyleStorage
|
||||
import com.nuvio.app.features.watched.WatchedStorage
|
||||
import com.nuvio.app.features.streams.StreamLinkCacheStorage
|
||||
import com.nuvio.app.features.watchprogress.ContinueWatchingEnrichmentStorage
|
||||
|
|
@ -60,6 +61,7 @@ class MainActivity : ComponentActivity() {
|
|||
SearchHistoryStorage.initialize(applicationContext)
|
||||
SeasonViewModeStorage.initialize(applicationContext)
|
||||
ThemeSettingsStorage.initialize(applicationContext)
|
||||
PosterCardStyleStorage.initialize(applicationContext)
|
||||
TmdbSettingsStorage.initialize(applicationContext)
|
||||
MdbListSettingsStorage.initialize(applicationContext)
|
||||
TraktAuthStorage.initialize(applicationContext)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ internal actual object PlatformLocalAccountDataCleaner {
|
|||
"nuvio_player_settings",
|
||||
"nuvio_profile_cache",
|
||||
"nuvio_theme_settings",
|
||||
"nuvio_poster_card_style",
|
||||
"nuvio_mdblist_settings",
|
||||
"nuvio_trakt_auth",
|
||||
"nuvio_watched",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
package com.nuvio.app.core.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import com.nuvio.app.core.storage.ProfileScopedKey
|
||||
|
||||
actual object PosterCardStyleStorage {
|
||||
private const val preferencesName = "nuvio_poster_card_style"
|
||||
private const val payloadKey = "poster_card_style_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()
|
||||
}
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@ import com.nuvio.app.features.settings.ThemeSettingsRepository
|
|||
import com.nuvio.app.features.streams.StreamContextStore
|
||||
import com.nuvio.app.features.streams.StreamsRepository
|
||||
import com.nuvio.app.features.trakt.TraktAuthRepository
|
||||
import com.nuvio.app.core.ui.PosterCardStyleRepository
|
||||
import com.nuvio.app.features.watchprogress.ContinueWatchingPreferencesRepository
|
||||
import com.nuvio.app.features.watchprogress.WatchProgressRepository
|
||||
import com.nuvio.app.features.watched.WatchedRepository
|
||||
|
|
@ -43,6 +44,7 @@ internal object LocalAccountDataCleaner {
|
|||
EpisodeReleaseNotificationsRepository.clearLocalState()
|
||||
CollectionRepository.clearLocalState()
|
||||
ThemeSettingsRepository.clearLocalState()
|
||||
PosterCardStyleRepository.clearLocalState()
|
||||
TraktAuthRepository.clearLocalState()
|
||||
PlayerSettingsRepository.clearLocalState()
|
||||
CatalogRepository.clear()
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ import com.nuvio.app.features.notifications.EpisodeReleaseNotificationsRepositor
|
|||
import com.nuvio.app.features.player.PlayerSettingsStorage
|
||||
import com.nuvio.app.features.player.PlayerSettingsRepository
|
||||
import com.nuvio.app.features.profiles.ProfileRepository
|
||||
import com.nuvio.app.core.ui.PosterCardStyleRepository
|
||||
import com.nuvio.app.core.ui.PosterCardStyleStorage
|
||||
import com.nuvio.app.features.settings.ThemeSettingsStorage
|
||||
import com.nuvio.app.features.settings.ThemeSettingsRepository
|
||||
import com.nuvio.app.features.tmdb.TmdbSettingsStorage
|
||||
|
|
@ -148,6 +150,7 @@ object ProfileSettingsSync {
|
|||
val signatureFlows = listOf(
|
||||
ThemeSettingsRepository.selectedTheme.map { "theme" },
|
||||
ThemeSettingsRepository.amoledEnabled.map { "amoled" },
|
||||
PosterCardStyleRepository.uiState.map { "poster_card_style" },
|
||||
PlayerSettingsRepository.uiState.map { "player" },
|
||||
TmdbSettingsRepository.uiState.map { "tmdb" },
|
||||
MdbListSettingsRepository.uiState.map { "mdblist" },
|
||||
|
|
@ -190,6 +193,7 @@ object ProfileSettingsSync {
|
|||
return MobileProfileSettingsBlob(
|
||||
features = MobileProfileSettingsFeatures(
|
||||
themeSettings = ThemeSettingsStorage.exportToSyncPayload(),
|
||||
posterCardStyleSettingsPayload = PosterCardStyleStorage.loadPayload().orEmpty().trim(),
|
||||
playerSettings = PlayerSettingsStorage.exportToSyncPayload(),
|
||||
tmdbSettings = TmdbSettingsStorage.exportToSyncPayload(),
|
||||
mdbListSettings = MdbListSettingsStorage.exportToSyncPayload(),
|
||||
|
|
@ -207,6 +211,9 @@ object ProfileSettingsSync {
|
|||
ThemeSettingsStorage.replaceFromSyncPayload(blob.features.themeSettings)
|
||||
ThemeSettingsRepository.onProfileChanged()
|
||||
|
||||
PosterCardStyleStorage.savePayload(blob.features.posterCardStyleSettingsPayload)
|
||||
PosterCardStyleRepository.onProfileChanged()
|
||||
|
||||
PlayerSettingsStorage.replaceFromSyncPayload(blob.features.playerSettings)
|
||||
PlayerSettingsRepository.onProfileChanged()
|
||||
|
||||
|
|
@ -231,6 +238,7 @@ object ProfileSettingsSync {
|
|||
|
||||
private fun ensureRepositoriesLoaded() {
|
||||
ThemeSettingsRepository.ensureLoaded()
|
||||
PosterCardStyleRepository.ensureLoaded()
|
||||
PlayerSettingsRepository.ensureLoaded()
|
||||
TmdbSettingsRepository.ensureLoaded()
|
||||
MdbListSettingsRepository.ensureLoaded()
|
||||
|
|
@ -249,6 +257,7 @@ object ProfileSettingsSync {
|
|||
private fun currentObservedStateSignature(): String = listOf(
|
||||
"theme=${ThemeSettingsRepository.selectedTheme.value.name}",
|
||||
"amoled=${ThemeSettingsRepository.amoledEnabled.value}",
|
||||
"poster_card_style=${PosterCardStyleRepository.uiState.value}",
|
||||
"player=${PlayerSettingsRepository.uiState.value}",
|
||||
"tmdb=${TmdbSettingsRepository.uiState.value}",
|
||||
"mdblist=${MdbListSettingsRepository.uiState.value}",
|
||||
|
|
@ -268,6 +277,7 @@ private data class MobileProfileSettingsBlob(
|
|||
@Serializable
|
||||
private data class MobileProfileSettingsFeatures(
|
||||
@SerialName("theme_settings") val themeSettings: JsonObject = JsonObject(emptyMap()),
|
||||
@SerialName("poster_card_style_settings_payload") val posterCardStyleSettingsPayload: String = "",
|
||||
@SerialName("player_settings") val playerSettings: JsonObject = JsonObject(emptyMap()),
|
||||
@SerialName("tmdb_settings") val tmdbSettings: JsonObject = JsonObject(emptyMap()),
|
||||
@SerialName("mdblist_settings") val mdbListSettings: JsonObject = JsonObject(emptyMap()),
|
||||
|
|
|
|||
|
|
@ -103,6 +103,8 @@ fun NuvioContinueWatchingActionSheet(
|
|||
private fun ContinueWatchingSheetHeader(
|
||||
item: ContinueWatchingItem,
|
||||
) {
|
||||
val posterCardStyle = rememberPosterCardStyleUiState()
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
|
|
@ -113,7 +115,7 @@ private fun ContinueWatchingSheetHeader(
|
|||
Box(
|
||||
modifier = Modifier
|
||||
.size(width = 64.dp, height = 92.dp)
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.clip(RoundedCornerShape(posterCardStyle.cornerRadiusDp.dp))
|
||||
.background(MaterialTheme.colorScheme.surfaceVariant),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -140,6 +140,8 @@ fun NuvioAnimatedWatchedBadge(
|
|||
private fun PosterSheetHeader(
|
||||
item: MetaPreview,
|
||||
) {
|
||||
val posterCardStyle = rememberPosterCardStyleUiState()
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
|
|
@ -150,7 +152,7 @@ private fun PosterSheetHeader(
|
|||
Box(
|
||||
modifier = Modifier
|
||||
.size(width = 64.dp, height = 92.dp)
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.clip(RoundedCornerShape(posterCardStyle.cornerRadiusDp.dp))
|
||||
.background(MaterialTheme.colorScheme.surfaceVariant),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -102,15 +102,19 @@ fun NuvioPosterCard(
|
|||
onClick: (() -> Unit)? = null,
|
||||
onLongClick: (() -> Unit)? = null,
|
||||
) {
|
||||
val posterCardStyle = rememberPosterCardStyleUiState()
|
||||
val cardWidth = shape.cardWidth(basePosterWidthDp = posterCardStyle.widthDp)
|
||||
val cardShape = RoundedCornerShape(posterCardStyle.cornerRadiusDp.dp)
|
||||
|
||||
Column(
|
||||
modifier = modifier.width(shape.cardWidth),
|
||||
modifier = modifier.width(cardWidth),
|
||||
verticalArrangement = Arrangement.spacedBy(6.dp),
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(shape.aspectRatio)
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.clip(cardShape)
|
||||
.background(MaterialTheme.colorScheme.surface)
|
||||
.posterCardClickable(onClick = onClick, onLongClick = onLongClick),
|
||||
contentAlignment = Alignment.Center,
|
||||
|
|
@ -254,11 +258,13 @@ private val NuvioPosterShape.aspectRatio: Float
|
|||
NuvioPosterShape.Landscape -> 1.77f
|
||||
}
|
||||
|
||||
private val NuvioPosterShape.cardWidth: Dp
|
||||
get() = when (this) {
|
||||
NuvioPosterShape.Poster -> 110.dp
|
||||
NuvioPosterShape.Square -> 110.dp
|
||||
NuvioPosterShape.Landscape -> 180.dp
|
||||
private const val LandscapeWidthScale = 180f / 110f
|
||||
|
||||
private fun NuvioPosterShape.cardWidth(basePosterWidthDp: Int): Dp =
|
||||
when (this) {
|
||||
NuvioPosterShape.Poster -> basePosterWidthDp.dp
|
||||
NuvioPosterShape.Square -> basePosterWidthDp.dp
|
||||
NuvioPosterShape.Landscape -> (basePosterWidthDp * LandscapeWidthScale).dp
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
package com.nuvio.app.core.ui
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.collectAsState
|
||||
|
||||
@Composable
|
||||
internal fun rememberPosterCardStyleUiState(): PosterCardStyleUiState {
|
||||
PosterCardStyleRepository.ensureLoaded()
|
||||
val uiState by PosterCardStyleRepository.uiState.collectAsState()
|
||||
return uiState
|
||||
}
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
package com.nuvio.app.core.ui
|
||||
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
internal const val DefaultPosterCardWidthDp = 126
|
||||
internal const val DefaultPosterCardHeightDp = 189
|
||||
internal const val DefaultPosterCardCornerRadiusDp = 12
|
||||
|
||||
@Serializable
|
||||
private data class StoredPosterCardStylePreferences(
|
||||
val widthDp: Int = DefaultPosterCardWidthDp,
|
||||
val heightDp: Int = DefaultPosterCardHeightDp,
|
||||
val cornerRadiusDp: Int = DefaultPosterCardCornerRadiusDp,
|
||||
)
|
||||
|
||||
data class PosterCardStyleUiState(
|
||||
val widthDp: Int = DefaultPosterCardWidthDp,
|
||||
val heightDp: Int = DefaultPosterCardHeightDp,
|
||||
val cornerRadiusDp: Int = DefaultPosterCardCornerRadiusDp,
|
||||
)
|
||||
|
||||
object PosterCardStyleRepository {
|
||||
private val json = Json {
|
||||
ignoreUnknownKeys = true
|
||||
encodeDefaults = true
|
||||
}
|
||||
|
||||
private val _uiState = MutableStateFlow(PosterCardStyleUiState())
|
||||
val uiState: StateFlow<PosterCardStyleUiState> = _uiState.asStateFlow()
|
||||
|
||||
private var hasLoaded = false
|
||||
|
||||
fun ensureLoaded() {
|
||||
if (hasLoaded) return
|
||||
loadFromDisk()
|
||||
}
|
||||
|
||||
fun onProfileChanged() {
|
||||
loadFromDisk()
|
||||
}
|
||||
|
||||
fun clearLocalState() {
|
||||
hasLoaded = false
|
||||
_uiState.value = PosterCardStyleUiState()
|
||||
}
|
||||
|
||||
fun setWidthDp(widthDp: Int) {
|
||||
ensureLoaded()
|
||||
val nextWidth = widthDp
|
||||
val nextHeight = (nextWidth * 3) / 2
|
||||
if (_uiState.value.widthDp == nextWidth && _uiState.value.heightDp == nextHeight) return
|
||||
_uiState.value = _uiState.value.copy(
|
||||
widthDp = nextWidth,
|
||||
heightDp = nextHeight,
|
||||
)
|
||||
persist()
|
||||
}
|
||||
|
||||
fun setCornerRadiusDp(cornerRadiusDp: Int) {
|
||||
ensureLoaded()
|
||||
if (_uiState.value.cornerRadiusDp == cornerRadiusDp) return
|
||||
_uiState.value = _uiState.value.copy(cornerRadiusDp = cornerRadiusDp)
|
||||
persist()
|
||||
}
|
||||
|
||||
fun resetToDefaults() {
|
||||
ensureLoaded()
|
||||
if (_uiState.value == PosterCardStyleUiState()) return
|
||||
_uiState.value = PosterCardStyleUiState()
|
||||
persist()
|
||||
}
|
||||
|
||||
private fun loadFromDisk() {
|
||||
hasLoaded = true
|
||||
|
||||
val payload = PosterCardStyleStorage.loadPayload().orEmpty().trim()
|
||||
if (payload.isEmpty()) {
|
||||
_uiState.value = PosterCardStyleUiState()
|
||||
return
|
||||
}
|
||||
|
||||
val stored = runCatching {
|
||||
json.decodeFromString<StoredPosterCardStylePreferences>(payload)
|
||||
}.getOrNull()
|
||||
|
||||
_uiState.value = if (stored != null) {
|
||||
val widthDp = stored.widthDp.takeIf { it > 0 } ?: DefaultPosterCardWidthDp
|
||||
val heightDp = stored.heightDp.takeIf { it > 0 } ?: ((widthDp * 3) / 2)
|
||||
val cornerRadiusDp = stored.cornerRadiusDp.coerceAtLeast(0)
|
||||
PosterCardStyleUiState(
|
||||
widthDp = widthDp,
|
||||
heightDp = heightDp,
|
||||
cornerRadiusDp = cornerRadiusDp,
|
||||
)
|
||||
} else {
|
||||
PosterCardStyleUiState()
|
||||
}
|
||||
}
|
||||
|
||||
private fun persist() {
|
||||
PosterCardStyleStorage.savePayload(
|
||||
json.encodeToString(
|
||||
StoredPosterCardStylePreferences(
|
||||
widthDp = _uiState.value.widthDp,
|
||||
heightDp = _uiState.value.heightDp,
|
||||
cornerRadiusDp = _uiState.value.cornerRadiusDp,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.nuvio.app.core.ui
|
||||
|
||||
internal expect object PosterCardStyleStorage {
|
||||
fun loadPayload(): String?
|
||||
fun savePayload(payload: String)
|
||||
}
|
||||
|
|
@ -47,6 +47,7 @@ import com.nuvio.app.core.ui.NuvioNetworkOfflineCard
|
|||
import coil3.compose.AsyncImage
|
||||
import com.nuvio.app.core.format.formatReleaseDateForDisplay
|
||||
import com.nuvio.app.core.ui.NuvioBackButton
|
||||
import com.nuvio.app.core.ui.rememberPosterCardStyleUiState
|
||||
import com.nuvio.app.core.ui.posterCardClickable
|
||||
import com.nuvio.app.core.ui.nuvioPlatformExtraBottomPadding
|
||||
import com.nuvio.app.features.home.MetaPreview
|
||||
|
|
@ -70,6 +71,7 @@ fun CatalogScreen(
|
|||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val uiState by CatalogRepository.uiState.collectAsStateWithLifecycle()
|
||||
val posterCardStyle = rememberPosterCardStyleUiState()
|
||||
val networkStatusUiState by NetworkStatusRepository.uiState.collectAsStateWithLifecycle()
|
||||
val gridState = rememberLazyGridState()
|
||||
var headerHeightPx by remember { mutableIntStateOf(0) }
|
||||
|
|
@ -148,7 +150,9 @@ fun CatalogScreen(
|
|||
verticalArrangement = Arrangement.spacedBy(18.dp),
|
||||
) {
|
||||
if (uiState.items.isEmpty() && uiState.isLoading) {
|
||||
items(columns * 3) { CatalogSkeletonTile() }
|
||||
items(columns * 3) {
|
||||
CatalogSkeletonTile(cornerRadiusDp = posterCardStyle.cornerRadiusDp)
|
||||
}
|
||||
} else if (uiState.items.isEmpty()) {
|
||||
item(span = { GridItemSpan(maxLineSpan) }) {
|
||||
CatalogEmptyState(
|
||||
|
|
@ -174,6 +178,7 @@ fun CatalogScreen(
|
|||
) { item ->
|
||||
CatalogPosterTile(
|
||||
item = item,
|
||||
cornerRadiusDp = posterCardStyle.cornerRadiusDp,
|
||||
onClick = onPosterClick?.let { { it(item) } },
|
||||
)
|
||||
}
|
||||
|
|
@ -242,6 +247,7 @@ private fun CatalogHeader(
|
|||
@Composable
|
||||
private fun CatalogPosterTile(
|
||||
item: MetaPreview,
|
||||
cornerRadiusDp: Int,
|
||||
onClick: (() -> Unit)? = null,
|
||||
) {
|
||||
Column(
|
||||
|
|
@ -251,7 +257,7 @@ private fun CatalogPosterTile(
|
|||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(item.posterShape.catalogAspectRatio())
|
||||
.clip(RoundedCornerShape(22.dp))
|
||||
.clip(RoundedCornerShape(cornerRadiusDp.dp))
|
||||
.background(MaterialTheme.colorScheme.surface)
|
||||
.posterCardClickable(onClick = onClick, onLongClick = null),
|
||||
) {
|
||||
|
|
@ -287,12 +293,12 @@ private fun CatalogPosterTile(
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun CatalogSkeletonTile() {
|
||||
private fun CatalogSkeletonTile(cornerRadiusDp: Int) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(0.68f)
|
||||
.clip(RoundedCornerShape(22.dp))
|
||||
.clip(RoundedCornerShape(cornerRadiusDp.dp))
|
||||
.background(MaterialTheme.colorScheme.surface),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ import androidx.compose.ui.unit.sp
|
|||
import coil3.compose.AsyncImage
|
||||
import coil3.compose.LocalPlatformContext
|
||||
import coil3.request.ImageRequest
|
||||
import com.nuvio.app.core.ui.rememberPosterCardStyleUiState
|
||||
import com.nuvio.app.features.details.components.DetailPosterRailSection
|
||||
import com.nuvio.app.features.home.MetaPreview
|
||||
import com.nuvio.app.features.tmdb.TmdbMetadataService
|
||||
|
|
@ -155,6 +156,7 @@ private fun PersonDetailContent(
|
|||
sharedTransitionScope: SharedTransitionScope? = null,
|
||||
animatedVisibilityScope: AnimatedVisibilityScope? = null,
|
||||
) {
|
||||
val posterCardStyle = rememberPosterCardStyleUiState()
|
||||
val accentColor = MaterialTheme.colorScheme.primary
|
||||
|
||||
val allCredits = remember(person.movieCredits, person.tvCredits) {
|
||||
|
|
@ -445,6 +447,7 @@ private fun PersonDetailSkeleton(
|
|||
sharedTransitionScope: SharedTransitionScope? = null,
|
||||
animatedVisibilityScope: AnimatedVisibilityScope? = null,
|
||||
) {
|
||||
val posterCardStyle = rememberPosterCardStyleUiState()
|
||||
val accentColor = MaterialTheme.colorScheme.primary
|
||||
val avatarCacheKey = avatarTransitionKey
|
||||
val platformContext = LocalPlatformContext.current
|
||||
|
|
@ -604,7 +607,7 @@ private fun PersonDetailSkeleton(
|
|||
modifier = Modifier
|
||||
.width(110.dp)
|
||||
.height(163.dp)
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.clip(RoundedCornerShape(posterCardStyle.cornerRadiusDp.dp))
|
||||
.background(MaterialTheme.colorScheme.surfaceVariant),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(6.dp))
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import coil3.compose.AsyncImage
|
||||
import com.nuvio.app.core.ui.rememberPosterCardStyleUiState
|
||||
import com.nuvio.app.features.details.components.DetailPosterRailSection
|
||||
import com.nuvio.app.features.home.MetaPreview
|
||||
import com.nuvio.app.features.tmdb.TmdbEntityBrowseData
|
||||
|
|
@ -297,6 +298,8 @@ private fun EntityHeroSection(
|
|||
|
||||
@Composable
|
||||
private fun EntityBrowseSkeleton() {
|
||||
val posterCardStyle = rememberPosterCardStyleUiState()
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
|
|
@ -354,7 +357,7 @@ private fun EntityBrowseSkeleton() {
|
|||
modifier = Modifier
|
||||
.width(110.dp)
|
||||
.height(163.dp)
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.clip(RoundedCornerShape(posterCardStyle.cornerRadiusDp.dp))
|
||||
.background(MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.3f)),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import androidx.compose.ui.unit.dp
|
|||
import androidx.compose.ui.unit.sp
|
||||
import com.nuvio.app.core.ui.NuvioShelfSection
|
||||
import com.nuvio.app.core.ui.posterCardClickable
|
||||
import com.nuvio.app.core.ui.rememberPosterCardStyleUiState
|
||||
import com.nuvio.app.features.collection.Collection
|
||||
import com.nuvio.app.features.collection.CollectionFolder
|
||||
import com.nuvio.app.features.home.PosterShape
|
||||
|
|
@ -86,6 +87,7 @@ private fun CollectionFolderCard(
|
|||
modifier: Modifier = Modifier,
|
||||
onClick: (() -> Unit)? = null,
|
||||
) {
|
||||
val posterCardStyle = rememberPosterCardStyleUiState()
|
||||
val shape = folder.posterShape
|
||||
val cardWidth: Dp
|
||||
val aspectRatio: Float
|
||||
|
|
@ -109,7 +111,7 @@ private fun CollectionFolderCard(
|
|||
modifier = modifier.width(cardWidth),
|
||||
verticalArrangement = Arrangement.spacedBy(6.dp),
|
||||
) {
|
||||
val shapeCorner = RoundedCornerShape(16.dp)
|
||||
val shapeCorner = RoundedCornerShape(posterCardStyle.cornerRadiusDp.dp)
|
||||
val imageUrl = collectionFolderCardImageUrl(folder)
|
||||
Card(
|
||||
modifier = Modifier
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import androidx.compose.ui.geometry.Offset
|
|||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.nuvio.app.core.ui.rememberPosterCardStyleUiState
|
||||
|
||||
@Composable
|
||||
private fun rememberHomeSkeletonBrush(): Brush {
|
||||
|
|
@ -180,6 +181,7 @@ fun HomeSkeletonHero(
|
|||
@Composable
|
||||
fun HomeSkeletonRow(modifier: Modifier = Modifier) {
|
||||
val brush = rememberHomeSkeletonBrush()
|
||||
val posterCardStyle = rememberPosterCardStyleUiState()
|
||||
|
||||
Column(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
|
|
@ -209,9 +211,9 @@ fun HomeSkeletonRow(modifier: Modifier = Modifier) {
|
|||
repeat(4) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.width(110.dp)
|
||||
.height(163.dp)
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.width(posterCardStyle.widthDp.dp)
|
||||
.height(posterCardStyle.heightDp.dp)
|
||||
.clip(RoundedCornerShape(posterCardStyle.cornerRadiusDp.dp))
|
||||
.background(brush),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import com.nuvio.app.features.collection.CollectionRepository
|
|||
import com.nuvio.app.features.downloads.DownloadsRepository
|
||||
import com.nuvio.app.features.details.MetaScreenSettingsRepository
|
||||
import com.nuvio.app.features.home.HomeCatalogSettingsRepository
|
||||
import com.nuvio.app.core.ui.PosterCardStyleRepository
|
||||
import com.nuvio.app.features.library.LibraryRepository
|
||||
import com.nuvio.app.features.mdblist.MdbListSettingsRepository
|
||||
import com.nuvio.app.features.notifications.EpisodeReleaseNotificationsRepository
|
||||
|
|
@ -138,6 +139,7 @@ object ProfileRepository {
|
|||
PluginRepository.onProfileChanged(profileIndex)
|
||||
}
|
||||
ThemeSettingsRepository.onProfileChanged()
|
||||
PosterCardStyleRepository.onProfileChanged()
|
||||
PlayerSettingsRepository.onProfileChanged()
|
||||
HomeCatalogSettingsRepository.onProfileChanged()
|
||||
MetaScreenSettingsRepository.onProfileChanged()
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ import com.nuvio.app.core.ui.NuvioBottomSheetDivider
|
|||
import com.nuvio.app.core.ui.NuvioModalBottomSheet
|
||||
import com.nuvio.app.core.ui.dismissNuvioBottomSheet
|
||||
import com.nuvio.app.core.ui.nuvioPlatformExtraBottomPadding
|
||||
import com.nuvio.app.core.ui.rememberPosterCardStyleUiState
|
||||
import com.nuvio.app.core.ui.posterCardClickable
|
||||
import com.nuvio.app.features.home.MetaPreview
|
||||
import com.nuvio.app.features.home.PosterShape
|
||||
|
|
@ -338,6 +339,8 @@ private fun DiscoverGridRow(
|
|||
onPosterClick: ((MetaPreview) -> Unit)? = null,
|
||||
onPosterLongClick: ((MetaPreview) -> Unit)? = null,
|
||||
) {
|
||||
val posterCardStyle = rememberPosterCardStyleUiState()
|
||||
|
||||
Row(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
|
|
@ -346,6 +349,7 @@ private fun DiscoverGridRow(
|
|||
items.forEach { item ->
|
||||
DiscoverPosterTile(
|
||||
item = item,
|
||||
cornerRadiusDp = posterCardStyle.cornerRadiusDp,
|
||||
modifier = Modifier.weight(1f),
|
||||
isWatched = WatchingState.isPosterWatched(
|
||||
watchedKeys = watchedKeys,
|
||||
|
|
@ -365,6 +369,7 @@ private fun DiscoverGridRow(
|
|||
@Composable
|
||||
private fun DiscoverPosterTile(
|
||||
item: MetaPreview,
|
||||
cornerRadiusDp: Int,
|
||||
modifier: Modifier = Modifier,
|
||||
isWatched: Boolean = false,
|
||||
onClick: (() -> Unit)? = null,
|
||||
|
|
@ -378,7 +383,7 @@ private fun DiscoverPosterTile(
|
|||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(item.posterShape.discoverAspectRatio())
|
||||
.clip(RoundedCornerShape(22.dp))
|
||||
.clip(RoundedCornerShape(cornerRadiusDp.dp))
|
||||
.background(MaterialTheme.colorScheme.surface)
|
||||
.posterCardClickable(onClick = onClick, onLongClick = onLongClick),
|
||||
) {
|
||||
|
|
@ -424,6 +429,8 @@ private fun DiscoverSkeletonRow(
|
|||
columns: Int,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val posterCardStyle = rememberPosterCardStyleUiState()
|
||||
|
||||
Row(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
|
|
@ -433,7 +440,7 @@ private fun DiscoverSkeletonRow(
|
|||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.aspectRatio(0.68f)
|
||||
.clip(RoundedCornerShape(22.dp))
|
||||
.clip(RoundedCornerShape(posterCardStyle.cornerRadiusDp.dp))
|
||||
.background(MaterialTheme.colorScheme.surface),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
|||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Check
|
||||
import androidx.compose.material.icons.rounded.Style
|
||||
import androidx.compose.material.icons.rounded.Tune
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
|
|
@ -41,6 +42,7 @@ internal fun LazyListScope.appearanceSettingsContent(
|
|||
amoledEnabled: Boolean,
|
||||
onAmoledToggle: (Boolean) -> Unit,
|
||||
onContinueWatchingClick: () -> Unit,
|
||||
onPosterCustomizationClick: () -> Unit,
|
||||
) {
|
||||
item {
|
||||
SettingsSection(
|
||||
|
|
@ -101,6 +103,14 @@ internal fun LazyListScope.appearanceSettingsContent(
|
|||
isTablet = isTablet,
|
||||
onClick = onContinueWatchingClick,
|
||||
)
|
||||
SettingsGroupDivider(isTablet = isTablet)
|
||||
SettingsNavigationRow(
|
||||
title = "Poster Customization",
|
||||
description = "Adjust shared poster card width and corner radius presets.",
|
||||
icon = Icons.Rounded.Tune,
|
||||
isTablet = isTablet,
|
||||
onClick = onPosterCustomizationClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,261 @@
|
|||
package com.nuvio.app.features.settings
|
||||
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.FilterChip
|
||||
import androidx.compose.material3.FilterChipDefaults
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.nuvio.app.core.ui.NuvioActionLabel
|
||||
import com.nuvio.app.core.ui.PosterCardStyleRepository
|
||||
import com.nuvio.app.core.ui.PosterCardStyleUiState
|
||||
|
||||
internal fun LazyListScope.posterCustomizationSettingsContent(
|
||||
isTablet: Boolean,
|
||||
uiState: PosterCardStyleUiState,
|
||||
) {
|
||||
item {
|
||||
SettingsSection(
|
||||
title = "POSTER CARD STYLE",
|
||||
isTablet = isTablet,
|
||||
actions = {
|
||||
NuvioActionLabel(
|
||||
text = "Reset",
|
||||
onClick = PosterCardStyleRepository::resetToDefaults,
|
||||
)
|
||||
},
|
||||
) {
|
||||
SettingsGroup(isTablet = isTablet) {
|
||||
PosterCardStyleControls(
|
||||
isTablet = isTablet,
|
||||
widthDp = uiState.widthDp,
|
||||
cornerRadiusDp = uiState.cornerRadiusDp,
|
||||
onWidthSelected = PosterCardStyleRepository::setWidthDp,
|
||||
onCornerRadiusSelected = PosterCardStyleRepository::setCornerRadiusDp,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
private fun PosterCardStyleControls(
|
||||
isTablet: Boolean,
|
||||
widthDp: Int,
|
||||
cornerRadiusDp: Int,
|
||||
onWidthSelected: (Int) -> Unit,
|
||||
onCornerRadiusSelected: (Int) -> Unit,
|
||||
) {
|
||||
val widthOptions = listOf(
|
||||
PresetOption("Compact", 104),
|
||||
PresetOption("Dense", 112),
|
||||
PresetOption("Standard", 120),
|
||||
PresetOption("Balanced", 126),
|
||||
PresetOption("Comfort", 134),
|
||||
PresetOption("Large", 140),
|
||||
)
|
||||
val radiusOptions = listOf(
|
||||
PresetOption("Sharp", 0),
|
||||
PresetOption("Subtle", 4),
|
||||
PresetOption("Classic", 8),
|
||||
PresetOption("Rounded", 12),
|
||||
PresetOption("Pill", 16),
|
||||
)
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = if (isTablet) 20.dp else 16.dp, vertical = if (isTablet) 18.dp else 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(14.dp),
|
||||
) {
|
||||
Text(
|
||||
text = "Customize card width and corner radius for shared poster cards across the app.",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
PosterCardLivePreview(
|
||||
widthDp = widthDp,
|
||||
cornerRadiusDp = cornerRadiusDp,
|
||||
)
|
||||
PosterStyleOptionRow(
|
||||
title = "Card Width",
|
||||
selectedValue = widthDp,
|
||||
options = widthOptions,
|
||||
onSelected = onWidthSelected,
|
||||
)
|
||||
PosterStyleOptionRow(
|
||||
title = "Card Radius",
|
||||
selectedValue = cornerRadiusDp,
|
||||
options = radiusOptions,
|
||||
onSelected = onCornerRadiusSelected,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PosterCardLivePreview(
|
||||
widthDp: Int,
|
||||
cornerRadiusDp: Int,
|
||||
) {
|
||||
val targetHeightDp = (widthDp * 3) / 2
|
||||
val previewFrameWidthDp = 140
|
||||
val previewFrameHeightDp = 210
|
||||
val animatedWidth = animateDpAsState(
|
||||
targetValue = widthDp.dp,
|
||||
animationSpec = tween(durationMillis = 280),
|
||||
label = "posterPreviewWidth",
|
||||
)
|
||||
val animatedHeight = animateDpAsState(
|
||||
targetValue = targetHeightDp.dp,
|
||||
animationSpec = tween(durationMillis = 280),
|
||||
label = "posterPreviewHeight",
|
||||
)
|
||||
val animatedCornerRadius = animateDpAsState(
|
||||
targetValue = cornerRadiusDp.dp,
|
||||
animationSpec = tween(durationMillis = 220),
|
||||
label = "posterPreviewCornerRadius",
|
||||
)
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
Text(
|
||||
text = "Live Preview",
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
)
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(14.dp),
|
||||
verticalAlignment = Alignment.Top,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.width(previewFrameWidthDp.dp)
|
||||
.height(previewFrameHeightDp.dp),
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.width(animatedWidth.value)
|
||||
.height(animatedHeight.value)
|
||||
.clip(RoundedCornerShape(animatedCornerRadius.value))
|
||||
.background(MaterialTheme.colorScheme.surfaceVariant),
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopStart)
|
||||
.padding(10.dp)
|
||||
.size(34.dp)
|
||||
.clip(RoundedCornerShape(999.dp))
|
||||
.background(MaterialTheme.colorScheme.primary.copy(alpha = 0.2f)),
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomStart)
|
||||
.padding(10.dp)
|
||||
.width(70.dp)
|
||||
.height(7.dp)
|
||||
.clip(RoundedCornerShape(999.dp))
|
||||
.background(MaterialTheme.colorScheme.onSurface.copy(alpha = 0.22f)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
Text(
|
||||
text = "Width: ${widthDp}dp",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
Text(
|
||||
text = "Corner radius: ${cornerRadiusDp}dp",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
Text(
|
||||
text = "Height: ${targetHeightDp}dp",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(1.dp)
|
||||
.background(MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.5f)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
private fun PosterStyleOptionRow(
|
||||
title: String,
|
||||
selectedValue: Int,
|
||||
options: List<PresetOption>,
|
||||
onSelected: (Int) -> Unit,
|
||||
) {
|
||||
val selectedLabel = options.firstOrNull { it.value == selectedValue }?.label ?: "Custom"
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
Text(
|
||||
text = "$title ($selectedLabel)",
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
)
|
||||
|
||||
FlowRow(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
options.forEach { option ->
|
||||
FilterChip(
|
||||
selected = option.value == selectedValue,
|
||||
onClick = { onSelected(option.value) },
|
||||
label = { Text(option.label) },
|
||||
colors = FilterChipDefaults.filterChipColors(
|
||||
selectedContainerColor = MaterialTheme.colorScheme.primaryContainer,
|
||||
selectedLabelColor = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private data class PresetOption(
|
||||
val label: String,
|
||||
val value: Int,
|
||||
)
|
||||
|
|
@ -49,6 +49,11 @@ internal enum class SettingsPage(
|
|||
category = SettingsCategory.General,
|
||||
parentPage = Appearance,
|
||||
),
|
||||
PosterCustomization(
|
||||
title = "Poster Customization",
|
||||
category = SettingsCategory.General,
|
||||
parentPage = Appearance,
|
||||
),
|
||||
ContentDiscovery(
|
||||
title = "Content & Discovery",
|
||||
category = SettingsCategory.General,
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ import com.nuvio.app.core.ui.PlatformBackHandler
|
|||
import com.nuvio.app.features.addons.AddonRepository
|
||||
import com.nuvio.app.features.details.MetaScreenSettingsRepository
|
||||
import com.nuvio.app.features.details.MetaScreenSettingsUiState
|
||||
import com.nuvio.app.core.ui.PosterCardStyleRepository
|
||||
import com.nuvio.app.core.ui.PosterCardStyleUiState
|
||||
import com.nuvio.app.features.home.HomeCatalogSettingsItem
|
||||
import com.nuvio.app.features.home.HomeCatalogSettingsRepository
|
||||
import com.nuvio.app.features.mdblist.MdbListSettings
|
||||
|
|
@ -129,6 +131,10 @@ fun SettingsScreen(
|
|||
ContinueWatchingPreferencesRepository.ensureLoaded()
|
||||
ContinueWatchingPreferencesRepository.uiState
|
||||
}.collectAsStateWithLifecycle()
|
||||
val posterCardStyleUiState by remember {
|
||||
PosterCardStyleRepository.ensureLoaded()
|
||||
PosterCardStyleRepository.uiState
|
||||
}.collectAsStateWithLifecycle()
|
||||
val episodeReleaseNotificationsUiState by remember {
|
||||
EpisodeReleaseNotificationsRepository.ensureLoaded()
|
||||
EpisodeReleaseNotificationsRepository.uiState
|
||||
|
|
@ -179,6 +185,7 @@ fun SettingsScreen(
|
|||
homescreenItems = homescreenSettingsUiState.items,
|
||||
metaScreenSettingsUiState = metaScreenSettingsUiState,
|
||||
continueWatchingPreferencesUiState = continueWatchingPreferencesUiState,
|
||||
posterCardStyleUiState = posterCardStyleUiState,
|
||||
onSwitchProfile = onSwitchProfile,
|
||||
onDownloadsClick = onDownloadsClick,
|
||||
onCollectionsClick = onCollectionsClick,
|
||||
|
|
@ -214,6 +221,7 @@ fun SettingsScreen(
|
|||
homescreenItems = homescreenSettingsUiState.items,
|
||||
metaScreenSettingsUiState = metaScreenSettingsUiState,
|
||||
continueWatchingPreferencesUiState = continueWatchingPreferencesUiState,
|
||||
posterCardStyleUiState = posterCardStyleUiState,
|
||||
onSwitchProfile = onSwitchProfile,
|
||||
onHomescreenClick = onHomescreenClick,
|
||||
onMetaScreenClick = onMetaScreenClick,
|
||||
|
|
@ -259,6 +267,7 @@ private fun MobileSettingsScreen(
|
|||
homescreenItems: List<HomeCatalogSettingsItem>,
|
||||
metaScreenSettingsUiState: MetaScreenSettingsUiState,
|
||||
continueWatchingPreferencesUiState: ContinueWatchingPreferencesUiState,
|
||||
posterCardStyleUiState: PosterCardStyleUiState,
|
||||
onSwitchProfile: (() -> Unit)? = null,
|
||||
onHomescreenClick: () -> Unit = {},
|
||||
onMetaScreenClick: () -> Unit = {},
|
||||
|
|
@ -318,6 +327,7 @@ private fun MobileSettingsScreen(
|
|||
amoledEnabled = amoledEnabled,
|
||||
onAmoledToggle = onAmoledToggle,
|
||||
onContinueWatchingClick = onContinueWatchingClick,
|
||||
onPosterCustomizationClick = { onPageChange(SettingsPage.PosterCustomization) },
|
||||
)
|
||||
SettingsPage.Notifications -> notificationsSettingsContent(
|
||||
isTablet = false,
|
||||
|
|
@ -330,6 +340,10 @@ private fun MobileSettingsScreen(
|
|||
upNextFromFurthestEpisode = continueWatchingPreferencesUiState.upNextFromFurthestEpisode,
|
||||
showResumePromptOnLaunch = continueWatchingPreferencesUiState.showResumePromptOnLaunch,
|
||||
)
|
||||
SettingsPage.PosterCustomization -> posterCustomizationSettingsContent(
|
||||
isTablet = false,
|
||||
uiState = posterCardStyleUiState,
|
||||
)
|
||||
SettingsPage.ContentDiscovery -> contentDiscoveryContent(
|
||||
isTablet = false,
|
||||
showPluginsEntry = AppFeaturePolicy.pluginsEnabled,
|
||||
|
|
@ -404,6 +418,7 @@ private fun TabletSettingsScreen(
|
|||
homescreenItems: List<HomeCatalogSettingsItem>,
|
||||
metaScreenSettingsUiState: MetaScreenSettingsUiState,
|
||||
continueWatchingPreferencesUiState: ContinueWatchingPreferencesUiState,
|
||||
posterCardStyleUiState: PosterCardStyleUiState,
|
||||
onSwitchProfile: (() -> Unit)? = null,
|
||||
onDownloadsClick: () -> Unit = {},
|
||||
onCollectionsClick: () -> Unit = {},
|
||||
|
|
@ -526,6 +541,7 @@ private fun TabletSettingsScreen(
|
|||
amoledEnabled = amoledEnabled,
|
||||
onAmoledToggle = onAmoledToggle,
|
||||
onContinueWatchingClick = { openInlinePage(SettingsPage.ContinueWatching) },
|
||||
onPosterCustomizationClick = { openInlinePage(SettingsPage.PosterCustomization) },
|
||||
)
|
||||
SettingsPage.Notifications -> notificationsSettingsContent(
|
||||
isTablet = true,
|
||||
|
|
@ -538,6 +554,10 @@ private fun TabletSettingsScreen(
|
|||
upNextFromFurthestEpisode = continueWatchingPreferencesUiState.upNextFromFurthestEpisode,
|
||||
showResumePromptOnLaunch = continueWatchingPreferencesUiState.showResumePromptOnLaunch,
|
||||
)
|
||||
SettingsPage.PosterCustomization -> posterCustomizationSettingsContent(
|
||||
isTablet = true,
|
||||
uiState = posterCardStyleUiState,
|
||||
)
|
||||
SettingsPage.ContentDiscovery -> contentDiscoveryContent(
|
||||
isTablet = true,
|
||||
showPluginsEntry = AppFeaturePolicy.pluginsEnabled,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ internal actual object PlatformLocalAccountDataCleaner {
|
|||
private val profileScopedBaseKeys = listOf(
|
||||
"catalog_settings_payload",
|
||||
"continue_watching_preferences_payload",
|
||||
"poster_card_style_payload",
|
||||
"episode_release_notifications_payload",
|
||||
"episode_release_notification_scheduled_ids",
|
||||
"selected_theme",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
package com.nuvio.app.core.ui
|
||||
|
||||
import com.nuvio.app.core.storage.ProfileScopedKey
|
||||
import platform.Foundation.NSUserDefaults
|
||||
|
||||
actual object PosterCardStyleStorage {
|
||||
private const val payloadKey = "poster_card_style_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