diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/MetaDetailsScreen.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/MetaDetailsScreen.kt index eab575054..2d77010df 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/MetaDetailsScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/MetaDetailsScreen.kt @@ -38,10 +38,17 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha +import androidx.compose.ui.draw.blur +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import androidx.compose.ui.zIndex import androidx.lifecycle.compose.collectAsStateWithLifecycle +import coil3.compose.AsyncImage import com.nuvio.app.core.ui.NuvioBackButton import com.nuvio.app.core.ui.TraktListPickerDialog import com.nuvio.app.core.ui.nuvioPlatformExtraBottomPadding @@ -435,11 +442,31 @@ fun MetaDetailsScreen( val isTablet = maxWidth >= 720.dp val contentHorizontalPadding = if (isTablet) 32.dp else 18.dp val contentMaxWidth = detailTabletContentMaxWidth(maxWidth, isTablet) + val cinematicEnabled = metaScreenSettingsUiState.cinematicBackground Box(modifier = Modifier.fillMaxSize()) { + if (cinematicEnabled) { + val backdropUrl = meta.background ?: meta.poster + if (backdropUrl != null) { + AsyncImage( + model = backdropUrl, + contentDescription = null, + modifier = Modifier + .fillMaxSize() + .blur(30.dp), + contentScale = ContentScale.Crop, + ) + Box( + modifier = Modifier + .fillMaxSize() + .background(MaterialTheme.colorScheme.background.copy(alpha = 0.92f)), + ) + } + } Column( modifier = Modifier .fillMaxSize() + .zIndex(1f) .verticalScroll(scrollState), ) { DetailHero( @@ -524,13 +551,36 @@ fun MetaDetailsScreen( } } + if (cinematicEnabled && heroHeightPx > 0) { + val blendColor = MaterialTheme.colorScheme.background + Box( + modifier = Modifier + .zIndex(0.5f) + .fillMaxWidth() + .height(132.dp) + .graphicsLayer { + translationY = heroHeightPx.toFloat() - scrollState.value + } + .background( + Brush.verticalGradient( + colors = listOf( + blendColor.copy(alpha = 0.98f), + blendColor.copy(alpha = 0.84f), + blendColor.copy(alpha = 0.52f), + Color.Transparent, + ), + ), + ), + ) + } + if (headerProgress <= 0.05f) { NuvioBackButton( onClick = onBack, modifier = Modifier.padding( start = 12.dp, top = WindowInsets.statusBars.asPaddingValues().calculateTopPadding() + 8.dp, - ), + ).zIndex(2f), containerColor = MaterialTheme.colorScheme.background.copy(alpha = 0.5f), contentColor = MaterialTheme.colorScheme.onBackground, ) @@ -542,6 +592,7 @@ fun MetaDetailsScreen( progress = headerProgress, onBack = onBack, onToggleSaved = toggleSaved, + modifier = Modifier.zIndex(2f), ) selectedEpisodeForActions?.let { selectedEpisode -> diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/MetaScreenSettingsRepository.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/MetaScreenSettingsRepository.kt index 6f3c9858a..00a50d0a0 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/MetaScreenSettingsRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/details/MetaScreenSettingsRepository.kt @@ -31,6 +31,7 @@ data class MetaScreenSectionItem( data class MetaScreenSettingsUiState( val items: List = emptyList(), + val cinematicBackground: Boolean = false, ) @Serializable @@ -43,6 +44,7 @@ private data class StoredMetaScreenSectionPreference( @Serializable private data class StoredMetaScreenSettingsPayload( val items: List = emptyList(), + val cinematicBackground: Boolean = false, ) private data class MetaScreenSectionDefinition( @@ -115,6 +117,7 @@ object MetaScreenSettingsRepository { private var hasLoaded = false private var preferences: MutableMap = mutableMapOf() + private var cinematicBackground: Boolean = false fun ensureLoaded() { if (hasLoaded) return @@ -126,6 +129,7 @@ object MetaScreenSettingsRepository { json.decodeFromString(payload) }.getOrNull() if (parsed != null) { + cinematicBackground = parsed.cinematicBackground preferences = parsed.items.mapNotNull { item -> val key = runCatching { MetaScreenSectionKey.valueOf(item.key) }.getOrNull() ?: return@mapNotNull null key to item @@ -141,13 +145,22 @@ object MetaScreenSettingsRepository { fun onProfileChanged() { hasLoaded = false preferences.clear() + cinematicBackground = false _uiState.value = MetaScreenSettingsUiState() ensureLoaded() } + fun setCinematicBackground(enabled: Boolean) { + ensureLoaded() + cinematicBackground = enabled + publish() + persist() + } + fun clearLocalState() { hasLoaded = false preferences.clear() + cinematicBackground = false _uiState.value = MetaScreenSettingsUiState() } @@ -160,6 +173,7 @@ object MetaScreenSettingsRepository { fun resetToDefaults() { ensureLoaded() preferences.clear() + cinematicBackground = false normalizePreferences() publish() persist() @@ -221,6 +235,7 @@ object MetaScreenSettingsRepository { order = preference?.order ?: 0, ) }, + cinematicBackground = cinematicBackground, ) } @@ -229,6 +244,7 @@ object MetaScreenSettingsRepository { json.encodeToString( StoredMetaScreenSettingsPayload( items = preferences.values.sortedBy { it.order }, + cinematicBackground = cinematicBackground, ), ), ) diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/MetaScreenSettingsPage.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/MetaScreenSettingsPage.kt index 46fcdfd60..55954dd6e 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/MetaScreenSettingsPage.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/MetaScreenSettingsPage.kt @@ -42,6 +42,22 @@ internal fun LazyListScope.metaScreenSettingsContent( isTablet: Boolean, uiState: MetaScreenSettingsUiState, ) { + item { + SettingsSection( + title = "APPEARANCE", + isTablet = isTablet, + ) { + SettingsGroup(isTablet = isTablet) { + SettingsSwitchRow( + title = "Cinematic Background", + description = "Blurred backdrop behind content, similar to stream screen.", + checked = uiState.cinematicBackground, + isTablet = isTablet, + onCheckedChange = { MetaScreenSettingsRepository.setCinematicBackground(it) }, + ) + } + } + } item { SettingsSection( title = "SECTIONS",