From d2f5f8547a77ac1f3bfb02692cd3e7d660cb69a9 Mon Sep 17 00:00:00 2001 From: Barnabas Balogh Date: Wed, 21 Jan 2026 20:48:53 +0100 Subject: [PATCH] refactor: Extract and generalize MediaHero component. Now MovieScreen, SeriesScreen and EpisodeScreen also use the same Component. --- .../app/content/episode/EpisodeComponents.kt | 43 +++-------- .../app/content/episode/EpisodeScreen.kt | 4 +- .../app/content/movie/MovieComponents.kt | 48 ++++-------- .../app/content/series/SeriesComponents.kt | 8 +- .../common/ui/MediaDetailComponents.kt | 73 +------------------ .../purefin/common/ui/components/MediaHero.kt | 59 +++++++++++++++ 6 files changed, 94 insertions(+), 141 deletions(-) create mode 100644 app/src/main/java/hu/bbara/purefin/common/ui/components/MediaHero.kt diff --git a/app/src/main/java/hu/bbara/purefin/app/content/episode/EpisodeComponents.kt b/app/src/main/java/hu/bbara/purefin/app/content/episode/EpisodeComponents.kt index 680e88a..7ef2d40 100644 --- a/app/src/main/java/hu/bbara/purefin/app/content/episode/EpisodeComponents.kt +++ b/app/src/main/java/hu/bbara/purefin/app/content/episode/EpisodeComponents.kt @@ -27,9 +27,9 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel @@ -38,9 +38,9 @@ import hu.bbara.purefin.common.ui.MediaCastMember import hu.bbara.purefin.common.ui.MediaCastRow import hu.bbara.purefin.common.ui.MediaFloatingPlayButton import hu.bbara.purefin.common.ui.MediaGhostIconButton -import hu.bbara.purefin.common.ui.MediaHero import hu.bbara.purefin.common.ui.MediaMetaChip import hu.bbara.purefin.common.ui.MediaPlaybackSettings +import hu.bbara.purefin.common.ui.components.MediaHero import hu.bbara.purefin.common.ui.toMediaDetailColors import hu.bbara.purefin.player.PlayerActivity @@ -68,27 +68,6 @@ internal fun EpisodeTopBar( } } -@Composable -internal fun EpisodeHero( - episode: EpisodeUiModel, - height: Dp, - isWide: Boolean, - onPlayClick: () -> Unit, - modifier: Modifier = Modifier -) { - val colors = rememberEpisodeColors().toMediaDetailColors() - MediaHero( - imageUrl = episode.heroImageUrl, - colors = colors, - height = height, - isWide = isWide, - modifier = modifier, - showPlayButton = true, - playButtonSize = if (isWide) 96.dp else 80.dp, - onPlayClick = onPlayClick - ) -} - @OptIn(ExperimentalLayoutApi::class) @Composable internal fun EpisodeDetails( @@ -164,6 +143,7 @@ internal fun EpisodeDetails( @Composable fun EpisodeCard( episode: EpisodeUiModel, + backGroundColor: Color, modifier: Modifier = Modifier, ) { val colors = rememberEpisodeColors().toMediaDetailColors() @@ -187,11 +167,10 @@ fun EpisodeCard( Box(modifier = Modifier.fillMaxSize()) { if (isWide) { Row(modifier = Modifier.fillMaxSize()) { - EpisodeHero( - episode = episode, + MediaHero( + imageUrl = episode.heroImageUrl, height = 300.dp, - isWide = true, - onPlayClick = playAction, + backgroundColor = backGroundColor, modifier = Modifier .fillMaxHeight() .weight(0.5f) @@ -216,11 +195,10 @@ fun EpisodeCard( .fillMaxSize() .verticalScroll(rememberScrollState()) ) { - EpisodeHero( - episode = episode, + MediaHero( + imageUrl = episode.heroImageUrl, + backgroundColor = backGroundColor, height = 400.dp, - isWide = false, - onPlayClick = playAction, modifier = Modifier.fillMaxWidth() ) EpisodeDetails( @@ -242,7 +220,8 @@ fun EpisodeCard( if (!isWide) { MediaFloatingPlayButton( - colors = colors, + containerColor = colors.primary, + onContainerColor = colors.onPrimary, onClick = playAction, modifier = Modifier .align(Alignment.BottomEnd) diff --git a/app/src/main/java/hu/bbara/purefin/app/content/episode/EpisodeScreen.kt b/app/src/main/java/hu/bbara/purefin/app/content/episode/EpisodeScreen.kt index ca27be3..f3e5059 100644 --- a/app/src/main/java/hu/bbara/purefin/app/content/episode/EpisodeScreen.kt +++ b/app/src/main/java/hu/bbara/purefin/app/content/episode/EpisodeScreen.kt @@ -1,5 +1,6 @@ package hu.bbara.purefin.app.content.episode +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState @@ -24,7 +25,8 @@ fun EpisodeScreen( if (episode.value != null) { EpisodeCard( episode = episode.value!!, - modifier = modifier + modifier = modifier, + backGroundColor = MaterialTheme.colorScheme.background ) } else { PurefinWaitingScreen() diff --git a/app/src/main/java/hu/bbara/purefin/app/content/movie/MovieComponents.kt b/app/src/main/java/hu/bbara/purefin/app/content/movie/MovieComponents.kt index 2cf3054..e48d38f 100644 --- a/app/src/main/java/hu/bbara/purefin/app/content/movie/MovieComponents.kt +++ b/app/src/main/java/hu/bbara/purefin/app/content/movie/MovieComponents.kt @@ -22,6 +22,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.ArrowBack import androidx.compose.material.icons.outlined.Cast import androidx.compose.material.icons.outlined.MoreVert +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.remember @@ -29,7 +30,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel @@ -38,9 +38,9 @@ import hu.bbara.purefin.common.ui.MediaCastMember import hu.bbara.purefin.common.ui.MediaCastRow import hu.bbara.purefin.common.ui.MediaFloatingPlayButton import hu.bbara.purefin.common.ui.MediaGhostIconButton -import hu.bbara.purefin.common.ui.MediaHero import hu.bbara.purefin.common.ui.MediaMetaChip import hu.bbara.purefin.common.ui.MediaPlaybackSettings +import hu.bbara.purefin.common.ui.components.MediaHero import hu.bbara.purefin.common.ui.toMediaDetailColors import hu.bbara.purefin.player.PlayerActivity @@ -68,27 +68,6 @@ internal fun MovieTopBar( } } -@Composable -internal fun MovieHero( - movie: MovieUiModel, - height: Dp, - isWide: Boolean, - onPlayClick: () -> Unit, - modifier: Modifier = Modifier -) { - val colors = rememberMovieColors().toMediaDetailColors() - MediaHero( - imageUrl = movie.heroImageUrl, - colors = colors, - height = height, - isWide = isWide, - modifier = modifier, - showPlayButton = true, - playButtonSize = if (isWide) 96.dp else 80.dp, - onPlayClick = onPlayClick - ) -} - @OptIn(ExperimentalLayoutApi::class) @Composable internal fun MovieDetails( @@ -161,12 +140,13 @@ internal fun MovieDetails( } } + + @Composable fun MovieCard( movie: MovieUiModel, modifier: Modifier = Modifier, ) { - val colors = rememberMovieColors().toMediaDetailColors() val context = LocalContext.current val playAction = remember(movie.id) { { @@ -179,7 +159,7 @@ fun MovieCard( BoxWithConstraints( modifier = modifier .fillMaxSize() - .background(colors.background) + .background(MaterialTheme.colorScheme.background) ) { val isWide = maxWidth >= 900.dp val contentPadding = if (isWide) 32.dp else 20.dp @@ -187,11 +167,10 @@ fun MovieCard( Box(modifier = Modifier.fillMaxSize()) { if (isWide) { Row(modifier = Modifier.fillMaxSize()) { - MovieHero( - movie = movie, + MediaHero( + imageUrl = movie.heroImageUrl, + backgroundColor = MaterialTheme.colorScheme.background, height = 300.dp, - isWide = true, - onPlayClick = playAction, modifier = Modifier .fillMaxHeight() .weight(0.5f) @@ -216,11 +195,10 @@ fun MovieCard( .fillMaxSize() .verticalScroll(rememberScrollState()) ) { - MovieHero( - movie = movie, + MediaHero( + imageUrl = movie.heroImageUrl, height = 400.dp, - isWide = false, - onPlayClick = playAction, + backgroundColor = MaterialTheme.colorScheme.background, modifier = Modifier.fillMaxWidth() ) MovieDetails( @@ -242,7 +220,9 @@ fun MovieCard( if (!isWide) { MediaFloatingPlayButton( - colors = colors, + containerColor = MaterialTheme.colorScheme.primary, + onContainerColor = MaterialTheme.colorScheme.onPrimary, + onClick = playAction, modifier = Modifier .align(Alignment.BottomEnd) diff --git a/app/src/main/java/hu/bbara/purefin/app/content/series/SeriesComponents.kt b/app/src/main/java/hu/bbara/purefin/app/content/series/SeriesComponents.kt index 6a189b4..d2d64a3 100644 --- a/app/src/main/java/hu/bbara/purefin/app/content/series/SeriesComponents.kt +++ b/app/src/main/java/hu/bbara/purefin/app/content/series/SeriesComponents.kt @@ -29,6 +29,7 @@ import androidx.compose.material.icons.outlined.Cast import androidx.compose.material.icons.outlined.MoreVert import androidx.compose.material.icons.outlined.PlayCircle import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -47,8 +48,8 @@ import hu.bbara.purefin.common.ui.MediaActionButtons import hu.bbara.purefin.common.ui.MediaCastMember import hu.bbara.purefin.common.ui.MediaCastRow import hu.bbara.purefin.common.ui.MediaGhostIconButton -import hu.bbara.purefin.common.ui.MediaHero import hu.bbara.purefin.common.ui.MediaMetaChip +import hu.bbara.purefin.common.ui.components.MediaHero import hu.bbara.purefin.common.ui.toMediaDetailColors @Composable @@ -83,12 +84,9 @@ internal fun SeriesHero( val colors = rememberSeriesColors().toMediaDetailColors() MediaHero( imageUrl = imageUrl, - colors = colors, + backgroundColor = MaterialTheme.colorScheme.background, height = height, - isWide = false, modifier = modifier, - showPlayButton = false, - horizontalGradientOnWide = false ) } diff --git a/app/src/main/java/hu/bbara/purefin/common/ui/MediaDetailComponents.kt b/app/src/main/java/hu/bbara/purefin/common/ui/MediaDetailComponents.kt index 41e7a54..4f75b58 100644 --- a/app/src/main/java/hu/bbara/purefin/common/ui/MediaDetailComponents.kt +++ b/app/src/main/java/hu/bbara/purefin/common/ui/MediaDetailComponents.kt @@ -37,7 +37,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.shadow -import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.layout.ContentScale @@ -128,71 +127,6 @@ fun MediaGhostIconButton( } } -@Composable -fun MediaHero( - imageUrl: String, - colors: MediaDetailColors, - height: Dp, - isWide: Boolean, - modifier: Modifier = Modifier, - showPlayButton: Boolean = false, - playButtonSize: Dp = 80.dp, - onPlayClick: (() -> Unit)? = null, - horizontalGradientOnWide: Boolean = true -) { - Box( - modifier = modifier - .height(height) - .background(colors.background) - ) { - AsyncImage( - model = imageUrl, - contentDescription = null, - modifier = Modifier.fillMaxSize(), - contentScale = ContentScale.Crop - ) - Box( - modifier = Modifier - .matchParentSize() - .background( - Brush.verticalGradient( - colors = listOf( - Color.Transparent, - colors.background.copy(alpha = 0.4f), - colors.background - ) - ) - ) - ) - if (horizontalGradientOnWide && isWide) { - Box( - modifier = Modifier - .matchParentSize() - .background( - Brush.horizontalGradient( - colors = listOf( - Color.Transparent, - colors.background.copy(alpha = 0.8f) - ) - ) - ) - ) - } - if (showPlayButton && onPlayClick != null) { - Box( - modifier = Modifier.fillMaxSize(), - contentAlignment = Alignment.Center - ) { - MediaPlayButton( - colors = colors, - size = playButtonSize, - onClick = onPlayClick - ) - } - } - } -} - @Composable fun MediaMetaChip( colors: MediaDetailColors, @@ -469,7 +403,8 @@ fun MediaPlayButton( @Composable fun MediaFloatingPlayButton( - colors: MediaDetailColors, + containerColor: Color, + onContainerColor: Color, onClick: () -> Unit, modifier: Modifier = Modifier ) { @@ -478,14 +413,14 @@ fun MediaFloatingPlayButton( .size(56.dp) .shadow(20.dp, CircleShape) .clip(CircleShape) - .background(colors.primary) + .background(containerColor) .clickable { onClick() }, contentAlignment = Alignment.Center ) { Icon( imageVector = Icons.Filled.PlayArrow, contentDescription = "Play", - tint = colors.onPrimary + tint = onContainerColor ) } } diff --git a/app/src/main/java/hu/bbara/purefin/common/ui/components/MediaHero.kt b/app/src/main/java/hu/bbara/purefin/common/ui/components/MediaHero.kt new file mode 100644 index 0000000..1cd8078 --- /dev/null +++ b/app/src/main/java/hu/bbara/purefin/common/ui/components/MediaHero.kt @@ -0,0 +1,59 @@ +package hu.bbara.purefin.common.ui.components + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.unit.Dp +import coil3.compose.AsyncImage + +@Composable +fun MediaHero( + imageUrl: String, + backgroundColor: Color, + height: Dp, + modifier: Modifier = Modifier, +) { + Box( + modifier = modifier + .height(height) + .background(backgroundColor) + ) { + AsyncImage( + model = imageUrl, + contentDescription = null, + modifier = Modifier.fillMaxSize(), + contentScale = ContentScale.Crop + ) + Box( + modifier = Modifier + .matchParentSize() + .background( + Brush.verticalGradient( + colors = listOf( + Color.Transparent, + backgroundColor.copy(alpha = 0.4f), + backgroundColor + ) + ) + ) + ) + Box( + modifier = Modifier + .matchParentSize() + .background( + Brush.horizontalGradient( + colors = listOf( + Color.Transparent, + backgroundColor.copy(alpha = 0.8f) + ) + ) + ) + ) + } +} \ No newline at end of file