mirror of
https://github.com/bbara04/Purefin.git
synced 2026-07-23 19:26:50 +00:00
refactor: extract common MediaComponents screen layout skeleton into its own component.
This commit is contained in:
@@ -0,0 +1,97 @@
|
|||||||
|
package hu.bbara.purefin.ui.common.media
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.ColumnScope
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
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 androidx.compose.ui.unit.dp
|
||||||
|
import hu.bbara.purefin.ui.common.image.PurefinAsyncImage
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun MediaDetailScaffold(
|
||||||
|
imageUrl: String,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
imageHeight: Dp = 320.dp,
|
||||||
|
contentOverlap: Dp = 24.dp,
|
||||||
|
contentSpacing: Dp = 16.dp,
|
||||||
|
topBar: @Composable () -> Unit = {},
|
||||||
|
heroContent: @Composable ColumnScope.() -> Unit,
|
||||||
|
content: @Composable ColumnScope.() -> Unit
|
||||||
|
) {
|
||||||
|
val scheme = MaterialTheme.colorScheme
|
||||||
|
val isHomeMediaTransitionActive = isHomeMediaSharedBoundsTransitionActive()
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(scheme.background)
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.homeMediaSharedBoundsDestination()
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(imageHeight)
|
||||||
|
) {
|
||||||
|
PurefinAsyncImage(
|
||||||
|
model = imageUrl,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
contentScale = ContentScale.Crop
|
||||||
|
)
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.matchParentSize()
|
||||||
|
.background(
|
||||||
|
Brush.verticalGradient(
|
||||||
|
colors = listOf(
|
||||||
|
Color.Transparent,
|
||||||
|
Color.Transparent,
|
||||||
|
scheme.background.copy(alpha = 0.5f),
|
||||||
|
scheme.background
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Scaffold(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
containerColor = Color.Transparent,
|
||||||
|
topBar = {
|
||||||
|
if (!isHomeMediaTransitionActive) {
|
||||||
|
topBar()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) { innerPadding ->
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.verticalScroll(rememberScrollState())
|
||||||
|
.padding(bottom = innerPadding.calculateBottomPadding())
|
||||||
|
.padding(horizontal = 16.dp)
|
||||||
|
.padding(top = imageHeight - contentOverlap),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(contentSpacing)
|
||||||
|
) {
|
||||||
|
if (!isHomeMediaTransitionActive) {
|
||||||
|
heroContent()
|
||||||
|
content()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
package hu.bbara.purefin.ui.common.media
|
|
||||||
|
|
||||||
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.platform.LocalConfiguration
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import hu.bbara.purefin.ui.common.image.PurefinAsyncImage
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun MediaHero(
|
|
||||||
imageUrl: String,
|
|
||||||
backgroundColor: Color,
|
|
||||||
heightFraction: Float = 0.4f,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
val screenHeight = LocalConfiguration.current.screenHeightDp.dp
|
|
||||||
val heroHeight = screenHeight * heightFraction
|
|
||||||
|
|
||||||
Box(
|
|
||||||
modifier = modifier
|
|
||||||
.height(heroHeight)
|
|
||||||
.background(backgroundColor)
|
|
||||||
) {
|
|
||||||
PurefinAsyncImage(
|
|
||||||
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.5f),
|
|
||||||
backgroundColor
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,51 +4,36 @@ import android.Manifest
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.rememberScrollState
|
|
||||||
import androidx.compose.foundation.verticalScroll
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Brush
|
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.ui.platform.LocalConfiguration
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
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 hu.bbara.purefin.ui.screen.waiting.PurefinWaitingScreen
|
|
||||||
import hu.bbara.purefin.ui.common.media.MediaHero
|
|
||||||
import hu.bbara.purefin.ui.common.media.MediaMetadataFlowRow
|
|
||||||
import hu.bbara.purefin.ui.common.media.homeMediaSharedBoundsDestination
|
|
||||||
import hu.bbara.purefin.ui.common.media.isHomeMediaSharedBoundsTransitionActive
|
|
||||||
import hu.bbara.purefin.core.download.DownloadState
|
import hu.bbara.purefin.core.download.DownloadState
|
||||||
|
import hu.bbara.purefin.core.feature.content.episode.EpisodeScreenViewModel
|
||||||
|
import hu.bbara.purefin.core.image.ArtworkKind
|
||||||
import hu.bbara.purefin.core.image.ImageUrlBuilder
|
import hu.bbara.purefin.core.image.ImageUrlBuilder
|
||||||
import hu.bbara.purefin.core.navigation.EpisodeDto
|
import hu.bbara.purefin.core.navigation.EpisodeDto
|
||||||
import hu.bbara.purefin.navigation.LocalNavigationBackStack
|
|
||||||
import hu.bbara.purefin.core.navigation.Route
|
import hu.bbara.purefin.core.navigation.Route
|
||||||
import hu.bbara.purefin.model.CastMember
|
import hu.bbara.purefin.model.CastMember
|
||||||
import hu.bbara.purefin.model.Episode
|
import hu.bbara.purefin.model.Episode
|
||||||
import hu.bbara.purefin.core.feature.content.episode.EpisodeScreenViewModel
|
import hu.bbara.purefin.navigation.LocalNavigationBackStack
|
||||||
|
import hu.bbara.purefin.ui.common.media.MediaDetailScaffold
|
||||||
|
import hu.bbara.purefin.ui.common.media.MediaMetadataFlowRow
|
||||||
import hu.bbara.purefin.ui.screen.episode.components.EpisodeDetails
|
import hu.bbara.purefin.ui.screen.episode.components.EpisodeDetails
|
||||||
import hu.bbara.purefin.ui.screen.episode.components.EpisodeTopBar
|
import hu.bbara.purefin.ui.screen.episode.components.EpisodeTopBar
|
||||||
import hu.bbara.purefin.ui.screen.episode.components.EpisodeTopBarShortcut
|
import hu.bbara.purefin.ui.screen.episode.components.EpisodeTopBarShortcut
|
||||||
|
import hu.bbara.purefin.ui.screen.waiting.PurefinWaitingScreen
|
||||||
import hu.bbara.purefin.ui.theme.AppTheme
|
import hu.bbara.purefin.ui.theme.AppTheme
|
||||||
import hu.bbara.purefin.core.image.ArtworkKind
|
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -119,101 +104,52 @@ private fun EpisodeScreenInternal(
|
|||||||
onDownloadClick: () -> Unit,
|
onDownloadClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val isHomeMediaTransitionActive = isHomeMediaSharedBoundsTransitionActive()
|
MediaDetailScaffold(
|
||||||
|
imageUrl = ImageUrlBuilder.finishImageUrl(episode.imageUrlPrefix, ArtworkKind.PRIMARY),
|
||||||
Scaffold(
|
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
containerColor = MaterialTheme.colorScheme.background,
|
|
||||||
topBar = {
|
topBar = {
|
||||||
if (!isHomeMediaTransitionActive) {
|
EpisodeTopBar(
|
||||||
EpisodeTopBar(
|
shortcut = topBarShortcut,
|
||||||
shortcut = topBarShortcut,
|
onBack = onBack,
|
||||||
onBack = onBack,
|
onSeriesClick = onSeriesClick
|
||||||
onSeriesClick = onSeriesClick,
|
)
|
||||||
modifier = Modifier
|
},
|
||||||
)
|
heroContent = {
|
||||||
}
|
EpisodeHeroContent(episode = episode)
|
||||||
}
|
}
|
||||||
) { innerPadding ->
|
) {
|
||||||
Column(
|
EpisodeDetails(
|
||||||
|
episode = episode,
|
||||||
|
downloadState = downloadState,
|
||||||
|
onDownloadClick = onDownloadClick,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxWidth()
|
||||||
.verticalScroll(rememberScrollState())
|
.padding(horizontal = 16.dp)
|
||||||
) {
|
)
|
||||||
EpisodeHeroSection(episode = episode)
|
|
||||||
if (!isHomeMediaTransitionActive) {
|
|
||||||
EpisodeDetails(
|
|
||||||
episode = episode,
|
|
||||||
downloadState = downloadState,
|
|
||||||
onDownloadClick = onDownloadClick,
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(horizontal = 16.dp)
|
|
||||||
.padding(bottom = innerPadding.calculateBottomPadding())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun EpisodeHeroSection(
|
private fun EpisodeHeroContent(
|
||||||
episode: Episode,
|
episode: Episode,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val scheme = MaterialTheme.colorScheme
|
val scheme = MaterialTheme.colorScheme
|
||||||
val screenHeight = LocalConfiguration.current.screenHeightDp.dp
|
|
||||||
val sectionHeight = screenHeight * 0.4f
|
|
||||||
|
|
||||||
Box(
|
Text(
|
||||||
modifier = modifier
|
text = episode.title,
|
||||||
.homeMediaSharedBoundsDestination()
|
color = scheme.onBackground,
|
||||||
.fillMaxWidth()
|
fontSize = 32.sp,
|
||||||
.height(sectionHeight)
|
fontWeight = FontWeight.Bold,
|
||||||
) {
|
lineHeight = 38.sp
|
||||||
MediaHero(
|
)
|
||||||
imageUrl = ImageUrlBuilder.finishImageUrl(episode.imageUrlPrefix, ArtworkKind.PRIMARY),
|
Text(
|
||||||
backgroundColor = scheme.background,
|
text = "Episode ${episode.index}",
|
||||||
modifier = Modifier.fillMaxSize()
|
color = scheme.onBackground,
|
||||||
)
|
fontSize = 14.sp,
|
||||||
Box(
|
fontWeight = FontWeight.Medium
|
||||||
modifier = Modifier
|
)
|
||||||
.matchParentSize()
|
EpisodeMetaChips(episode = episode)
|
||||||
.background(
|
|
||||||
Brush.verticalGradient(
|
|
||||||
colors = listOf(
|
|
||||||
Color.Transparent,
|
|
||||||
Color.Transparent,
|
|
||||||
scheme.background.copy(alpha = 0.5f),
|
|
||||||
scheme.background
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.align(Alignment.BottomStart)
|
|
||||||
.padding(16.dp)
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = episode.title,
|
|
||||||
color = scheme.onBackground,
|
|
||||||
fontSize = 32.sp,
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
lineHeight = 38.sp
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(4.dp))
|
|
||||||
Text(
|
|
||||||
text = "Episode ${episode.index}",
|
|
||||||
color = scheme.onBackground,
|
|
||||||
fontSize = 14.sp,
|
|
||||||
fontWeight = FontWeight.Medium
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
|
||||||
EpisodeMetaChips(episode = episode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
@@ -131,8 +131,6 @@ internal fun EpisodeDetails(
|
|||||||
MediaSynopsis(
|
MediaSynopsis(
|
||||||
synopsis = episode.synopsis
|
synopsis = episode.synopsis
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(24.dp))
|
|
||||||
|
|
||||||
Row() {
|
Row() {
|
||||||
MediaResumeButton(
|
MediaResumeButton(
|
||||||
text = mediaPlayButtonText(episode.progress, episode.watched),
|
text = mediaPlayButtonText(episode.progress, episode.watched),
|
||||||
@@ -162,8 +160,6 @@ internal fun EpisodeDetails(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.height(24.dp))
|
|
||||||
|
|
||||||
MediaPlaybackSettings(
|
MediaPlaybackSettings(
|
||||||
backgroundColor = MaterialTheme.colorScheme.surface,
|
backgroundColor = MaterialTheme.colorScheme.surface,
|
||||||
foregroundColor = MaterialTheme.colorScheme.onSurface,
|
foregroundColor = MaterialTheme.colorScheme.onSurface,
|
||||||
@@ -171,8 +167,6 @@ internal fun EpisodeDetails(
|
|||||||
audioTrack = "ENG",
|
audioTrack = "ENG",
|
||||||
subtitles = "ENG"
|
subtitles = "ENG"
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(24.dp))
|
|
||||||
|
|
||||||
if (episode.cast.isNotEmpty()) {
|
if (episode.cast.isNotEmpty()) {
|
||||||
Text(
|
Text(
|
||||||
text = "Cast",
|
text = "Cast",
|
||||||
|
|||||||
@@ -4,43 +4,25 @@ import android.Manifest
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.foundation.layout.height
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.rememberScrollState
|
|
||||||
import androidx.compose.foundation.verticalScroll
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Brush
|
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.ui.platform.LocalConfiguration
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
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 hu.bbara.purefin.core.download.DownloadState
|
import hu.bbara.purefin.core.download.DownloadState
|
||||||
import hu.bbara.purefin.core.feature.content.movie.MovieScreenViewModel
|
import hu.bbara.purefin.core.feature.content.movie.MovieScreenViewModel
|
||||||
import hu.bbara.purefin.core.image.ArtworkKind
|
import hu.bbara.purefin.core.image.ArtworkKind
|
||||||
import hu.bbara.purefin.core.image.ImageUrlBuilder
|
import hu.bbara.purefin.core.image.ImageUrlBuilder
|
||||||
|
import hu.bbara.purefin.core.navigation.MovieDto
|
||||||
import hu.bbara.purefin.model.CastMember
|
import hu.bbara.purefin.model.CastMember
|
||||||
import hu.bbara.purefin.model.Movie
|
import hu.bbara.purefin.model.Movie
|
||||||
import hu.bbara.purefin.core.navigation.MovieDto
|
import hu.bbara.purefin.ui.common.media.MediaDetailScaffold
|
||||||
import hu.bbara.purefin.ui.common.media.MediaHero
|
|
||||||
import hu.bbara.purefin.ui.common.media.MediaMetadataFlowRow
|
import hu.bbara.purefin.ui.common.media.MediaMetadataFlowRow
|
||||||
import hu.bbara.purefin.ui.common.media.homeMediaSharedBoundsDestination
|
|
||||||
import hu.bbara.purefin.ui.common.media.isHomeMediaSharedBoundsTransitionActive
|
|
||||||
import hu.bbara.purefin.ui.screen.movie.components.MovieDetails
|
import hu.bbara.purefin.ui.screen.movie.components.MovieDetails
|
||||||
import hu.bbara.purefin.ui.screen.movie.components.MovieTopBar
|
import hu.bbara.purefin.ui.screen.movie.components.MovieTopBar
|
||||||
import hu.bbara.purefin.ui.screen.waiting.PurefinWaitingScreen
|
import hu.bbara.purefin.ui.screen.waiting.PurefinWaitingScreen
|
||||||
@@ -49,7 +31,9 @@ import java.util.UUID
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MovieScreen(
|
fun MovieScreen(
|
||||||
movie: MovieDto, viewModel: MovieScreenViewModel = hiltViewModel(), modifier: Modifier = Modifier
|
movie: MovieDto,
|
||||||
|
viewModel: MovieScreenViewModel = hiltViewModel(),
|
||||||
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
LaunchedEffect(movie.id) {
|
LaunchedEffect(movie.id) {
|
||||||
viewModel.selectMovie(movie.id)
|
viewModel.selectMovie(movie.id)
|
||||||
@@ -96,93 +80,43 @@ private fun MovieScreenInternal(
|
|||||||
onBack: () -> Unit,
|
onBack: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val isHomeMediaTransitionActive = isHomeMediaSharedBoundsTransitionActive()
|
MediaDetailScaffold(
|
||||||
|
imageUrl = ImageUrlBuilder.finishImageUrl(movie.imageUrlPrefix, ArtworkKind.PRIMARY),
|
||||||
Scaffold(
|
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
containerColor = MaterialTheme.colorScheme.background,
|
|
||||||
topBar = {
|
topBar = {
|
||||||
if (!isHomeMediaTransitionActive) {
|
MovieTopBar(onBack = onBack)
|
||||||
MovieTopBar(
|
},
|
||||||
onBack = onBack,
|
heroContent = {
|
||||||
modifier = Modifier
|
MovieHeroContent(movie = movie)
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
) { innerPadding ->
|
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.verticalScroll(rememberScrollState())
|
|
||||||
) {
|
|
||||||
MediaHeroSection(movie = movie)
|
|
||||||
if (!isHomeMediaTransitionActive) {
|
|
||||||
MovieDetails(
|
|
||||||
movie = movie,
|
|
||||||
downloadState = downloadState,
|
|
||||||
onDownloadClick = onDownloadClick,
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(horizontal = 16.dp)
|
|
||||||
.padding(bottom = innerPadding.calculateBottomPadding())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
) {
|
||||||
|
MovieDetails(
|
||||||
|
movie = movie,
|
||||||
|
downloadState = downloadState,
|
||||||
|
onDownloadClick = onDownloadClick,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MediaHeroSection(
|
private fun MovieHeroContent(
|
||||||
movie: Movie,
|
movie: Movie,
|
||||||
modifier: Modifier = Modifier) {
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
val scheme = MaterialTheme.colorScheme
|
val scheme = MaterialTheme.colorScheme
|
||||||
val screenHeight = LocalConfiguration.current.screenHeightDp.dp
|
|
||||||
val sectionHeight = screenHeight * 0.4f
|
|
||||||
|
|
||||||
Box (
|
Text(
|
||||||
modifier = modifier
|
text = movie.title,
|
||||||
.homeMediaSharedBoundsDestination()
|
color = scheme.onBackground,
|
||||||
.height(sectionHeight)
|
fontSize = 32.sp,
|
||||||
) {
|
fontWeight = FontWeight.Bold,
|
||||||
MediaHero(
|
lineHeight = 38.sp
|
||||||
imageUrl = ImageUrlBuilder.finishImageUrl(movie.imageUrlPrefix, ArtworkKind.PRIMARY),
|
)
|
||||||
backgroundColor = MaterialTheme.colorScheme.background,
|
// Spacer(modifier = Modifier.height(8.dp))
|
||||||
modifier = Modifier.fillMaxSize()
|
MediaMetadataFlowRow(
|
||||||
)
|
items = listOf(movie.year, movie.rating, movie.runtime, movie.format),
|
||||||
Box(
|
highlightedItem = movie.format
|
||||||
modifier = Modifier
|
)
|
||||||
.matchParentSize()
|
|
||||||
.background(
|
|
||||||
Brush.verticalGradient(
|
|
||||||
colors = listOf(
|
|
||||||
Color.Transparent,
|
|
||||||
Color.Transparent,
|
|
||||||
scheme.background.copy(alpha = 0.5f),
|
|
||||||
scheme.background
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
Column (
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
.align(Alignment.BottomStart)
|
|
||||||
.padding(16.dp)
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = movie.title,
|
|
||||||
color = scheme.onBackground,
|
|
||||||
fontSize = 32.sp,
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
lineHeight = 38.sp
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
|
||||||
MediaMetadataFlowRow(
|
|
||||||
items = listOf(movie.year, movie.rating, movie.runtime, movie.format),
|
|
||||||
highlightedItem = movie.format
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview(showBackground = true)
|
@Preview(showBackground = true)
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package hu.bbara.purefin.ui.screen.movie.components
|
|||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
@@ -60,7 +59,10 @@ internal fun MovieTopBar(
|
|||||||
)
|
)
|
||||||
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
|
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||||
GhostIconButton(icon = Icons.Outlined.Cast, contentDescription = "Cast", onClick = { })
|
GhostIconButton(icon = Icons.Outlined.Cast, contentDescription = "Cast", onClick = { })
|
||||||
GhostIconButton(icon = Icons.Outlined.MoreVert, contentDescription = "More", onClick = { })
|
GhostIconButton(
|
||||||
|
icon = Icons.Outlined.MoreVert,
|
||||||
|
contentDescription = "More",
|
||||||
|
onClick = { })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -71,7 +73,6 @@ internal fun MovieDetails(
|
|||||||
movie: Movie,
|
movie: Movie,
|
||||||
downloadState: DownloadState,
|
downloadState: DownloadState,
|
||||||
onDownloadClick: () -> Unit,
|
onDownloadClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier
|
|
||||||
) {
|
) {
|
||||||
val scheme = MaterialTheme.colorScheme
|
val scheme = MaterialTheme.colorScheme
|
||||||
|
|
||||||
@@ -84,63 +85,56 @@ internal fun MovieDetails(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Column(modifier = modifier) {
|
MediaSynopsis(
|
||||||
MediaSynopsis(
|
synopsis = movie.synopsis
|
||||||
synopsis = movie.synopsis
|
)
|
||||||
|
Row() {
|
||||||
|
MediaResumeButton(
|
||||||
|
text = mediaPlayButtonText(movie.progress, movie.watched),
|
||||||
|
progress = mediaPlaybackProgress(movie.progress),
|
||||||
|
onClick = playAction,
|
||||||
|
modifier = Modifier.sizeIn(maxWidth = 200.dp)
|
||||||
|
)
|
||||||
|
VerticalDivider(
|
||||||
|
color = MaterialTheme.colorScheme.secondary,
|
||||||
|
thickness = 4.dp,
|
||||||
|
modifier = Modifier
|
||||||
|
.height(48.dp)
|
||||||
|
.padding(horizontal = 16.dp, vertical = 8.dp)
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(24.dp))
|
|
||||||
|
|
||||||
Row() {
|
Row() {
|
||||||
MediaResumeButton(
|
MediaActionButton(
|
||||||
text = mediaPlayButtonText(movie.progress, movie.watched),
|
backgroundColor = MaterialTheme.colorScheme.surface,
|
||||||
progress = mediaPlaybackProgress(movie.progress),
|
iconColor = MaterialTheme.colorScheme.onSurface,
|
||||||
onClick = playAction,
|
icon = when (downloadState) {
|
||||||
modifier = Modifier.sizeIn(maxWidth = 200.dp)
|
is DownloadState.NotDownloaded -> Icons.Outlined.Download
|
||||||
|
is DownloadState.Downloading -> Icons.Outlined.Close
|
||||||
|
is DownloadState.Downloaded -> Icons.Outlined.DownloadDone
|
||||||
|
is DownloadState.Failed -> Icons.Outlined.Download
|
||||||
|
},
|
||||||
|
height = 48.dp,
|
||||||
|
onClick = onDownloadClick
|
||||||
)
|
)
|
||||||
VerticalDivider(
|
|
||||||
color = MaterialTheme.colorScheme.secondary,
|
|
||||||
thickness = 4.dp,
|
|
||||||
modifier = Modifier
|
|
||||||
.height(48.dp)
|
|
||||||
.padding(horizontal = 16.dp, vertical = 8.dp)
|
|
||||||
)
|
|
||||||
Row() {
|
|
||||||
MediaActionButton(
|
|
||||||
backgroundColor = MaterialTheme.colorScheme.surface,
|
|
||||||
iconColor = MaterialTheme.colorScheme.onSurface,
|
|
||||||
icon = when (downloadState) {
|
|
||||||
is DownloadState.NotDownloaded -> Icons.Outlined.Download
|
|
||||||
is DownloadState.Downloading -> Icons.Outlined.Close
|
|
||||||
is DownloadState.Downloaded -> Icons.Outlined.DownloadDone
|
|
||||||
is DownloadState.Failed -> Icons.Outlined.Download
|
|
||||||
},
|
|
||||||
height = 48.dp,
|
|
||||||
onClick = onDownloadClick
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
MediaPlaybackSettings(
|
||||||
|
backgroundColor = MaterialTheme.colorScheme.surface,
|
||||||
|
foregroundColor = MaterialTheme.colorScheme.onSurface,
|
||||||
|
audioTrack = "ENG",
|
||||||
|
subtitles = "ENG"
|
||||||
|
)
|
||||||
|
if (movie.cast.isNotEmpty()) {
|
||||||
Spacer(modifier = Modifier.height(24.dp))
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
|
Text(
|
||||||
MediaPlaybackSettings(
|
text = "Cast",
|
||||||
backgroundColor = MaterialTheme.colorScheme.surface,
|
color = scheme.onBackground,
|
||||||
foregroundColor = MaterialTheme.colorScheme.onSurface,
|
fontSize = 18.sp,
|
||||||
audioTrack = "ENG",
|
fontWeight = FontWeight.Bold
|
||||||
subtitles = "ENG"
|
|
||||||
)
|
)
|
||||||
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
if (movie.cast.isNotEmpty()) {
|
//TODO fix
|
||||||
Spacer(modifier = Modifier.height(24.dp))
|
|
||||||
Text(
|
|
||||||
text = "Cast",
|
|
||||||
color = scheme.onBackground,
|
|
||||||
fontSize = 18.sp,
|
|
||||||
fontWeight = FontWeight.Bold
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(12.dp))
|
|
||||||
//TODO fix
|
|
||||||
// MediaCastRow(
|
// MediaCastRow(
|
||||||
// cast = movie.cast,
|
// cast = movie.cast,
|
||||||
// )
|
// )
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,8 @@
|
|||||||
package hu.bbara.purefin.ui.screen.series
|
package hu.bbara.purefin.ui.screen.series
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.rememberScrollState
|
|
||||||
import androidx.compose.foundation.verticalScroll
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
@@ -20,11 +11,7 @@ import androidx.compose.runtime.getValue
|
|||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Brush
|
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.ui.platform.LocalConfiguration
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
@@ -34,15 +21,13 @@ import hu.bbara.purefin.core.download.DownloadState
|
|||||||
import hu.bbara.purefin.core.feature.content.series.SeriesViewModel
|
import hu.bbara.purefin.core.feature.content.series.SeriesViewModel
|
||||||
import hu.bbara.purefin.core.image.ArtworkKind
|
import hu.bbara.purefin.core.image.ArtworkKind
|
||||||
import hu.bbara.purefin.core.image.ImageUrlBuilder
|
import hu.bbara.purefin.core.image.ImageUrlBuilder
|
||||||
|
import hu.bbara.purefin.core.navigation.SeriesDto
|
||||||
import hu.bbara.purefin.model.CastMember
|
import hu.bbara.purefin.model.CastMember
|
||||||
import hu.bbara.purefin.model.Episode
|
import hu.bbara.purefin.model.Episode
|
||||||
import hu.bbara.purefin.model.Season
|
import hu.bbara.purefin.model.Season
|
||||||
import hu.bbara.purefin.model.Series
|
import hu.bbara.purefin.model.Series
|
||||||
import hu.bbara.purefin.core.navigation.SeriesDto
|
import hu.bbara.purefin.ui.common.media.MediaDetailScaffold
|
||||||
import hu.bbara.purefin.ui.common.media.MediaHero
|
|
||||||
import hu.bbara.purefin.ui.common.media.MediaSynopsis
|
import hu.bbara.purefin.ui.common.media.MediaSynopsis
|
||||||
import hu.bbara.purefin.ui.common.media.homeMediaSharedBoundsDestination
|
|
||||||
import hu.bbara.purefin.ui.common.media.isHomeMediaSharedBoundsTransitionActive
|
|
||||||
import hu.bbara.purefin.ui.screen.series.components.CastRow
|
import hu.bbara.purefin.ui.screen.series.components.CastRow
|
||||||
import hu.bbara.purefin.ui.screen.series.components.EpisodeCarousel
|
import hu.bbara.purefin.ui.screen.series.components.EpisodeCarousel
|
||||||
import hu.bbara.purefin.ui.screen.series.components.SeasonTabs
|
import hu.bbara.purefin.ui.screen.series.components.SeasonTabs
|
||||||
@@ -79,8 +64,10 @@ fun SeriesScreen(
|
|||||||
when (option) {
|
when (option) {
|
||||||
SeriesDownloadOption.SEASON ->
|
SeriesDownloadOption.SEASON ->
|
||||||
viewModel.downloadSeason(seriesData.id, selectedSeason.id)
|
viewModel.downloadSeason(seriesData.id, selectedSeason.id)
|
||||||
|
|
||||||
SeriesDownloadOption.SERIES ->
|
SeriesDownloadOption.SERIES ->
|
||||||
viewModel.downloadSeries(seriesData)
|
viewModel.downloadSeries(seriesData)
|
||||||
|
|
||||||
SeriesDownloadOption.SMART ->
|
SeriesDownloadOption.SMART ->
|
||||||
viewModel.enableSmartDownload(seriesData.id)
|
viewModel.enableSmartDownload(seriesData.id)
|
||||||
}
|
}
|
||||||
@@ -107,14 +94,14 @@ private fun SeriesScreenInternal(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val scheme = MaterialTheme.colorScheme
|
val scheme = MaterialTheme.colorScheme
|
||||||
val isHomeMediaTransitionActive = isHomeMediaSharedBoundsTransitionActive()
|
|
||||||
|
|
||||||
fun getDefaultSeason(): Season {
|
fun getDefaultSeason(): Season {
|
||||||
return series.seasons.firstOrNull { it.unwatchedEpisodeCount > 0 } ?: series.seasons.first()
|
return series.seasons.firstOrNull { it.unwatchedEpisodeCount > 0 } ?: series.seasons.first()
|
||||||
}
|
}
|
||||||
|
|
||||||
var selectedSeasonId by remember(series.id) { mutableStateOf(getDefaultSeason().id) }
|
var selectedSeasonId by remember(series.id) { mutableStateOf(getDefaultSeason().id) }
|
||||||
val selectedSeason = series.seasons.firstOrNull { it.id == selectedSeasonId } ?: getDefaultSeason()
|
val selectedSeason =
|
||||||
|
series.seasons.firstOrNull { it.id == selectedSeasonId } ?: getDefaultSeason()
|
||||||
val nextUpEpisode = selectedSeason.episodes.firstOrNull { !it.watched }
|
val nextUpEpisode = selectedSeason.episodes.firstOrNull { !it.watched }
|
||||||
?: selectedSeason.episodes.firstOrNull()
|
?: selectedSeason.episodes.firstOrNull()
|
||||||
|
|
||||||
@@ -126,125 +113,68 @@ private fun SeriesScreenInternal(
|
|||||||
onObserveSeasonDownloadState(selectedSeason.episodes)
|
onObserveSeasonDownloadState(selectedSeason.episodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
Scaffold(
|
MediaDetailScaffold(
|
||||||
|
imageUrl = ImageUrlBuilder.finishImageUrl(series.imageUrlPrefix, ArtworkKind.PRIMARY),
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
containerColor = MaterialTheme.colorScheme.background,
|
|
||||||
topBar = {
|
topBar = {
|
||||||
if (!isHomeMediaTransitionActive) {
|
SeriesTopBar(onBack = onBack)
|
||||||
SeriesTopBar(
|
},
|
||||||
onBack = onBack,
|
heroContent = {
|
||||||
modifier = Modifier
|
SeriesHeroContent(series = series)
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
) { innerPadding ->
|
) {
|
||||||
Column(
|
SeriesActionButtons(
|
||||||
modifier = Modifier
|
nextUpEpisode = nextUpEpisode,
|
||||||
.fillMaxSize()
|
seriesDownloadState = seriesDownloadState,
|
||||||
.verticalScroll(rememberScrollState())
|
selectedSeason = selectedSeason,
|
||||||
) {
|
seasonDownloadState = seasonDownloadState,
|
||||||
SeriesHeroSection(series = series)
|
onDownloadOptionSelected = { option ->
|
||||||
if (!isHomeMediaTransitionActive) {
|
onDownloadOptionSelected(option, selectedSeason)
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(horizontal = 16.dp)
|
|
||||||
.padding(bottom = innerPadding.calculateBottomPadding())
|
|
||||||
) {
|
|
||||||
SeriesActionButtons(
|
|
||||||
nextUpEpisode = nextUpEpisode,
|
|
||||||
seriesDownloadState = seriesDownloadState,
|
|
||||||
selectedSeason = selectedSeason,
|
|
||||||
seasonDownloadState = seasonDownloadState,
|
|
||||||
onDownloadOptionSelected = { option ->
|
|
||||||
onDownloadOptionSelected(option, selectedSeason)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(24.dp))
|
|
||||||
MediaSynopsis(
|
|
||||||
synopsis = series.synopsis,
|
|
||||||
bodyColor = scheme.onSurface,
|
|
||||||
bodyFontSize = 13.sp,
|
|
||||||
bodyLineHeight = null,
|
|
||||||
titleSpacing = 8.dp
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(24.dp))
|
|
||||||
SeasonTabs(
|
|
||||||
seasons = series.seasons,
|
|
||||||
selectedSeason = selectedSeason,
|
|
||||||
onSelect = { selectedSeasonId = it.id }
|
|
||||||
)
|
|
||||||
EpisodeCarousel(
|
|
||||||
episodes = selectedSeason.episodes,
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
|
||||||
if(series.cast.isNotEmpty()) {
|
|
||||||
Text(
|
|
||||||
text = "Cast",
|
|
||||||
color = scheme.onBackground,
|
|
||||||
fontSize = 18.sp,
|
|
||||||
fontWeight = FontWeight.Bold
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(12.dp))
|
|
||||||
CastRow(cast = series.cast)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
MediaSynopsis(
|
||||||
|
synopsis = series.synopsis,
|
||||||
|
bodyColor = scheme.onSurface,
|
||||||
|
bodyFontSize = 13.sp,
|
||||||
|
bodyLineHeight = null,
|
||||||
|
titleSpacing = 8.dp
|
||||||
|
)
|
||||||
|
SeasonTabs(
|
||||||
|
seasons = series.seasons,
|
||||||
|
selectedSeason = selectedSeason,
|
||||||
|
onSelect = { selectedSeasonId = it.id }
|
||||||
|
)
|
||||||
|
EpisodeCarousel(
|
||||||
|
episodes = selectedSeason.episodes,
|
||||||
|
)
|
||||||
|
if (series.cast.isNotEmpty()) {
|
||||||
|
Text(
|
||||||
|
text = "Cast",
|
||||||
|
color = scheme.onBackground,
|
||||||
|
fontSize = 18.sp,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
|
CastRow(cast = series.cast)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun SeriesHeroSection(
|
private fun SeriesHeroContent(
|
||||||
series: Series,
|
series: Series,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val scheme = MaterialTheme.colorScheme
|
val scheme = MaterialTheme.colorScheme
|
||||||
val screenHeight = LocalConfiguration.current.screenHeightDp.dp
|
|
||||||
val sectionHeight = screenHeight * 0.4f
|
|
||||||
|
|
||||||
Box(
|
Text(
|
||||||
modifier = modifier
|
text = series.name,
|
||||||
.homeMediaSharedBoundsDestination()
|
color = scheme.onBackground,
|
||||||
.fillMaxWidth()
|
fontSize = 30.sp,
|
||||||
.height(sectionHeight)
|
fontWeight = FontWeight.Bold,
|
||||||
) {
|
lineHeight = 36.sp
|
||||||
MediaHero(
|
)
|
||||||
imageUrl = ImageUrlBuilder.finishImageUrl(series.imageUrlPrefix, ArtworkKind.PRIMARY),
|
SeriesMetaChips(series = series)
|
||||||
backgroundColor = scheme.background,
|
|
||||||
modifier = Modifier.fillMaxSize()
|
|
||||||
)
|
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
|
||||||
.matchParentSize()
|
|
||||||
.background(
|
|
||||||
Brush.verticalGradient(
|
|
||||||
colors = listOf(
|
|
||||||
Color.Transparent,
|
|
||||||
Color.Transparent,
|
|
||||||
scheme.background.copy(alpha = 0.5f),
|
|
||||||
scheme.background
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.align(Alignment.BottomStart)
|
|
||||||
.padding(16.dp)
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = series.name,
|
|
||||||
color = scheme.onBackground,
|
|
||||||
fontSize = 30.sp,
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
lineHeight = 36.sp
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
|
||||||
SeriesMetaChips(series = series)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview(showBackground = true)
|
@Preview(showBackground = true)
|
||||||
|
|||||||
Reference in New Issue
Block a user