This commit is contained in:
Sai Mukesh Cheekatla 2026-05-16 01:02:00 +02:00 committed by GitHub
commit cd61c7659b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 87 additions and 30 deletions

View file

@ -757,8 +757,8 @@
<string name="theme_emerald">Esmeralda</string>
<string name="theme_ocean">Océano</string>
<string name="theme_rose">Rosa</string>
<string name="theme_violet">Violeta</string>
<string name="theme_white">Blanco</string>
<string name="theme_violet">Amatista</string>
<string name="theme_white">Glaciar</string>
<string name="player_next_episode">Siguiente episodio</string>
<string name="player_next_episode_finding_source">Buscando fuente…</string>
<string name="player_next_episode_playing_via_countdown">Reproduciendo vía %1$s en %2$d…</string>

View file

@ -790,8 +790,8 @@
<string name="theme_emerald">Émeraude</string>
<string name="theme_ocean">Océan</string>
<string name="theme_rose">Rose</string>
<string name="theme_violet">Violet</string>
<string name="theme_white">Blanc</string>
<string name="theme_violet">Améthyste</string>
<string name="theme_white">Glacier</string>
<string name="player_next_episode">Épisode suivant</string>
<string name="player_next_episode_finding_source">Recherche de la source…</string>
<string name="player_next_episode_playing_via_countdown">Lecture via %1$s dans %2$d…</string>

View file

@ -905,8 +905,8 @@
<string name="theme_emerald">Emerald</string>
<string name="theme_ocean">Ocean</string>
<string name="theme_rose">Rose</string>
<string name="theme_violet">Violet</string>
<string name="theme_white">White</string>
<string name="theme_violet">Amethyst</string>
<string name="theme_white">Glacier</string>
<string name="player_next_episode">Next Episode</string>
<string name="player_next_episode_finding_source">Finding source…</string>
<string name="player_next_episode_playing_via_countdown">Playing via %1$s in %2$d…</string>

View file

@ -13,20 +13,20 @@ import org.jetbrains.compose.resources.StringResource
enum class AppTheme {
CRIMSON,
OCEAN,
VIOLET,
AMETHYST,
EMERALD,
AMBER,
ROSE,
WHITE,
GLACIER,
}
val AppTheme.labelRes: StringResource
get() = when (this) {
AppTheme.CRIMSON -> Res.string.theme_crimson
AppTheme.OCEAN -> Res.string.theme_ocean
AppTheme.VIOLET -> Res.string.theme_violet
AppTheme.AMETHYST -> Res.string.theme_violet
AppTheme.EMERALD -> Res.string.theme_emerald
AppTheme.AMBER -> Res.string.theme_amber
AppTheme.ROSE -> Res.string.theme_rose
AppTheme.WHITE -> Res.string.theme_white
AppTheme.GLACIER -> Res.string.theme_white
}

View file

@ -25,7 +25,7 @@ import nuvio.composeapp.generated.resources.jetbrains_sans_regular
import nuvio.composeapp.generated.resources.jetbrains_sans_semibold
import org.jetbrains.compose.resources.Font
val LocalAppTheme = staticCompositionLocalOf { AppTheme.WHITE }
val LocalAppTheme = staticCompositionLocalOf { AppTheme.GLACIER }
val MaterialTheme.appTheme: AppTheme
@Composable
@ -207,7 +207,7 @@ private val NuvioRippleConfiguration = RippleConfiguration(
@Composable
fun NuvioTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
appTheme: AppTheme = AppTheme.WHITE,
appTheme: AppTheme = AppTheme.GLACIER,
amoled: Boolean = false,
content: @Composable () -> Unit,
) {

View file

@ -36,7 +36,7 @@ object ThemeColors {
backgroundCard = Color(0xFF1A1F24),
)
val Violet = ThemeColorPalette(
val Amethyst = ThemeColorPalette(
secondary = Color(0xFF8E24AA),
secondaryVariant = Color(0xFF6A1B9A),
focusRing = Color(0xFFAB47BC),
@ -76,7 +76,7 @@ object ThemeColors {
backgroundCard = Color(0xFF241A1F),
)
val White = ThemeColorPalette(
val Glacier = ThemeColorPalette(
secondary = Color(0xFFF5F5F5),
secondaryVariant = Color(0xFFE0E0E0),
onSecondary = Color(0xFF111111),
@ -91,10 +91,10 @@ object ThemeColors {
fun getColorPalette(theme: AppTheme): ThemeColorPalette = when (theme) {
AppTheme.CRIMSON -> Crimson
AppTheme.OCEAN -> Ocean
AppTheme.VIOLET -> Violet
AppTheme.AMETHYST -> Amethyst
AppTheme.EMERALD -> Emerald
AppTheme.AMBER -> Amber
AppTheme.ROSE -> Rose
AppTheme.WHITE -> White
AppTheme.GLACIER -> Glacier
}
}

View file

@ -38,6 +38,17 @@ data class MetaDetails(
val videos: List<MetaVideo> = emptyList(),
)
fun MetaDetails.toMetaPreview(): MetaPreview = MetaPreview(
id = id,
type = type,
name = name,
poster = poster,
banner = background,
logo = logo,
description = description,
releaseInfo = releaseInfo,
)
data class MetaExternalRating(
val source: String,
val value: Double,

View file

@ -93,6 +93,7 @@ import com.nuvio.app.features.trailer.TrailerPlaybackSource
import com.nuvio.app.features.watched.WatchedRepository
import com.nuvio.app.features.watched.previousReleasedEpisodesBefore
import com.nuvio.app.features.watched.releasedEpisodesForSeason
import com.nuvio.app.features.watched.watchedItemKey
import com.nuvio.app.features.watchprogress.CurrentDateProvider
import com.nuvio.app.features.watchprogress.WatchProgressEntry
import com.nuvio.app.features.watchprogress.WatchProgressRepository
@ -662,10 +663,12 @@ fun MetaDetailsScreen(
isTablet = isTablet,
playButtonLabel = playButtonLabel,
isSaved = isSaved,
isWatched = WatchingState.isPosterWatched(watchedUiState.watchedKeys, meta.type, meta.id),
onPrimaryPlayClick = onPrimaryPlayClick,
onPrimaryPlayLongClick = onPrimaryPlayLongClick,
onSaveClick = toggleSaved,
onSaveLongClick = openLibraryListPicker,
onWatchedClick = { WatchingActions.toggleMetaWatched(meta) },
showManualPlayOption = showManualPlayOption,
preferredEpisodeSeasonNumber = seriesAction?.seasonNumber,
preferredEpisodeNumber = seriesAction?.episodeNumber,
@ -996,10 +999,12 @@ private fun ConfiguredMetaSections(
isTablet: Boolean,
playButtonLabel: String,
isSaved: Boolean,
isWatched: Boolean,
onPrimaryPlayClick: () -> Unit,
onPrimaryPlayLongClick: (() -> Unit)?,
onSaveClick: () -> Unit,
onSaveLongClick: (() -> Unit)?,
onWatchedClick: () -> Unit,
showManualPlayOption: Boolean,
preferredEpisodeSeasonNumber: Int?,
preferredEpisodeNumber: Int?,
@ -1062,11 +1067,13 @@ private fun ConfiguredMetaSections(
stringResource(Res.string.action_save)
},
isSaved = isSaved,
isWatched = isWatched,
isTablet = isTablet,
onPlayClick = onPrimaryPlayClick,
onPlayLongClick = if (showManualPlayOption) onPrimaryPlayLongClick else null,
onSaveClick = onSaveClick,
onSaveLongClick = onSaveLongClick,
onWatchedClick = onWatchedClick,
)
}
MetaScreenSectionKey.OVERVIEW -> {

View file

@ -13,6 +13,8 @@ import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.Visibility
import androidx.compose.material3.IconButton
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
@ -37,11 +39,13 @@ fun DetailActionButtons(
playLabel: String = stringResource(Res.string.action_play),
saveLabel: String = stringResource(Res.string.action_save),
isSaved: Boolean = false,
isWatched: Boolean = false,
isTablet: Boolean = false,
onPlayClick: () -> Unit = {},
onPlayLongClick: (() -> Unit)? = null,
onSaveClick: () -> Unit = {},
onSaveLongClick: (() -> Unit)? = null,
onWatchedClick: () -> Unit = {},
) {
val playPainter = appIconPainter(AppIconResource.PlayerPlay)
val libraryAddPainter = appIconPainter(AppIconResource.LibraryAddPlus)
@ -138,5 +142,21 @@ fun DetailActionButtons(
)
}
}
Surface(
modifier = Modifier.size(50.dp),
shape = RoundedCornerShape(40.dp),
border = BorderStroke(1.dp, MaterialTheme.colorScheme.outline),
color = if (isWatched) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.surface.copy(alpha = 0f),
contentColor = if (isWatched) MaterialTheme.colorScheme.onPrimary else MaterialTheme.colorScheme.onSurface,
) {
IconButton(onClick = onWatchedClick) {
Icon(
imageVector = Icons.Default.Visibility,
contentDescription = null,
modifier = Modifier.size(20.dp),
)
}
}
}
}

View file

@ -40,21 +40,23 @@ import com.nuvio.app.core.ui.rememberPosterCardStyleUiState
private fun rememberHomeSkeletonBrush(): Brush {
val shimmerColors = listOf(
MaterialTheme.colorScheme.surface,
MaterialTheme.colorScheme.surface.copy(alpha = 0.5f),
MaterialTheme.colorScheme.surface,
MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f),
MaterialTheme.colorScheme.surface,
MaterialTheme.colorScheme.surface,
)
val transition = rememberInfiniteTransition()
val translateAnim by transition.animateFloat(
initialValue = 0f,
targetValue = 1000f,
targetValue = 1300f,
animationSpec = infiniteRepeatable(
animation = tween(durationMillis = 1200, easing = LinearEasing),
animation = tween(durationMillis = 1500, easing = LinearEasing),
repeatMode = RepeatMode.Restart,
),
)
val brush = Brush.linearGradient(
colors = shimmerColors,
start = Offset(translateAnim - 200f, 0f),
start = Offset(translateAnim - 600f, 0f),
end = Offset(translateAnim, 0f),
)
return brush

View file

@ -86,7 +86,7 @@ internal fun LazyListScope.appearanceSettingsContent(
isTablet = isTablet,
) {
SettingsGroup(isTablet = isTablet) {
val themes = listOf(AppTheme.WHITE) + AppTheme.entries.filterNot { it == AppTheme.WHITE }
val themes = listOf(AppTheme.GLACIER) + AppTheme.entries.filterNot { it == AppTheme.GLACIER }
FlowRow(
modifier = Modifier
.fillMaxWidth()

View file

@ -7,7 +7,7 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
object ThemeSettingsRepository {
private val _selectedTheme = MutableStateFlow(AppTheme.WHITE)
private val _selectedTheme = MutableStateFlow(AppTheme.GLACIER)
val selectedTheme: StateFlow<AppTheme> = _selectedTheme.asStateFlow()
private val _amoledEnabled = MutableStateFlow(false)
@ -32,10 +32,10 @@ object ThemeSettingsRepository {
fun clearLocalState() {
hasLoaded = false
_selectedTheme.value = AppTheme.WHITE
_selectedTheme.value = AppTheme.GLACIER
_amoledEnabled.value = false
_liquidGlassNativeTabBarEnabled.value = false
NativeTabBridge.publishAccentColor(AppTheme.WHITE.nativeTabAccentHex())
NativeTabBridge.publishAccentColor(AppTheme.GLACIER.nativeTabAccentHex())
NativeTabBridge.publishLiquidGlassEnabled(false)
_selectedAppLanguage.value = AppLanguage.ENGLISH
}
@ -45,12 +45,16 @@ object ThemeSettingsRepository {
val stored = ThemeSettingsStorage.loadSelectedTheme()
val theme = if (stored != null) {
try {
AppTheme.valueOf(stored)
when (stored) {
"VIOLET" -> AppTheme.AMETHYST
"WHITE" -> AppTheme.GLACIER
else -> AppTheme.valueOf(stored)
}
} catch (_: IllegalArgumentException) {
AppTheme.WHITE
AppTheme.GLACIER
}
} else {
AppTheme.WHITE
AppTheme.GLACIER
}
_selectedTheme.value = theme
NativeTabBridge.publishAccentColor(theme.nativeTabAccentHex())
@ -98,9 +102,9 @@ object ThemeSettingsRepository {
private fun AppTheme.nativeTabAccentHex(): String = when (this) {
AppTheme.CRIMSON -> "#E53935"
AppTheme.OCEAN -> "#1E88E5"
AppTheme.VIOLET -> "#8E24AA"
AppTheme.AMETHYST -> "#8E24AA"
AppTheme.EMERALD -> "#43A047"
AppTheme.AMBER -> "#FB8C00"
AppTheme.ROSE -> "#D81B60"
AppTheme.WHITE -> "#F5F5F5"
AppTheme.GLACIER -> "#F5F5F5"
}

View file

@ -3,6 +3,7 @@ package com.nuvio.app.features.watching.application
import com.nuvio.app.features.details.MetaDetails
import com.nuvio.app.features.details.MetaDetailsRepository
import com.nuvio.app.features.details.MetaVideo
import com.nuvio.app.features.details.toMetaPreview
import com.nuvio.app.features.home.MetaPreview
import com.nuvio.app.features.watched.WatchedItem
import com.nuvio.app.features.watched.WatchedRepository
@ -22,6 +23,12 @@ import kotlinx.coroutines.launch
object WatchingActions {
private val actionScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
fun toggleMetaWatched(meta: MetaDetails) {
actionScope.launch {
togglePosterWatched(meta.toMetaPreview())
}
}
suspend fun togglePosterWatched(preview: MetaPreview) {
if (preview.type != "series") {
WatchedRepository.toggleWatched(preview.toWatchedItem(markedAtEpochMs = 0L))

View file

@ -18,7 +18,13 @@ object WatchingState {
fun isPosterWatched(
watchedKeys: Set<String>,
item: MetaPreview,
): Boolean = watchedKeys.contains(watchedItemKey(item.type, item.id))
): Boolean = isPosterWatched(watchedKeys, item.type, item.id)
fun isPosterWatched(
watchedKeys: Set<String>,
type: String,
id: String,
): Boolean = watchedKeys.contains(watchedItemKey(type, id))
fun isEpisodeWatched(
watchedKeys: Set<String>,