mirror of
https://github.com/bbara04/Purefin.git
synced 2026-04-01 01:30:08 +02:00
refactor UI styling and image handling
- Remove custom color token classes (`EpisodeColors`, `MovieColors`, `SeriesColors`, `HomeColors`) in favor of standard `MaterialTheme.colorScheme`. - Introduce `PurefinAsyncImage` component to provide consistent theme-synced placeholders for asynchronous images. - Refactor various UI components (`MediaGhostIconButton`, `MediaMetaChip`, `MediaCastRow`, `PosterCard`) to use `MaterialTheme` directly instead of custom color objects. - Simplify `MediaDetailColors` mapping logic by removing redundant conversion functions. - Update `PurefinTheme` to disable dynamic color by default. - Force light mode in `PlayerActivity` temporarily. - Replace standard Coil `AsyncImage` with `PurefinAsyncImage` across the application.
This commit is contained in:
@@ -1,38 +0,0 @@
|
|||||||
package hu.bbara.purefin.app.content.episode
|
|
||||||
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
|
|
||||||
internal data class EpisodeColors(
|
|
||||||
val primary: Color,
|
|
||||||
val onPrimary: Color,
|
|
||||||
val background: Color,
|
|
||||||
val surface: Color,
|
|
||||||
val surfaceAlt: Color,
|
|
||||||
val surfaceBorder: Color,
|
|
||||||
val textPrimary: Color,
|
|
||||||
val textSecondary: Color,
|
|
||||||
val textMuted: Color,
|
|
||||||
val textMutedStrong: Color
|
|
||||||
)
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
internal fun rememberEpisodeColors(): EpisodeColors {
|
|
||||||
val scheme = MaterialTheme.colorScheme
|
|
||||||
return remember(scheme) {
|
|
||||||
EpisodeColors(
|
|
||||||
primary = scheme.primary,
|
|
||||||
onPrimary = scheme.onPrimary,
|
|
||||||
background = scheme.background,
|
|
||||||
surface = scheme.surface,
|
|
||||||
surfaceAlt = scheme.surfaceVariant,
|
|
||||||
surfaceBorder = scheme.outlineVariant,
|
|
||||||
textPrimary = scheme.onBackground,
|
|
||||||
textSecondary = scheme.onSurface,
|
|
||||||
textMuted = scheme.onSurfaceVariant,
|
|
||||||
textMutedStrong = scheme.onSurfaceVariant.copy(alpha = 0.7f)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -36,7 +36,6 @@ import hu.bbara.purefin.common.ui.MediaMetaChip
|
|||||||
import hu.bbara.purefin.common.ui.components.MediaActionButton
|
import hu.bbara.purefin.common.ui.components.MediaActionButton
|
||||||
import hu.bbara.purefin.common.ui.components.MediaPlayButton
|
import hu.bbara.purefin.common.ui.components.MediaPlayButton
|
||||||
import hu.bbara.purefin.common.ui.components.MediaPlaybackSettings
|
import hu.bbara.purefin.common.ui.components.MediaPlaybackSettings
|
||||||
import hu.bbara.purefin.common.ui.toMediaDetailColors
|
|
||||||
import hu.bbara.purefin.player.PlayerActivity
|
import hu.bbara.purefin.player.PlayerActivity
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -44,7 +43,6 @@ internal fun EpisodeTopBar(
|
|||||||
onBack: () -> Unit,
|
onBack: () -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val colors = rememberEpisodeColors().toMediaDetailColors()
|
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -54,14 +52,13 @@ internal fun EpisodeTopBar(
|
|||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
MediaGhostIconButton(
|
MediaGhostIconButton(
|
||||||
colors = colors,
|
|
||||||
icon = Icons.Outlined.ArrowBack,
|
icon = Icons.Outlined.ArrowBack,
|
||||||
contentDescription = "Back",
|
contentDescription = "Back",
|
||||||
onClick = onBack
|
onClick = onBack
|
||||||
)
|
)
|
||||||
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
|
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||||
MediaGhostIconButton(colors = colors, icon = Icons.Outlined.Cast, contentDescription = "Cast", onClick = { })
|
MediaGhostIconButton(icon = Icons.Outlined.Cast, contentDescription = "Cast", onClick = { })
|
||||||
MediaGhostIconButton(colors = colors, icon = Icons.Outlined.MoreVert, contentDescription = "More", onClick = { })
|
MediaGhostIconButton(icon = Icons.Outlined.MoreVert, contentDescription = "More", onClick = { })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -72,6 +69,8 @@ internal fun EpisodeDetails(
|
|||||||
episode: EpisodeUiModel,
|
episode: EpisodeUiModel,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
|
val scheme = MaterialTheme.colorScheme
|
||||||
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val playAction = remember(episode.id) {
|
val playAction = remember(episode.id) {
|
||||||
{
|
{
|
||||||
@@ -81,11 +80,10 @@ internal fun EpisodeDetails(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val colors = rememberEpisodeColors().toMediaDetailColors()
|
|
||||||
Column(modifier = modifier) {
|
Column(modifier = modifier) {
|
||||||
Text(
|
Text(
|
||||||
text = episode.title,
|
text = episode.title,
|
||||||
color = colors.textPrimary,
|
color = scheme.onBackground,
|
||||||
fontSize = 32.sp,
|
fontSize = 32.sp,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
lineHeight = 38.sp
|
lineHeight = 38.sp
|
||||||
@@ -95,29 +93,28 @@ internal fun EpisodeDetails(
|
|||||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
) {
|
) {
|
||||||
MediaMetaChip(colors = colors, text = episode.releaseDate)
|
MediaMetaChip(text = episode.releaseDate)
|
||||||
MediaMetaChip(colors = colors, text = episode.rating)
|
MediaMetaChip(text = episode.rating)
|
||||||
MediaMetaChip(colors = colors, text = episode.runtime)
|
MediaMetaChip(text = episode.runtime)
|
||||||
MediaMetaChip(
|
MediaMetaChip(
|
||||||
colors = colors,
|
|
||||||
text = episode.format,
|
text = episode.format,
|
||||||
background = colors.primary.copy(alpha = 0.2f),
|
background = scheme.primary.copy(alpha = 0.2f),
|
||||||
border = colors.primary.copy(alpha = 0.3f),
|
border = scheme.primary.copy(alpha = 0.3f),
|
||||||
textColor = colors.primary
|
textColor = scheme.primary
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.height(24.dp))
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = "Synopsis",
|
text = "Synopsis",
|
||||||
color = colors.textPrimary,
|
color = scheme.onBackground,
|
||||||
fontSize = 18.sp,
|
fontSize = 18.sp,
|
||||||
fontWeight = FontWeight.Bold
|
fontWeight = FontWeight.Bold
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(12.dp))
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
Text(
|
Text(
|
||||||
text = episode.synopsis,
|
text = episode.synopsis,
|
||||||
color = colors.textMuted,
|
color = scheme.onSurfaceVariant,
|
||||||
fontSize = 15.sp,
|
fontSize = 15.sp,
|
||||||
lineHeight = 22.sp
|
lineHeight = 22.sp
|
||||||
)
|
)
|
||||||
@@ -165,13 +162,12 @@ internal fun EpisodeDetails(
|
|||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = "Cast",
|
text = "Cast",
|
||||||
color = colors.textPrimary,
|
color = scheme.onBackground,
|
||||||
fontSize = 18.sp,
|
fontSize = 18.sp,
|
||||||
fontWeight = FontWeight.Bold
|
fontWeight = FontWeight.Bold
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(12.dp))
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
MediaCastRow(
|
MediaCastRow(
|
||||||
colors = colors,
|
|
||||||
cast = episode.cast.map { it.toMediaCastMember() }
|
cast = episode.cast.map { it.toMediaCastMember() }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
package hu.bbara.purefin.app.content.movie
|
|
||||||
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
|
|
||||||
internal data class MovieColors(
|
|
||||||
val primary: Color,
|
|
||||||
val onPrimary: Color,
|
|
||||||
val background: Color,
|
|
||||||
val surface: Color,
|
|
||||||
val surfaceAlt: Color,
|
|
||||||
val surfaceBorder: Color,
|
|
||||||
val textPrimary: Color,
|
|
||||||
val textSecondary: Color,
|
|
||||||
val textMuted: Color,
|
|
||||||
val textMutedStrong: Color
|
|
||||||
)
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
internal fun rememberMovieColors(): MovieColors {
|
|
||||||
val scheme = MaterialTheme.colorScheme
|
|
||||||
return remember(scheme) {
|
|
||||||
MovieColors(
|
|
||||||
primary = scheme.primary,
|
|
||||||
onPrimary = scheme.onPrimary,
|
|
||||||
background = scheme.background,
|
|
||||||
surface = scheme.surface,
|
|
||||||
surfaceAlt = scheme.surfaceVariant,
|
|
||||||
surfaceBorder = scheme.outlineVariant,
|
|
||||||
textPrimary = scheme.onBackground,
|
|
||||||
textSecondary = scheme.onSurface,
|
|
||||||
textMuted = scheme.onSurfaceVariant,
|
|
||||||
textMutedStrong = scheme.onSurfaceVariant.copy(alpha = 0.7f)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -36,7 +36,6 @@ import hu.bbara.purefin.common.ui.MediaMetaChip
|
|||||||
import hu.bbara.purefin.common.ui.components.MediaActionButton
|
import hu.bbara.purefin.common.ui.components.MediaActionButton
|
||||||
import hu.bbara.purefin.common.ui.components.MediaPlayButton
|
import hu.bbara.purefin.common.ui.components.MediaPlayButton
|
||||||
import hu.bbara.purefin.common.ui.components.MediaPlaybackSettings
|
import hu.bbara.purefin.common.ui.components.MediaPlaybackSettings
|
||||||
import hu.bbara.purefin.common.ui.toMediaDetailColors
|
|
||||||
import hu.bbara.purefin.player.PlayerActivity
|
import hu.bbara.purefin.player.PlayerActivity
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -44,7 +43,7 @@ internal fun MovieTopBar(
|
|||||||
onBack: () -> Unit,
|
onBack: () -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val colors = rememberMovieColors().toMediaDetailColors()
|
val scheme = MaterialTheme.colorScheme
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -54,14 +53,13 @@ internal fun MovieTopBar(
|
|||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
MediaGhostIconButton(
|
MediaGhostIconButton(
|
||||||
colors = colors,
|
|
||||||
icon = Icons.Outlined.ArrowBack,
|
icon = Icons.Outlined.ArrowBack,
|
||||||
contentDescription = "Back",
|
contentDescription = "Back",
|
||||||
onClick = onBack
|
onClick = onBack
|
||||||
)
|
)
|
||||||
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
|
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||||
MediaGhostIconButton(colors = colors, icon = Icons.Outlined.Cast, contentDescription = "Cast", onClick = { })
|
MediaGhostIconButton(icon = Icons.Outlined.Cast, contentDescription = "Cast", onClick = { })
|
||||||
MediaGhostIconButton(colors = colors, icon = Icons.Outlined.MoreVert, contentDescription = "More", onClick = { })
|
MediaGhostIconButton(icon = Icons.Outlined.MoreVert, contentDescription = "More", onClick = { })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -72,6 +70,8 @@ internal fun MovieDetails(
|
|||||||
movie: MovieUiModel,
|
movie: MovieUiModel,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
|
val scheme = MaterialTheme.colorScheme
|
||||||
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val playAction = remember(movie.id) {
|
val playAction = remember(movie.id) {
|
||||||
{
|
{
|
||||||
@@ -81,11 +81,10 @@ internal fun MovieDetails(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val colors = rememberMovieColors().toMediaDetailColors()
|
|
||||||
Column(modifier = modifier) {
|
Column(modifier = modifier) {
|
||||||
Text(
|
Text(
|
||||||
text = movie.title,
|
text = movie.title,
|
||||||
color = colors.textPrimary,
|
color = scheme.onBackground,
|
||||||
fontSize = 32.sp,
|
fontSize = 32.sp,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
lineHeight = 38.sp
|
lineHeight = 38.sp
|
||||||
@@ -95,29 +94,28 @@ internal fun MovieDetails(
|
|||||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
) {
|
) {
|
||||||
MediaMetaChip(colors = colors, text = movie.year)
|
MediaMetaChip(text = movie.year)
|
||||||
MediaMetaChip(colors = colors, text = movie.rating)
|
MediaMetaChip(text = movie.rating)
|
||||||
MediaMetaChip(colors = colors, text = movie.runtime)
|
MediaMetaChip(text = movie.runtime)
|
||||||
MediaMetaChip(
|
MediaMetaChip(
|
||||||
colors = colors,
|
|
||||||
text = movie.format,
|
text = movie.format,
|
||||||
background = colors.primary.copy(alpha = 0.2f),
|
background = scheme.primary.copy(alpha = 0.2f),
|
||||||
border = colors.primary.copy(alpha = 0.3f),
|
border = scheme.primary.copy(alpha = 0.3f),
|
||||||
textColor = colors.primary
|
textColor = scheme.primary
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.height(24.dp))
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = "Synopsis",
|
text = "Synopsis",
|
||||||
color = colors.textPrimary,
|
color = scheme.onBackground,
|
||||||
fontSize = 18.sp,
|
fontSize = 18.sp,
|
||||||
fontWeight = FontWeight.Bold
|
fontWeight = FontWeight.Bold
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(12.dp))
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
Text(
|
Text(
|
||||||
text = movie.synopsis,
|
text = movie.synopsis,
|
||||||
color = colors.textMuted,
|
color = scheme.onSurfaceVariant,
|
||||||
fontSize = 15.sp,
|
fontSize = 15.sp,
|
||||||
lineHeight = 22.sp
|
lineHeight = 22.sp
|
||||||
)
|
)
|
||||||
@@ -165,13 +163,12 @@ internal fun MovieDetails(
|
|||||||
Spacer(modifier = Modifier.height(24.dp))
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
Text(
|
Text(
|
||||||
text = "Cast",
|
text = "Cast",
|
||||||
color = colors.textPrimary,
|
color = scheme.onBackground,
|
||||||
fontSize = 18.sp,
|
fontSize = 18.sp,
|
||||||
fontWeight = FontWeight.Bold
|
fontWeight = FontWeight.Bold
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(12.dp))
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
MediaCastRow(
|
MediaCastRow(
|
||||||
colors = colors,
|
|
||||||
cast = movie.cast.map { it.toMediaCastMember() }
|
cast = movie.cast.map { it.toMediaCastMember() }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
package hu.bbara.purefin.app.content.series
|
|
||||||
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
|
|
||||||
internal data class SeriesColors(
|
|
||||||
val primary: Color,
|
|
||||||
val onPrimary: Color,
|
|
||||||
val background: Color,
|
|
||||||
val surface: Color,
|
|
||||||
val surfaceAlt: Color,
|
|
||||||
val surfaceBorder: Color,
|
|
||||||
val textPrimary: Color,
|
|
||||||
val textSecondary: Color,
|
|
||||||
val textMuted: Color,
|
|
||||||
val textMutedStrong: Color
|
|
||||||
)
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
internal fun rememberSeriesColors(): SeriesColors {
|
|
||||||
val scheme = MaterialTheme.colorScheme
|
|
||||||
return remember(scheme) {
|
|
||||||
SeriesColors(
|
|
||||||
primary = scheme.primary,
|
|
||||||
onPrimary = scheme.onPrimary,
|
|
||||||
background = scheme.background,
|
|
||||||
surface = scheme.surface,
|
|
||||||
surfaceAlt = scheme.surfaceVariant,
|
|
||||||
surfaceBorder = scheme.outlineVariant,
|
|
||||||
textPrimary = scheme.onBackground,
|
|
||||||
textSecondary = scheme.onSurface,
|
|
||||||
textMuted = scheme.onSurfaceVariant,
|
|
||||||
textMutedStrong = scheme.onSurfaceVariant.copy(alpha = 0.7f)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -46,21 +46,19 @@ import androidx.compose.ui.unit.Dp
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import coil3.compose.AsyncImage
|
|
||||||
import hu.bbara.purefin.common.ui.MediaCastMember
|
import hu.bbara.purefin.common.ui.MediaCastMember
|
||||||
import hu.bbara.purefin.common.ui.MediaCastRow
|
import hu.bbara.purefin.common.ui.MediaCastRow
|
||||||
import hu.bbara.purefin.common.ui.MediaGhostIconButton
|
import hu.bbara.purefin.common.ui.MediaGhostIconButton
|
||||||
import hu.bbara.purefin.common.ui.MediaMetaChip
|
import hu.bbara.purefin.common.ui.MediaMetaChip
|
||||||
import hu.bbara.purefin.common.ui.components.MediaActionButton
|
import hu.bbara.purefin.common.ui.components.MediaActionButton
|
||||||
import hu.bbara.purefin.common.ui.components.MediaHero
|
import hu.bbara.purefin.common.ui.components.MediaHero
|
||||||
import hu.bbara.purefin.common.ui.toMediaDetailColors
|
import hu.bbara.purefin.common.ui.components.PurefinAsyncImage
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun SeriesTopBar(
|
internal fun SeriesTopBar(
|
||||||
onBack: () -> Unit,
|
onBack: () -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val colors = rememberSeriesColors().toMediaDetailColors()
|
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -70,13 +68,12 @@ internal fun SeriesTopBar(
|
|||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
MediaGhostIconButton(
|
MediaGhostIconButton(
|
||||||
colors = colors,
|
|
||||||
onClick = onBack,
|
onClick = onBack,
|
||||||
icon = Icons.Outlined.ArrowBack,
|
icon = Icons.Outlined.ArrowBack,
|
||||||
contentDescription = "Back")
|
contentDescription = "Back")
|
||||||
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
|
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||||
MediaGhostIconButton(colors = colors, icon = Icons.Outlined.Cast, contentDescription = "Cast", onClick = { })
|
MediaGhostIconButton(icon = Icons.Outlined.Cast, contentDescription = "Cast", onClick = { })
|
||||||
MediaGhostIconButton(colors = colors, icon = Icons.Outlined.MoreVert, contentDescription = "More", onClick = { })
|
MediaGhostIconButton(icon = Icons.Outlined.MoreVert, contentDescription = "More", onClick = { })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,7 +84,6 @@ internal fun SeriesHero(
|
|||||||
height: Dp,
|
height: Dp,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val colors = rememberSeriesColors().toMediaDetailColors()
|
|
||||||
MediaHero(
|
MediaHero(
|
||||||
imageUrl = imageUrl,
|
imageUrl = imageUrl,
|
||||||
backgroundColor = MaterialTheme.colorScheme.background,
|
backgroundColor = MaterialTheme.colorScheme.background,
|
||||||
@@ -99,27 +95,25 @@ internal fun SeriesHero(
|
|||||||
@OptIn(ExperimentalLayoutApi::class)
|
@OptIn(ExperimentalLayoutApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
internal fun SeriesMetaChips(series: SeriesUiModel) {
|
internal fun SeriesMetaChips(series: SeriesUiModel) {
|
||||||
val colors = rememberSeriesColors().toMediaDetailColors()
|
val scheme = MaterialTheme.colorScheme
|
||||||
FlowRow(
|
FlowRow(
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
) {
|
) {
|
||||||
MediaMetaChip(colors = colors, text = series.year)
|
MediaMetaChip(text = series.year)
|
||||||
MediaMetaChip(colors = colors, text = series.rating)
|
MediaMetaChip(text = series.rating)
|
||||||
MediaMetaChip(colors = colors, text = series.seasons)
|
MediaMetaChip(text = series.seasons)
|
||||||
MediaMetaChip(
|
MediaMetaChip(
|
||||||
colors = colors,
|
|
||||||
text = series.format,
|
text = series.format,
|
||||||
background = colors.primary.copy(alpha = 0.2f),
|
background = scheme.primary.copy(alpha = 0.2f),
|
||||||
border = colors.primary.copy(alpha = 0.3f),
|
border = scheme.primary.copy(alpha = 0.3f),
|
||||||
textColor = colors.primary
|
textColor = scheme.primary
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun SeriesActionButtons(modifier: Modifier = Modifier) {
|
internal fun SeriesActionButtons(modifier: Modifier = Modifier) {
|
||||||
val colors = rememberSeriesColors().toMediaDetailColors()
|
|
||||||
Row() {
|
Row() {
|
||||||
MediaActionButton(
|
MediaActionButton(
|
||||||
backgroundColor = MaterialTheme.colorScheme.secondary,
|
backgroundColor = MaterialTheme.colorScheme.secondary,
|
||||||
@@ -153,9 +147,10 @@ internal fun SeasonTabs(seasons: List<SeriesSeasonUiModel>, modifier: Modifier =
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun SeasonTab(name: String, isSelected: Boolean) {
|
private fun SeasonTab(name: String, isSelected: Boolean) {
|
||||||
val colors = rememberSeriesColors()
|
val scheme = MaterialTheme.colorScheme
|
||||||
val color = if (isSelected) colors.primary else colors.textMutedStrong
|
val mutedStrong = scheme.onSurfaceVariant.copy(alpha = 0.7f)
|
||||||
val borderColor = if (isSelected) colors.primary else Color.Transparent
|
val color = if (isSelected) scheme.primary else mutedStrong
|
||||||
|
val borderColor = if (isSelected) scheme.primary else Color.Transparent
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(bottom = 8.dp)
|
.padding(bottom = 8.dp)
|
||||||
@@ -195,13 +190,14 @@ private fun EpisodeCard(
|
|||||||
viewModel: SeriesViewModel = hiltViewModel(),
|
viewModel: SeriesViewModel = hiltViewModel(),
|
||||||
episode: SeriesEpisodeUiModel
|
episode: SeriesEpisodeUiModel
|
||||||
) {
|
) {
|
||||||
val colors = rememberSeriesColors()
|
val scheme = MaterialTheme.colorScheme
|
||||||
|
val mutedStrong = scheme.onSurfaceVariant.copy(alpha = 0.7f)
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.width(260.dp)
|
.width(260.dp)
|
||||||
.clip(RoundedCornerShape(16.dp))
|
.clip(RoundedCornerShape(16.dp))
|
||||||
.background(colors.surfaceAlt.copy(alpha = 0.6f))
|
.background(scheme.surfaceVariant.copy(alpha = 0.6f))
|
||||||
.border(1.dp, colors.surfaceBorder, RoundedCornerShape(16.dp))
|
.border(1.dp, scheme.outlineVariant, RoundedCornerShape(16.dp))
|
||||||
.padding(12.dp)
|
.padding(12.dp)
|
||||||
.clickable { viewModel.onSelectEpisode(episode.id) },
|
.clickable { viewModel.onSelectEpisode(episode.id) },
|
||||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||||
@@ -211,10 +207,10 @@ private fun EpisodeCard(
|
|||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.aspectRatio(16f / 9f)
|
.aspectRatio(16f / 9f)
|
||||||
.clip(RoundedCornerShape(12.dp))
|
.clip(RoundedCornerShape(12.dp))
|
||||||
.background(colors.surface)
|
.background(scheme.surface)
|
||||||
.border(1.dp, colors.surfaceBorder, RoundedCornerShape(12.dp))
|
.border(1.dp, scheme.outlineVariant, RoundedCornerShape(12.dp))
|
||||||
) {
|
) {
|
||||||
AsyncImage(
|
PurefinAsyncImage(
|
||||||
model = episode.imageUrl,
|
model = episode.imageUrl,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
@@ -223,12 +219,12 @@ private fun EpisodeCard(
|
|||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.matchParentSize()
|
.matchParentSize()
|
||||||
.background(colors.background.copy(alpha = 0.2f))
|
.background(scheme.background.copy(alpha = 0.2f))
|
||||||
)
|
)
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Outlined.PlayCircle,
|
imageVector = Icons.Outlined.PlayCircle,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
tint = colors.textPrimary,
|
tint = scheme.onBackground,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(Alignment.Center)
|
.align(Alignment.Center)
|
||||||
.size(32.dp)
|
.size(32.dp)
|
||||||
@@ -237,12 +233,12 @@ private fun EpisodeCard(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(Alignment.BottomEnd)
|
.align(Alignment.BottomEnd)
|
||||||
.padding(6.dp)
|
.padding(6.dp)
|
||||||
.background(colors.background.copy(alpha = 0.8f), RoundedCornerShape(6.dp))
|
.background(scheme.background.copy(alpha = 0.8f), RoundedCornerShape(6.dp))
|
||||||
.padding(horizontal = 6.dp, vertical = 2.dp)
|
.padding(horizontal = 6.dp, vertical = 2.dp)
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = episode.duration,
|
text = episode.duration,
|
||||||
color = colors.textPrimary,
|
color = scheme.onBackground,
|
||||||
fontSize = 10.sp,
|
fontSize = 10.sp,
|
||||||
fontWeight = FontWeight.Bold
|
fontWeight = FontWeight.Bold
|
||||||
)
|
)
|
||||||
@@ -253,7 +249,7 @@ private fun EpisodeCard(
|
|||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = episode.title,
|
text = episode.title,
|
||||||
color = colors.textPrimary,
|
color = scheme.onBackground,
|
||||||
fontSize = 13.sp,
|
fontSize = 13.sp,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
@@ -261,7 +257,7 @@ private fun EpisodeCard(
|
|||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = episode.description,
|
text = episode.description,
|
||||||
color = colors.textMutedStrong,
|
color = mutedStrong,
|
||||||
fontSize = 11.sp,
|
fontSize = 11.sp,
|
||||||
lineHeight = 16.sp,
|
lineHeight = 16.sp,
|
||||||
maxLines = 2,
|
maxLines = 2,
|
||||||
@@ -273,9 +269,7 @@ private fun EpisodeCard(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun CastRow(cast: List<SeriesCastMemberUiModel>, modifier: Modifier = Modifier) {
|
internal fun CastRow(cast: List<SeriesCastMemberUiModel>, modifier: Modifier = Modifier) {
|
||||||
val colors = rememberSeriesColors().toMediaDetailColors()
|
|
||||||
MediaCastRow(
|
MediaCastRow(
|
||||||
colors = colors,
|
|
||||||
cast = cast.map { it.toMediaCastMember() },
|
cast = cast.map { it.toMediaCastMember() },
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
cardWidth = 84.dp,
|
cardWidth = 84.dp,
|
||||||
|
|||||||
@@ -53,7 +53,8 @@ private fun SeriesScreenInternal(
|
|||||||
onBack: () -> Unit,
|
onBack: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val colors = rememberSeriesColors()
|
val scheme = MaterialTheme.colorScheme
|
||||||
|
val textMutedStrong = scheme.onSurfaceVariant.copy(alpha = 0.7f)
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
@@ -83,7 +84,7 @@ private fun SeriesScreenInternal(
|
|||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = series.title,
|
text = series.title,
|
||||||
color = colors.textPrimary,
|
color = scheme.onBackground,
|
||||||
fontSize = 30.sp,
|
fontSize = 30.sp,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
lineHeight = 36.sp
|
lineHeight = 36.sp
|
||||||
@@ -95,20 +96,20 @@ private fun SeriesScreenInternal(
|
|||||||
Spacer(modifier = Modifier.height(24.dp))
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
Text(
|
Text(
|
||||||
text = "Synopsis",
|
text = "Synopsis",
|
||||||
color = colors.textPrimary,
|
color = scheme.onBackground,
|
||||||
fontSize = 18.sp,
|
fontSize = 18.sp,
|
||||||
fontWeight = FontWeight.Bold
|
fontWeight = FontWeight.Bold
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
Text(
|
Text(
|
||||||
text = series.synopsis,
|
text = series.synopsis,
|
||||||
color = colors.textMutedStrong,
|
color = textMutedStrong,
|
||||||
fontSize = 13.sp,
|
fontSize = 13.sp,
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(28.dp))
|
Spacer(modifier = Modifier.height(28.dp))
|
||||||
Text(
|
Text(
|
||||||
text = "Episodes",
|
text = "Episodes",
|
||||||
color = colors.textPrimary,
|
color = scheme.onBackground,
|
||||||
fontSize = 18.sp,
|
fontSize = 18.sp,
|
||||||
fontWeight = FontWeight.Bold
|
fontWeight = FontWeight.Bold
|
||||||
)
|
)
|
||||||
@@ -123,7 +124,7 @@ private fun SeriesScreenInternal(
|
|||||||
Spacer(modifier = Modifier.height(32.dp))
|
Spacer(modifier = Modifier.height(32.dp))
|
||||||
Text(
|
Text(
|
||||||
text = "Cast",
|
text = "Cast",
|
||||||
color = colors.textPrimary,
|
color = scheme.onBackground,
|
||||||
fontSize = 18.sp,
|
fontSize = 18.sp,
|
||||||
fontWeight = FontWeight.Bold
|
fontWeight = FontWeight.Bold
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import androidx.compose.material.icons.outlined.Collections
|
|||||||
import androidx.compose.material.icons.outlined.Movie
|
import androidx.compose.material.icons.outlined.Movie
|
||||||
import androidx.compose.material.icons.outlined.Tv
|
import androidx.compose.material.icons.outlined.Tv
|
||||||
import androidx.compose.material3.DrawerValue
|
import androidx.compose.material3.DrawerValue
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.ModalDrawerSheet
|
import androidx.compose.material3.ModalDrawerSheet
|
||||||
import androidx.compose.material3.ModalNavigationDrawer
|
import androidx.compose.material3.ModalNavigationDrawer
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
@@ -23,7 +24,6 @@ import hu.bbara.purefin.app.home.ui.HomeDrawerContent
|
|||||||
import hu.bbara.purefin.app.home.ui.HomeMockData
|
import hu.bbara.purefin.app.home.ui.HomeMockData
|
||||||
import hu.bbara.purefin.app.home.ui.HomeNavItem
|
import hu.bbara.purefin.app.home.ui.HomeNavItem
|
||||||
import hu.bbara.purefin.app.home.ui.HomeTopBar
|
import hu.bbara.purefin.app.home.ui.HomeTopBar
|
||||||
import hu.bbara.purefin.app.home.ui.rememberHomeColors
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.jellyfin.sdk.model.api.CollectionType
|
import org.jellyfin.sdk.model.api.CollectionType
|
||||||
|
|
||||||
@@ -32,7 +32,6 @@ fun HomePage(
|
|||||||
viewModel: HomePageViewModel = hiltViewModel(),
|
viewModel: HomePageViewModel = hiltViewModel(),
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val colors = rememberHomeColors()
|
|
||||||
val drawerState = rememberDrawerState(DrawerValue.Closed)
|
val drawerState = rememberDrawerState(DrawerValue.Closed)
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
@@ -56,13 +55,12 @@ fun HomePage(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.width(280.dp)
|
.width(280.dp)
|
||||||
.fillMaxSize(),
|
.fillMaxSize(),
|
||||||
drawerContainerColor = colors.drawerBackground,
|
drawerContainerColor = MaterialTheme.colorScheme.surface,
|
||||||
drawerContentColor = colors.textPrimary
|
drawerContentColor = MaterialTheme.colorScheme.onBackground
|
||||||
) {
|
) {
|
||||||
HomeDrawerContent(
|
HomeDrawerContent(
|
||||||
title = "Jellyfin",
|
title = "Jellyfin",
|
||||||
subtitle = "Library Dashboard",
|
subtitle = "Library Dashboard",
|
||||||
colors = colors,
|
|
||||||
primaryNavItems = libraries,
|
primaryNavItems = libraries,
|
||||||
secondaryNavItems = HomeMockData.secondaryNavItems,
|
secondaryNavItems = HomeMockData.secondaryNavItems,
|
||||||
user = HomeMockData.user,
|
user = HomeMockData.user,
|
||||||
@@ -72,18 +70,16 @@ fun HomePage(
|
|||||||
) {
|
) {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = modifier.fillMaxSize(),
|
modifier = modifier.fillMaxSize(),
|
||||||
containerColor = colors.background,
|
containerColor = MaterialTheme.colorScheme.background,
|
||||||
contentColor = colors.textPrimary,
|
contentColor = MaterialTheme.colorScheme.onBackground,
|
||||||
topBar = {
|
topBar = {
|
||||||
HomeTopBar(
|
HomeTopBar(
|
||||||
title = "Home",
|
title = "Home",
|
||||||
colors = colors,
|
|
||||||
onMenuClick = { coroutineScope.launch { drawerState.open() } }
|
onMenuClick = { coroutineScope.launch { drawerState.open() } }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
) { innerPadding ->
|
) { innerPadding ->
|
||||||
HomeContent(
|
HomeContent(
|
||||||
colors = colors,
|
|
||||||
continueWatching = continueWatching.value,
|
continueWatching = continueWatching.value,
|
||||||
modifier = Modifier.padding(innerPadding)
|
modifier = Modifier.padding(innerPadding)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -17,7 +18,6 @@ import hu.bbara.purefin.app.home.HomePageViewModel
|
|||||||
@Composable
|
@Composable
|
||||||
fun HomeContent(
|
fun HomeContent(
|
||||||
viewModel: HomePageViewModel = hiltViewModel(),
|
viewModel: HomePageViewModel = hiltViewModel(),
|
||||||
colors: HomeColors,
|
|
||||||
continueWatching: List<ContinueWatchingItem>,
|
continueWatching: List<ContinueWatchingItem>,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
@@ -28,15 +28,14 @@ fun HomeContent(
|
|||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.background(colors.background)
|
.background(MaterialTheme.colorScheme.background)
|
||||||
) {
|
) {
|
||||||
item {
|
item {
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
}
|
}
|
||||||
item {
|
item {
|
||||||
ContinueWatchingSection(
|
ContinueWatchingSection(
|
||||||
items = continueWatching,
|
items = continueWatching
|
||||||
colors = colors
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
items(
|
items(
|
||||||
@@ -47,7 +46,6 @@ fun HomeContent(
|
|||||||
title = item.name,
|
title = item.name,
|
||||||
items = libraryContent[item.id] ?: emptyList(),
|
items = libraryContent[item.id] ?: emptyList(),
|
||||||
action = "See All",
|
action = "See All",
|
||||||
colors = colors
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
item {
|
item {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import androidx.compose.material.icons.filled.PlayArrow
|
|||||||
import androidx.compose.material.icons.outlined.Person
|
import androidx.compose.material.icons.outlined.Person
|
||||||
import androidx.compose.material3.HorizontalDivider
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@@ -32,7 +33,6 @@ import hu.bbara.purefin.app.home.HomePageViewModel
|
|||||||
fun HomeDrawerContent(
|
fun HomeDrawerContent(
|
||||||
title: String,
|
title: String,
|
||||||
subtitle: String,
|
subtitle: String,
|
||||||
colors: HomeColors,
|
|
||||||
primaryNavItems: List<HomeNavItem>,
|
primaryNavItems: List<HomeNavItem>,
|
||||||
secondaryNavItems: List<HomeNavItem>,
|
secondaryNavItems: List<HomeNavItem>,
|
||||||
user: HomeUser,
|
user: HomeUser,
|
||||||
@@ -41,16 +41,14 @@ fun HomeDrawerContent(
|
|||||||
Column(modifier = modifier.fillMaxSize()) {
|
Column(modifier = modifier.fillMaxSize()) {
|
||||||
HomeDrawerHeader(
|
HomeDrawerHeader(
|
||||||
title = title,
|
title = title,
|
||||||
subtitle = subtitle,
|
subtitle = subtitle
|
||||||
colors = colors
|
|
||||||
)
|
)
|
||||||
HomeDrawerNav(
|
HomeDrawerNav(
|
||||||
primaryItems = primaryNavItems,
|
primaryItems = primaryNavItems,
|
||||||
secondaryItems = secondaryNavItems,
|
secondaryItems = secondaryNavItems
|
||||||
colors = colors,
|
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
HomeDrawerFooter(user = user, colors = colors)
|
HomeDrawerFooter(user = user)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,9 +56,10 @@ fun HomeDrawerContent(
|
|||||||
fun HomeDrawerHeader(
|
fun HomeDrawerHeader(
|
||||||
title: String,
|
title: String,
|
||||||
subtitle: String,
|
subtitle: String,
|
||||||
colors: HomeColors,
|
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
|
val scheme = MaterialTheme.colorScheme
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -70,38 +69,37 @@ fun HomeDrawerHeader(
|
|||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(40.dp)
|
.size(40.dp)
|
||||||
.background(colors.primary, RoundedCornerShape(12.dp)),
|
.background(scheme.primary, RoundedCornerShape(12.dp)),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
horizontalArrangement = Arrangement.Center
|
horizontalArrangement = Arrangement.Center
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Filled.PlayArrow,
|
imageVector = Icons.Filled.PlayArrow,
|
||||||
contentDescription = "Play",
|
contentDescription = "Play",
|
||||||
tint = colors.onPrimary
|
tint = scheme.onPrimary
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Column(modifier = Modifier.padding(start = 12.dp)) {
|
Column(modifier = Modifier.padding(start = 12.dp)) {
|
||||||
Text(
|
Text(
|
||||||
text = title,
|
text = title,
|
||||||
color = colors.textPrimary,
|
color = scheme.onBackground,
|
||||||
fontSize = 20.sp,
|
fontSize = 20.sp,
|
||||||
fontWeight = FontWeight.Bold
|
fontWeight = FontWeight.Bold
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = subtitle,
|
text = subtitle,
|
||||||
color = colors.textSecondary,
|
color = scheme.onSurfaceVariant,
|
||||||
fontSize = 12.sp
|
fontSize = 12.sp
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
HorizontalDivider(color = colors.textSecondary.copy(alpha = 0.2f))
|
HorizontalDivider(color = scheme.onSurfaceVariant.copy(alpha = 0.2f))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun HomeDrawerNav(
|
fun HomeDrawerNav(
|
||||||
primaryItems: List<HomeNavItem>,
|
primaryItems: List<HomeNavItem>,
|
||||||
secondaryItems: List<HomeNavItem>,
|
secondaryItems: List<HomeNavItem>,
|
||||||
colors: HomeColors,
|
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
@@ -110,16 +108,16 @@ fun HomeDrawerNav(
|
|||||||
.padding(vertical = 16.dp)
|
.padding(vertical = 16.dp)
|
||||||
) {
|
) {
|
||||||
primaryItems.forEach { item ->
|
primaryItems.forEach { item ->
|
||||||
HomeDrawerNavItem(item = item, colors = colors)
|
HomeDrawerNavItem(item = item)
|
||||||
}
|
}
|
||||||
if (secondaryItems.isNotEmpty()) {
|
if (secondaryItems.isNotEmpty()) {
|
||||||
HorizontalDivider(
|
HorizontalDivider(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(horizontal = 20.dp, vertical = 12.dp),
|
.padding(horizontal = 20.dp, vertical = 12.dp),
|
||||||
color = colors.divider
|
color = MaterialTheme.colorScheme.outlineVariant
|
||||||
)
|
)
|
||||||
secondaryItems.forEach { item ->
|
secondaryItems.forEach { item ->
|
||||||
HomeDrawerNavItem(item = item, colors = colors)
|
HomeDrawerNavItem(item = item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -128,12 +126,12 @@ fun HomeDrawerNav(
|
|||||||
@Composable
|
@Composable
|
||||||
fun HomeDrawerNavItem(
|
fun HomeDrawerNavItem(
|
||||||
item: HomeNavItem,
|
item: HomeNavItem,
|
||||||
colors: HomeColors,
|
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
viewModel: HomePageViewModel = hiltViewModel(),
|
viewModel: HomePageViewModel = hiltViewModel(),
|
||||||
) {
|
) {
|
||||||
val background = if (item.selected) colors.primary.copy(alpha = 0.12f) else Color.Transparent
|
val scheme = MaterialTheme.colorScheme
|
||||||
val tint = if (item.selected) colors.primary else colors.textSecondary
|
val background = if (item.selected) scheme.primary.copy(alpha = 0.12f) else Color.Transparent
|
||||||
|
val tint = if (item.selected) scheme.primary else scheme.onSurfaceVariant
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -150,7 +148,7 @@ fun HomeDrawerNavItem(
|
|||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = item.label,
|
text = item.label,
|
||||||
color = if (item.selected) colors.primary else colors.textPrimary,
|
color = if (item.selected) scheme.primary else scheme.onBackground,
|
||||||
fontSize = 15.sp,
|
fontSize = 15.sp,
|
||||||
fontWeight = FontWeight.Medium,
|
fontWeight = FontWeight.Medium,
|
||||||
modifier = Modifier.padding(start = 12.dp)
|
modifier = Modifier.padding(start = 12.dp)
|
||||||
@@ -162,30 +160,31 @@ fun HomeDrawerNavItem(
|
|||||||
fun HomeDrawerFooter (
|
fun HomeDrawerFooter (
|
||||||
viewModel: HomePageViewModel = hiltViewModel(),
|
viewModel: HomePageViewModel = hiltViewModel(),
|
||||||
user: HomeUser,
|
user: HomeUser,
|
||||||
colors: HomeColors,
|
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
|
val scheme = MaterialTheme.colorScheme
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(16.dp)
|
.padding(16.dp)
|
||||||
.background(colors.drawerFooterBackground, RoundedCornerShape(12.dp))
|
.background(scheme.surfaceVariant, RoundedCornerShape(12.dp))
|
||||||
.padding(12.dp),
|
.padding(12.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
HomeAvatar(
|
HomeAvatar(
|
||||||
size = 32.dp,
|
size = 32.dp,
|
||||||
borderWidth = 1.dp,
|
borderWidth = 1.dp,
|
||||||
borderColor = colors.divider,
|
borderColor = scheme.outlineVariant,
|
||||||
backgroundColor = colors.avatarBackground,
|
backgroundColor = scheme.primaryContainer,
|
||||||
icon = Icons.Outlined.Person,
|
icon = Icons.Outlined.Person,
|
||||||
iconTint = colors.textPrimary
|
iconTint = scheme.onBackground
|
||||||
)
|
)
|
||||||
Column(modifier = Modifier.padding(start = 12.dp)
|
Column(modifier = Modifier.padding(start = 12.dp)
|
||||||
.clickable {viewModel.logout()}) {
|
.clickable {viewModel.logout()}) {
|
||||||
Text(
|
Text(
|
||||||
text = user.name,
|
text = user.name,
|
||||||
color = colors.textPrimary,
|
color = scheme.onBackground,
|
||||||
fontSize = 14.sp,
|
fontSize = 14.sp,
|
||||||
fontWeight = FontWeight.SemiBold,
|
fontWeight = FontWeight.SemiBold,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
@@ -193,7 +192,7 @@ fun HomeDrawerFooter (
|
|||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = user.plan,
|
text = user.plan,
|
||||||
color = colors.textSecondary,
|
color = scheme.onSurfaceVariant,
|
||||||
fontSize = 11.sp,
|
fontSize = 11.sp,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis
|
overflow = TextOverflow.Ellipsis
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import androidx.compose.material.icons.Icons
|
|||||||
import androidx.compose.material.icons.outlined.PlayArrow
|
import androidx.compose.material.icons.outlined.PlayArrow
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@@ -36,9 +37,9 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import coil3.compose.AsyncImage
|
|
||||||
import hu.bbara.purefin.app.home.HomePageViewModel
|
import hu.bbara.purefin.app.home.HomePageViewModel
|
||||||
import hu.bbara.purefin.common.ui.PosterCard
|
import hu.bbara.purefin.common.ui.PosterCard
|
||||||
|
import hu.bbara.purefin.common.ui.components.PurefinAsyncImage
|
||||||
import hu.bbara.purefin.player.PlayerActivity
|
import hu.bbara.purefin.player.PlayerActivity
|
||||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||||
import org.jellyfin.sdk.model.api.ImageType
|
import org.jellyfin.sdk.model.api.ImageType
|
||||||
@@ -46,10 +47,12 @@ import kotlin.math.nextUp
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ContinueWatchingSection(
|
fun ContinueWatchingSection(
|
||||||
items: List<ContinueWatchingItem>, colors: HomeColors, modifier: Modifier = Modifier
|
items: List<ContinueWatchingItem>,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
SectionHeader(
|
SectionHeader(
|
||||||
title = "Continue Watching", action = null, colors = colors
|
title = "Continue Watching",
|
||||||
|
action = null
|
||||||
)
|
)
|
||||||
LazyRow(
|
LazyRow(
|
||||||
modifier = modifier.fillMaxWidth(),
|
modifier = modifier.fillMaxWidth(),
|
||||||
@@ -59,7 +62,7 @@ fun ContinueWatchingSection(
|
|||||||
items(
|
items(
|
||||||
items = items, key = { it.id }) { item ->
|
items = items, key = { it.id }) { item ->
|
||||||
ContinueWatchingCard(
|
ContinueWatchingCard(
|
||||||
item = item, colors = colors
|
item = item
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -68,10 +71,11 @@ fun ContinueWatchingSection(
|
|||||||
@Composable
|
@Composable
|
||||||
fun ContinueWatchingCard(
|
fun ContinueWatchingCard(
|
||||||
item: ContinueWatchingItem,
|
item: ContinueWatchingItem,
|
||||||
colors: HomeColors,
|
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
viewModel: HomePageViewModel = hiltViewModel()
|
viewModel: HomePageViewModel = hiltViewModel()
|
||||||
) {
|
) {
|
||||||
|
val scheme = MaterialTheme.colorScheme
|
||||||
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
fun openItem(item: ContinueWatchingItem) {
|
fun openItem(item: ContinueWatchingItem) {
|
||||||
@@ -92,9 +96,9 @@ fun ContinueWatchingCard(
|
|||||||
.aspectRatio(16f / 9f)
|
.aspectRatio(16f / 9f)
|
||||||
.shadow(12.dp, RoundedCornerShape(16.dp))
|
.shadow(12.dp, RoundedCornerShape(16.dp))
|
||||||
.clip(RoundedCornerShape(16.dp))
|
.clip(RoundedCornerShape(16.dp))
|
||||||
.background(colors.card)
|
.background(scheme.surfaceVariant)
|
||||||
) {
|
) {
|
||||||
AsyncImage(
|
PurefinAsyncImage(
|
||||||
model = viewModel.getImageUrl(itemId = item.id, type = ImageType.PRIMARY),
|
model = viewModel.getImageUrl(itemId = item.id, type = ImageType.PRIMARY),
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -103,25 +107,24 @@ fun ContinueWatchingCard(
|
|||||||
openItem(item)
|
openItem(item)
|
||||||
},
|
},
|
||||||
contentScale = ContentScale.Crop,
|
contentScale = ContentScale.Crop,
|
||||||
|
)
|
||||||
)
|
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.matchParentSize()
|
.matchParentSize()
|
||||||
.background(colors.textPrimary.copy(alpha = 0.2f))
|
.background(scheme.onBackground.copy(alpha = 0.2f))
|
||||||
)
|
)
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(Alignment.BottomStart)
|
.align(Alignment.BottomStart)
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.height(4.dp)
|
.height(4.dp)
|
||||||
.background(colors.textPrimary.copy(alpha = 0.2f))
|
.background(scheme.onBackground.copy(alpha = 0.2f))
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxHeight()
|
.fillMaxHeight()
|
||||||
.fillMaxWidth(item.progress.toFloat().nextUp().div(100))
|
.fillMaxWidth(item.progress.toFloat().nextUp().div(100))
|
||||||
.background(colors.primary)
|
.background(scheme.primary)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Button(
|
Button(
|
||||||
@@ -136,7 +139,7 @@ fun ContinueWatchingCard(
|
|||||||
Column(modifier = Modifier.padding(top = 12.dp)) {
|
Column(modifier = Modifier.padding(top = 12.dp)) {
|
||||||
Text(
|
Text(
|
||||||
text = item.primaryText,
|
text = item.primaryText,
|
||||||
color = colors.textPrimary,
|
color = scheme.onBackground,
|
||||||
fontSize = 16.sp,
|
fontSize = 16.sp,
|
||||||
fontWeight = FontWeight.SemiBold,
|
fontWeight = FontWeight.SemiBold,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
@@ -144,7 +147,7 @@ fun ContinueWatchingCard(
|
|||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = item.secondaryText,
|
text = item.secondaryText,
|
||||||
color = colors.textSecondary,
|
color = scheme.onSurfaceVariant,
|
||||||
fontSize = 13.sp,
|
fontSize = 13.sp,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis
|
overflow = TextOverflow.Ellipsis
|
||||||
@@ -158,11 +161,11 @@ fun LibraryPosterSection(
|
|||||||
title: String,
|
title: String,
|
||||||
items: List<PosterItem>,
|
items: List<PosterItem>,
|
||||||
action: String?,
|
action: String?,
|
||||||
colors: HomeColors,
|
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
SectionHeader(
|
SectionHeader(
|
||||||
title = title, action = action, colors = colors
|
title = title,
|
||||||
|
action = action
|
||||||
)
|
)
|
||||||
LazyRow(
|
LazyRow(
|
||||||
modifier = modifier.fillMaxWidth(),
|
modifier = modifier.fillMaxWidth(),
|
||||||
@@ -173,7 +176,6 @@ fun LibraryPosterSection(
|
|||||||
items = items, key = { it.id }) { item ->
|
items = items, key = { it.id }) { item ->
|
||||||
PosterCard(
|
PosterCard(
|
||||||
item = item,
|
item = item,
|
||||||
colors = colors,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -183,10 +185,11 @@ fun LibraryPosterSection(
|
|||||||
fun SectionHeader(
|
fun SectionHeader(
|
||||||
title: String,
|
title: String,
|
||||||
action: String?,
|
action: String?,
|
||||||
colors: HomeColors,
|
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
onActionClick: () -> Unit = {}
|
onActionClick: () -> Unit = {}
|
||||||
) {
|
) {
|
||||||
|
val scheme = MaterialTheme.colorScheme
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -195,12 +198,15 @@ fun SectionHeader(
|
|||||||
horizontalArrangement = Arrangement.SpaceBetween
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = title, color = colors.textPrimary, fontSize = 20.sp, fontWeight = FontWeight.Bold
|
text = title,
|
||||||
|
color = scheme.onBackground,
|
||||||
|
fontSize = 20.sp,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
)
|
)
|
||||||
if (action != null) {
|
if (action != null) {
|
||||||
Text(
|
Text(
|
||||||
text = action,
|
text = action,
|
||||||
color = colors.primary,
|
color = scheme.primary,
|
||||||
fontSize = 14.sp,
|
fontSize = 14.sp,
|
||||||
fontWeight = FontWeight.SemiBold,
|
fontWeight = FontWeight.SemiBold,
|
||||||
modifier = Modifier.clickable { onActionClick() })
|
modifier = Modifier.clickable { onActionClick() })
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
package hu.bbara.purefin.app.home.ui
|
|
||||||
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
|
|
||||||
data class HomeColors(
|
|
||||||
val primary: Color,
|
|
||||||
val onPrimary: Color,
|
|
||||||
val background: Color,
|
|
||||||
val drawerBackground: Color,
|
|
||||||
val card: Color,
|
|
||||||
val textPrimary: Color,
|
|
||||||
val textSecondary: Color,
|
|
||||||
val divider: Color,
|
|
||||||
val avatarBackground: Color,
|
|
||||||
val avatarBorder: Color,
|
|
||||||
val drawerFooterBackground: Color
|
|
||||||
)
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun rememberHomeColors(): HomeColors {
|
|
||||||
val scheme = MaterialTheme.colorScheme
|
|
||||||
return remember(scheme) {
|
|
||||||
HomeColors(
|
|
||||||
primary = scheme.primary,
|
|
||||||
onPrimary = scheme.onPrimary,
|
|
||||||
background = scheme.background,
|
|
||||||
drawerBackground = scheme.surface,
|
|
||||||
card = scheme.surfaceVariant,
|
|
||||||
textPrimary = scheme.onBackground,
|
|
||||||
textSecondary = scheme.onSurfaceVariant,
|
|
||||||
divider = scheme.outlineVariant,
|
|
||||||
avatarBackground = scheme.primaryContainer,
|
|
||||||
avatarBorder = scheme.outline,
|
|
||||||
drawerFooterBackground = scheme.surfaceVariant
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -15,6 +15,7 @@ import androidx.compose.material.icons.outlined.Refresh
|
|||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@@ -30,24 +31,25 @@ import hu.bbara.purefin.app.home.HomePageViewModel
|
|||||||
fun HomeTopBar(
|
fun HomeTopBar(
|
||||||
viewModel: HomePageViewModel = hiltViewModel(),
|
viewModel: HomePageViewModel = hiltViewModel(),
|
||||||
title: String,
|
title: String,
|
||||||
colors: HomeColors,
|
|
||||||
onMenuClick: () -> Unit,
|
onMenuClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
actions: @Composable RowScope.() -> Unit = {
|
actions: @Composable RowScope.() -> Unit = {
|
||||||
HomeAvatar(
|
HomeAvatar(
|
||||||
size = 36.dp,
|
size = 36.dp,
|
||||||
borderWidth = 2.dp,
|
borderWidth = 2.dp,
|
||||||
borderColor = colors.avatarBorder,
|
borderColor = MaterialTheme.colorScheme.outline,
|
||||||
backgroundColor = colors.avatarBackground,
|
backgroundColor = MaterialTheme.colorScheme.primaryContainer,
|
||||||
icon = Icons.Outlined.Person,
|
icon = Icons.Outlined.Person,
|
||||||
iconTint = colors.onPrimary
|
iconTint = MaterialTheme.colorScheme.onPrimary
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
|
val scheme = MaterialTheme.colorScheme
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.background(colors.background.copy(alpha = 0.95f))
|
.background(scheme.background.copy(alpha = 0.95f))
|
||||||
.zIndex(1f)
|
.zIndex(1f)
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
@@ -63,12 +65,12 @@ fun HomeTopBar(
|
|||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Outlined.Menu,
|
imageVector = Icons.Outlined.Menu,
|
||||||
contentDescription = "Menu",
|
contentDescription = "Menu",
|
||||||
tint = colors.textPrimary
|
tint = scheme.onBackground
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Text(
|
Text(
|
||||||
text = title,
|
text = title,
|
||||||
color = colors.textPrimary,
|
color = scheme.onBackground,
|
||||||
fontSize = 20.sp,
|
fontSize = 20.sp,
|
||||||
fontWeight = FontWeight.Bold
|
fontWeight = FontWeight.Bold
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
|||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.Person
|
import androidx.compose.material.icons.outlined.Person
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@@ -36,94 +37,39 @@ import androidx.compose.ui.unit.Dp
|
|||||||
import androidx.compose.ui.unit.TextUnit
|
import androidx.compose.ui.unit.TextUnit
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import coil3.compose.AsyncImage
|
import hu.bbara.purefin.common.ui.components.PurefinAsyncImage
|
||||||
import hu.bbara.purefin.app.content.episode.EpisodeColors
|
|
||||||
import hu.bbara.purefin.app.content.movie.MovieColors
|
|
||||||
import hu.bbara.purefin.app.content.series.SeriesColors
|
|
||||||
|
|
||||||
data class MediaDetailColors(
|
|
||||||
val primary: Color,
|
|
||||||
val onPrimary: Color,
|
|
||||||
val background: Color,
|
|
||||||
val surface: Color,
|
|
||||||
val surfaceAlt: Color,
|
|
||||||
val surfaceBorder: Color,
|
|
||||||
val textPrimary: Color,
|
|
||||||
val textSecondary: Color,
|
|
||||||
val textMuted: Color,
|
|
||||||
val textMutedStrong: Color
|
|
||||||
)
|
|
||||||
|
|
||||||
internal fun MovieColors.toMediaDetailColors() = MediaDetailColors(
|
|
||||||
primary = primary,
|
|
||||||
onPrimary = onPrimary,
|
|
||||||
background = background,
|
|
||||||
surface = surface,
|
|
||||||
surfaceAlt = surfaceAlt,
|
|
||||||
surfaceBorder = surfaceBorder,
|
|
||||||
textPrimary = textPrimary,
|
|
||||||
textSecondary = textSecondary,
|
|
||||||
textMuted = textMuted,
|
|
||||||
textMutedStrong = textMutedStrong
|
|
||||||
)
|
|
||||||
|
|
||||||
internal fun EpisodeColors.toMediaDetailColors() = MediaDetailColors(
|
|
||||||
primary = primary,
|
|
||||||
onPrimary = onPrimary,
|
|
||||||
background = background,
|
|
||||||
surface = surface,
|
|
||||||
surfaceAlt = surfaceAlt,
|
|
||||||
surfaceBorder = surfaceBorder,
|
|
||||||
textPrimary = textPrimary,
|
|
||||||
textSecondary = textSecondary,
|
|
||||||
textMuted = textMuted,
|
|
||||||
textMutedStrong = textMutedStrong
|
|
||||||
)
|
|
||||||
|
|
||||||
internal fun SeriesColors.toMediaDetailColors() = MediaDetailColors(
|
|
||||||
primary = primary,
|
|
||||||
onPrimary = onPrimary,
|
|
||||||
background = background,
|
|
||||||
surface = surface,
|
|
||||||
surfaceAlt = surfaceAlt,
|
|
||||||
surfaceBorder = surfaceBorder,
|
|
||||||
textPrimary = textPrimary,
|
|
||||||
textSecondary = textSecondary,
|
|
||||||
textMuted = textMuted,
|
|
||||||
textMutedStrong = textMutedStrong
|
|
||||||
)
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MediaGhostIconButton(
|
fun MediaGhostIconButton(
|
||||||
colors: MediaDetailColors,
|
|
||||||
icon: ImageVector,
|
icon: ImageVector,
|
||||||
contentDescription: String,
|
contentDescription: String,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
|
val scheme = MaterialTheme.colorScheme
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.size(40.dp)
|
.size(40.dp)
|
||||||
.clip(CircleShape)
|
.clip(CircleShape)
|
||||||
.background(colors.background.copy(alpha = 0.4f))
|
.background(scheme.background.copy(alpha = 0.4f))
|
||||||
.clickable { onClick() },
|
.clickable { onClick() },
|
||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = icon,
|
imageVector = icon,
|
||||||
contentDescription = contentDescription,
|
contentDescription = contentDescription,
|
||||||
tint = colors.textPrimary
|
tint = scheme.onBackground
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MediaMetaChip(
|
fun MediaMetaChip(
|
||||||
colors: MediaDetailColors,
|
|
||||||
text: String,
|
text: String,
|
||||||
background: Color = colors.surfaceAlt,
|
background: Color = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
border: Color = Color.Transparent,
|
border: Color = Color.Transparent,
|
||||||
textColor: Color = colors.textSecondary,
|
textColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
@@ -153,13 +99,15 @@ data class MediaCastMember(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MediaCastRow(
|
fun MediaCastRow(
|
||||||
colors: MediaDetailColors,
|
|
||||||
cast: List<MediaCastMember>,
|
cast: List<MediaCastMember>,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
cardWidth: Dp = 96.dp,
|
cardWidth: Dp = 96.dp,
|
||||||
nameSize: TextUnit = 12.sp,
|
nameSize: TextUnit = 12.sp,
|
||||||
roleSize: TextUnit = 10.sp
|
roleSize: TextUnit = 10.sp
|
||||||
) {
|
) {
|
||||||
|
val scheme = MaterialTheme.colorScheme
|
||||||
|
val mutedStrong = scheme.onSurfaceVariant.copy(alpha = 0.7f)
|
||||||
|
|
||||||
LazyRow(
|
LazyRow(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
contentPadding = PaddingValues(horizontal = 4.dp),
|
contentPadding = PaddingValues(horizontal = 4.dp),
|
||||||
@@ -171,23 +119,23 @@ fun MediaCastRow(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.aspectRatio(4f / 5f)
|
.aspectRatio(4f / 5f)
|
||||||
.clip(RoundedCornerShape(12.dp))
|
.clip(RoundedCornerShape(12.dp))
|
||||||
.background(colors.surfaceAlt)
|
.background(scheme.surfaceVariant)
|
||||||
) {
|
) {
|
||||||
if (member.imageUrl == null) {
|
if (member.imageUrl == null) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.background(colors.surfaceAlt.copy(alpha = 0.6f)),
|
.background(scheme.surfaceVariant.copy(alpha = 0.6f)),
|
||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Outlined.Person,
|
imageVector = Icons.Outlined.Person,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
tint = colors.textMutedStrong
|
tint = mutedStrong
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
AsyncImage(
|
PurefinAsyncImage(
|
||||||
model = member.imageUrl,
|
model = member.imageUrl,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
@@ -198,7 +146,7 @@ fun MediaCastRow(
|
|||||||
Spacer(modifier = Modifier.height(6.dp))
|
Spacer(modifier = Modifier.height(6.dp))
|
||||||
Text(
|
Text(
|
||||||
text = member.name,
|
text = member.name,
|
||||||
color = colors.textPrimary,
|
color = scheme.onBackground,
|
||||||
fontSize = nameSize,
|
fontSize = nameSize,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
@@ -206,7 +154,7 @@ fun MediaCastRow(
|
|||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = member.role,
|
text = member.role,
|
||||||
color = colors.textMutedStrong,
|
color = mutedStrong,
|
||||||
fontSize = roleSize,
|
fontSize = roleSize,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis
|
overflow = TextOverflow.Ellipsis
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.aspectRatio
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -18,21 +19,20 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import coil3.compose.AsyncImage
|
|
||||||
import hu.bbara.purefin.app.home.HomePageViewModel
|
import hu.bbara.purefin.app.home.HomePageViewModel
|
||||||
import hu.bbara.purefin.app.home.ui.HomeColors
|
|
||||||
import hu.bbara.purefin.app.home.ui.PosterItem
|
import hu.bbara.purefin.app.home.ui.PosterItem
|
||||||
import hu.bbara.purefin.app.home.ui.rememberHomeColors
|
import hu.bbara.purefin.common.ui.components.PurefinAsyncImage
|
||||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||||
import org.jellyfin.sdk.model.api.ImageType
|
import org.jellyfin.sdk.model.api.ImageType
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PosterCard(
|
fun PosterCard(
|
||||||
item: PosterItem,
|
item: PosterItem,
|
||||||
colors: HomeColors = rememberHomeColors(),
|
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
viewModel: HomePageViewModel = hiltViewModel()
|
viewModel: HomePageViewModel = hiltViewModel()
|
||||||
) {
|
) {
|
||||||
|
val scheme = MaterialTheme.colorScheme
|
||||||
|
|
||||||
fun openItem(posterItem: PosterItem) {
|
fun openItem(posterItem: PosterItem) {
|
||||||
when (posterItem.type) {
|
when (posterItem.type) {
|
||||||
BaseItemKind.MOVIE -> viewModel.onMovieSelected(posterItem.id.toString())
|
BaseItemKind.MOVIE -> viewModel.onMovieSelected(posterItem.id.toString())
|
||||||
@@ -45,20 +45,20 @@ fun PosterCard(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.width(144.dp)
|
.width(144.dp)
|
||||||
) {
|
) {
|
||||||
AsyncImage(
|
PurefinAsyncImage(
|
||||||
model = viewModel.getImageUrl(item.imageItemId, ImageType.PRIMARY),
|
model = viewModel.getImageUrl(item.imageItemId, ImageType.PRIMARY),
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.aspectRatio(2f / 3f)
|
.aspectRatio(2f / 3f)
|
||||||
.shadow(10.dp, RoundedCornerShape(14.dp))
|
.shadow(10.dp, RoundedCornerShape(14.dp))
|
||||||
.clip(RoundedCornerShape(14.dp))
|
.clip(RoundedCornerShape(14.dp))
|
||||||
.background(colors.card)
|
.background(scheme.surfaceVariant)
|
||||||
.clickable(onClick = { openItem(item) }),
|
.clickable(onClick = { openItem(item) }),
|
||||||
contentScale = ContentScale.Crop
|
contentScale = ContentScale.Crop
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = item.title,
|
text = item.title,
|
||||||
color = colors.textPrimary,
|
color = scheme.onBackground,
|
||||||
fontSize = 13.sp,
|
fontSize = 13.sp,
|
||||||
fontWeight = FontWeight.Medium,
|
fontWeight = FontWeight.Medium,
|
||||||
modifier = Modifier.padding(top = 8.dp, start = 4.dp, end = 4.dp, bottom = 8.dp),
|
modifier = Modifier.padding(top = 8.dp, start = 4.dp, end = 4.dp, bottom = 8.dp),
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import androidx.compose.ui.graphics.Brush
|
|||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import coil3.compose.AsyncImage
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MediaHero(
|
fun MediaHero(
|
||||||
@@ -24,7 +23,7 @@ fun MediaHero(
|
|||||||
.height(height)
|
.height(height)
|
||||||
.background(backgroundColor)
|
.background(backgroundColor)
|
||||||
) {
|
) {
|
||||||
AsyncImage(
|
PurefinAsyncImage(
|
||||||
model = imageUrl,
|
model = imageUrl,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package hu.bbara.purefin.common.ui.components
|
||||||
|
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.painter.ColorPainter
|
||||||
|
import androidx.compose.ui.layout.ContentScale
|
||||||
|
import coil3.compose.AsyncImage
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Async image that falls back to theme-synced color blocks so loading/error states
|
||||||
|
* stay aligned with PurefinTheme's colorScheme.
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun PurefinAsyncImage(
|
||||||
|
model: Any?,
|
||||||
|
contentDescription: String?,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
contentScale: ContentScale = ContentScale.Crop
|
||||||
|
) {
|
||||||
|
val placeholderPainter = ColorPainter(MaterialTheme.colorScheme.surfaceVariant)
|
||||||
|
|
||||||
|
AsyncImage(
|
||||||
|
model = model,
|
||||||
|
contentDescription = contentDescription,
|
||||||
|
modifier = modifier,
|
||||||
|
contentScale = contentScale,
|
||||||
|
placeholder = placeholderPainter,
|
||||||
|
error = placeholderPainter,
|
||||||
|
fallback = placeholderPainter
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -24,7 +24,7 @@ class PlayerActivity : ComponentActivity() {
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
PurefinTheme {
|
PurefinTheme(darkTheme = false) {
|
||||||
val viewModel = hiltViewModel<PlayerViewModel>()
|
val viewModel = hiltViewModel<PlayerViewModel>()
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier.fillMaxSize()
|
modifier = Modifier.fillMaxSize()
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ private val DarkColorScheme = darkColorScheme(
|
|||||||
@Composable
|
@Composable
|
||||||
fun PurefinTheme(
|
fun PurefinTheme(
|
||||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||||
dynamicColor: Boolean = true,
|
dynamicColor: Boolean = false,
|
||||||
content: @Composable () -> Unit
|
content: @Composable () -> Unit
|
||||||
) {
|
) {
|
||||||
val colorScheme = when {
|
val colorScheme = when {
|
||||||
|
|||||||
Reference in New Issue
Block a user