mirror of
https://github.com/bbara04/Purefin.git
synced 2026-03-31 17:10:08 +02:00
refactor media screens to use Scaffold and standard layout components
- Replace `BoxWithConstraints` with `Scaffold` in `MovieScreen`, `EpisodeScreen`, and `SeriesScreen`. - Move `MovieTopBar`, `EpisodeTopBar`, and `SeriesTopBar` into the `topBar` slot of `Scaffold`. - Implement `statusBarsPadding` and standard padding in top bar components. - Decouple top bar components from ViewModels by passing an `onBack` callback. - Simplify hero image heights and remove complex offsets and conditional wide-screen layouts. - Use `floatingActionButton` slot for the play button in movie and episode screens. - Standardize background handling using `MaterialTheme.colorScheme.background`.
This commit is contained in:
@@ -6,7 +6,10 @@ import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
|||||||
import androidx.compose.foundation.layout.FlowRow
|
import androidx.compose.foundation.layout.FlowRow
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
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.layout.statusBarsPadding
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.ArrowBack
|
import androidx.compose.material.icons.outlined.ArrowBack
|
||||||
import androidx.compose.material.icons.outlined.Cast
|
import androidx.compose.material.icons.outlined.Cast
|
||||||
@@ -18,7 +21,6 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.text.font.FontWeight
|
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.compose.ui.unit.sp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
|
||||||
import hu.bbara.purefin.common.ui.MediaActionButtons
|
import hu.bbara.purefin.common.ui.MediaActionButtons
|
||||||
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
|
||||||
@@ -29,12 +31,15 @@ import hu.bbara.purefin.common.ui.toMediaDetailColors
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun EpisodeTopBar(
|
internal fun EpisodeTopBar(
|
||||||
viewModel: EpisodeScreenViewModel = hiltViewModel(),
|
onBack: () -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val colors = rememberEpisodeColors().toMediaDetailColors()
|
val colors = rememberEpisodeColors().toMediaDetailColors()
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier,
|
modifier = modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.statusBarsPadding()
|
||||||
|
.padding(16.dp),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
@@ -42,7 +47,7 @@ internal fun EpisodeTopBar(
|
|||||||
colors = colors,
|
colors = colors,
|
||||||
icon = Icons.Outlined.ArrowBack,
|
icon = Icons.Outlined.ArrowBack,
|
||||||
contentDescription = "Back",
|
contentDescription = "Back",
|
||||||
onClick = { viewModel.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(colors = colors, icon = Icons.Outlined.Cast, contentDescription = "Cast", onClick = { })
|
||||||
|
|||||||
@@ -1,26 +1,19 @@
|
|||||||
package hu.bbara.purefin.app.content.episode
|
package hu.bbara.purefin.app.content.episode
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.offset
|
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
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.Color
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
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
|
||||||
@@ -29,7 +22,6 @@ import hu.bbara.purefin.app.content.ContentMockData
|
|||||||
import hu.bbara.purefin.common.ui.MediaFloatingPlayButton
|
import hu.bbara.purefin.common.ui.MediaFloatingPlayButton
|
||||||
import hu.bbara.purefin.common.ui.PurefinWaitingScreen
|
import hu.bbara.purefin.common.ui.PurefinWaitingScreen
|
||||||
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.navigation.ItemDto
|
import hu.bbara.purefin.navigation.ItemDto
|
||||||
import hu.bbara.purefin.player.PlayerActivity
|
import hu.bbara.purefin.player.PlayerActivity
|
||||||
|
|
||||||
@@ -52,19 +44,18 @@ fun EpisodeScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
EpisodeScreenInternal(
|
EpisodeScreenInternal(
|
||||||
episode = episode.value!!,
|
episode = episode.value!!,
|
||||||
modifier = modifier,
|
onBack = viewModel::onBack,
|
||||||
backGroundColor = MaterialTheme.colorScheme.background
|
modifier = modifier
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun EpisodeScreenInternal(
|
private fun EpisodeScreenInternal(
|
||||||
episode: EpisodeUiModel,
|
episode: EpisodeUiModel,
|
||||||
backGroundColor: Color,
|
onBack: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val colors = rememberEpisodeColors().toMediaDetailColors()
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val playAction = remember(episode.id) {
|
val playAction = remember(episode.id) {
|
||||||
{
|
{
|
||||||
@@ -74,78 +65,43 @@ private fun EpisodeScreenInternal(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BoxWithConstraints(
|
Scaffold(
|
||||||
modifier = modifier
|
modifier = modifier,
|
||||||
.fillMaxSize()
|
containerColor = MaterialTheme.colorScheme.background,
|
||||||
.background(colors.background)
|
topBar = {
|
||||||
) {
|
|
||||||
val isWide = maxWidth >= 900.dp
|
|
||||||
val contentPadding = if (isWide) 32.dp else 20.dp
|
|
||||||
|
|
||||||
Box(modifier = Modifier.fillMaxSize()) {
|
|
||||||
if (isWide) {
|
|
||||||
Row(modifier = Modifier.fillMaxSize()) {
|
|
||||||
MediaHero(
|
|
||||||
imageUrl = episode.heroImageUrl,
|
|
||||||
height = 300.dp,
|
|
||||||
backgroundColor = backGroundColor,
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxHeight()
|
|
||||||
.weight(0.5f)
|
|
||||||
)
|
|
||||||
EpisodeDetails(
|
|
||||||
episode = episode,
|
|
||||||
modifier = Modifier
|
|
||||||
.weight(0.5f)
|
|
||||||
.fillMaxHeight()
|
|
||||||
.verticalScroll(rememberScrollState())
|
|
||||||
.padding(
|
|
||||||
start = contentPadding,
|
|
||||||
end = contentPadding,
|
|
||||||
top = 96.dp,
|
|
||||||
bottom = 32.dp
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.verticalScroll(rememberScrollState())
|
|
||||||
) {
|
|
||||||
MediaHero(
|
|
||||||
imageUrl = episode.heroImageUrl,
|
|
||||||
backgroundColor = backGroundColor,
|
|
||||||
height = 400.dp,
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
)
|
|
||||||
EpisodeDetails(
|
|
||||||
episode = episode,
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(horizontal = contentPadding)
|
|
||||||
.offset(y = (-48).dp)
|
|
||||||
.padding(bottom = 96.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EpisodeTopBar(
|
EpisodeTopBar(
|
||||||
|
onBack = onBack,
|
||||||
|
modifier = Modifier
|
||||||
|
)
|
||||||
|
},
|
||||||
|
floatingActionButton = {
|
||||||
|
MediaFloatingPlayButton(
|
||||||
|
containerColor = MaterialTheme.colorScheme.primary,
|
||||||
|
onContainerColor = MaterialTheme.colorScheme.onPrimary,
|
||||||
|
onClick = playAction,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(16.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) { innerPadding ->
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.verticalScroll(rememberScrollState())
|
||||||
|
) {
|
||||||
|
MediaHero(
|
||||||
|
imageUrl = episode.heroImageUrl,
|
||||||
|
backgroundColor = MaterialTheme.colorScheme.background,
|
||||||
|
height = 200.dp,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
)
|
||||||
|
EpisodeDetails(
|
||||||
|
episode = episode,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 16.dp, vertical = 16.dp)
|
.padding(horizontal = 16.dp)
|
||||||
|
.padding(bottom = innerPadding.calculateBottomPadding())
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!isWide) {
|
|
||||||
MediaFloatingPlayButton(
|
|
||||||
containerColor = colors.primary,
|
|
||||||
onContainerColor = colors.onPrimary,
|
|
||||||
onClick = playAction,
|
|
||||||
modifier = Modifier
|
|
||||||
.align(Alignment.BottomEnd)
|
|
||||||
.padding(20.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,6 +111,6 @@ private fun EpisodeScreenInternal(
|
|||||||
fun EpisodeScreenPreview() {
|
fun EpisodeScreenPreview() {
|
||||||
EpisodeScreenInternal(
|
EpisodeScreenInternal(
|
||||||
episode = ContentMockData.episode(),
|
episode = ContentMockData.episode(),
|
||||||
backGroundColor = MaterialTheme.colorScheme.background
|
onBack = {}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,10 @@ import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
|||||||
import androidx.compose.foundation.layout.FlowRow
|
import androidx.compose.foundation.layout.FlowRow
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
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.layout.statusBarsPadding
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.ArrowBack
|
import androidx.compose.material.icons.outlined.ArrowBack
|
||||||
import androidx.compose.material.icons.outlined.Cast
|
import androidx.compose.material.icons.outlined.Cast
|
||||||
@@ -18,7 +21,6 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.text.font.FontWeight
|
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.compose.ui.unit.sp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
|
||||||
import hu.bbara.purefin.common.ui.MediaActionButtons
|
import hu.bbara.purefin.common.ui.MediaActionButtons
|
||||||
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
|
||||||
@@ -29,12 +31,15 @@ import hu.bbara.purefin.common.ui.toMediaDetailColors
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun MovieTopBar(
|
internal fun MovieTopBar(
|
||||||
viewModel: MovieScreenViewModel = hiltViewModel(),
|
onBack: () -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val colors = rememberMovieColors().toMediaDetailColors()
|
val colors = rememberMovieColors().toMediaDetailColors()
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier,
|
modifier = modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.statusBarsPadding()
|
||||||
|
.padding(16.dp),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
@@ -42,7 +47,7 @@ internal fun MovieTopBar(
|
|||||||
colors = colors,
|
colors = colors,
|
||||||
icon = Icons.Outlined.ArrowBack,
|
icon = Icons.Outlined.ArrowBack,
|
||||||
contentDescription = "Back",
|
contentDescription = "Back",
|
||||||
onClick = { viewModel.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(colors = colors, icon = Icons.Outlined.Cast, contentDescription = "Cast", onClick = { })
|
||||||
|
|||||||
@@ -1,24 +1,18 @@
|
|||||||
package hu.bbara.purefin.app.content.movie
|
package hu.bbara.purefin.app.content.movie
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.offset
|
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
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.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
@@ -43,7 +37,9 @@ fun MovieScreen(
|
|||||||
|
|
||||||
if (movieItem.value != null) {
|
if (movieItem.value != null) {
|
||||||
MovieScreenInternal(
|
MovieScreenInternal(
|
||||||
movie = movieItem.value!!, modifier = modifier
|
movie = movieItem.value!!,
|
||||||
|
onBack = viewModel::onBack,
|
||||||
|
modifier = modifier
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
PurefinWaitingScreen()
|
PurefinWaitingScreen()
|
||||||
@@ -53,6 +49,7 @@ fun MovieScreen(
|
|||||||
@Composable
|
@Composable
|
||||||
private fun MovieScreenInternal(
|
private fun MovieScreenInternal(
|
||||||
movie: MovieUiModel,
|
movie: MovieUiModel,
|
||||||
|
onBack: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
@@ -64,85 +61,53 @@ private fun MovieScreenInternal(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BoxWithConstraints(
|
Scaffold(
|
||||||
modifier = modifier
|
modifier = modifier,
|
||||||
.fillMaxSize()
|
containerColor = MaterialTheme.colorScheme.background,
|
||||||
.background(MaterialTheme.colorScheme.background)
|
topBar = {
|
||||||
) {
|
|
||||||
val isWide = maxWidth >= 900.dp
|
|
||||||
val contentPadding = if (isWide) 32.dp else 20.dp
|
|
||||||
|
|
||||||
Box(modifier = Modifier.fillMaxSize()) {
|
|
||||||
if (isWide) {
|
|
||||||
Row(modifier = Modifier.fillMaxSize()) {
|
|
||||||
MediaHero(
|
|
||||||
imageUrl = movie.heroImageUrl,
|
|
||||||
backgroundColor = MaterialTheme.colorScheme.background,
|
|
||||||
height = 300.dp,
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxHeight()
|
|
||||||
.weight(0.5f)
|
|
||||||
)
|
|
||||||
MovieDetails(
|
|
||||||
movie = movie,
|
|
||||||
modifier = Modifier
|
|
||||||
.weight(0.5f)
|
|
||||||
.fillMaxHeight()
|
|
||||||
.verticalScroll(rememberScrollState())
|
|
||||||
.padding(
|
|
||||||
start = contentPadding,
|
|
||||||
end = contentPadding,
|
|
||||||
top = 96.dp,
|
|
||||||
bottom = 32.dp
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.verticalScroll(rememberScrollState())
|
|
||||||
) {
|
|
||||||
MediaHero(
|
|
||||||
imageUrl = movie.heroImageUrl,
|
|
||||||
height = 400.dp,
|
|
||||||
backgroundColor = MaterialTheme.colorScheme.background,
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
)
|
|
||||||
MovieDetails(
|
|
||||||
movie = movie,
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(horizontal = contentPadding)
|
|
||||||
.offset(y = (-48).dp)
|
|
||||||
.padding(bottom = 96.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MovieTopBar(
|
MovieTopBar(
|
||||||
|
onBack = onBack,
|
||||||
|
modifier = Modifier
|
||||||
|
)
|
||||||
|
},
|
||||||
|
floatingActionButton = {
|
||||||
|
MediaFloatingPlayButton(
|
||||||
|
containerColor = MaterialTheme.colorScheme.primary,
|
||||||
|
onContainerColor = MaterialTheme.colorScheme.onPrimary,
|
||||||
|
onClick = playAction,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(16.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) { innerPadding ->
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.verticalScroll(rememberScrollState())
|
||||||
|
) {
|
||||||
|
MediaHero(
|
||||||
|
imageUrl = movie.heroImageUrl,
|
||||||
|
backgroundColor = MaterialTheme.colorScheme.background,
|
||||||
|
height = 200.dp,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
)
|
||||||
|
MovieDetails(
|
||||||
|
movie = movie,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 16.dp, vertical = 16.dp)
|
.padding(horizontal = 16.dp)
|
||||||
|
.padding(bottom = innerPadding.calculateBottomPadding())
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!isWide) {
|
|
||||||
MediaFloatingPlayButton(
|
|
||||||
containerColor = MaterialTheme.colorScheme.primary,
|
|
||||||
onContainerColor = MaterialTheme.colorScheme.onPrimary,
|
|
||||||
|
|
||||||
onClick = playAction,
|
|
||||||
modifier = Modifier
|
|
||||||
.align(Alignment.BottomEnd)
|
|
||||||
.padding(20.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
fun MovieScreenPreview() {
|
fun MovieScreenPreview() {
|
||||||
MovieScreenInternal(movie = ContentMockData.movie())
|
MovieScreenInternal(
|
||||||
|
movie = ContentMockData.movie(),
|
||||||
|
onBack = {}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ 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.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.statusBarsPadding
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.LazyRow
|
import androidx.compose.foundation.lazy.LazyRow
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
@@ -54,18 +55,21 @@ import hu.bbara.purefin.common.ui.toMediaDetailColors
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun SeriesTopBar(
|
internal fun SeriesTopBar(
|
||||||
viewModel: SeriesViewModel = hiltViewModel(),
|
onBack: () -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val colors = rememberSeriesColors().toMediaDetailColors()
|
val colors = rememberSeriesColors().toMediaDetailColors()
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier,
|
modifier = modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.statusBarsPadding()
|
||||||
|
.padding(16.dp),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
MediaGhostIconButton(
|
MediaGhostIconButton(
|
||||||
colors = colors,
|
colors = colors,
|
||||||
onClick = { viewModel.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)) {
|
||||||
|
|||||||
@@ -1,21 +1,19 @@
|
|||||||
package hu.bbara.purefin.app.content.series
|
package hu.bbara.purefin.app.content.series
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
|
||||||
import androidx.compose.foundation.layout.Column
|
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.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.offset
|
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
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.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
@@ -41,6 +39,7 @@ fun SeriesScreen(
|
|||||||
if (series.value != null) {
|
if (series.value != null) {
|
||||||
SeriesScreenInternal(
|
SeriesScreenInternal(
|
||||||
series = series.value!!,
|
series = series.value!!,
|
||||||
|
onBack = viewModel::onBack,
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
@@ -51,16 +50,21 @@ fun SeriesScreen(
|
|||||||
@Composable
|
@Composable
|
||||||
private fun SeriesScreenInternal(
|
private fun SeriesScreenInternal(
|
||||||
series: SeriesUiModel,
|
series: SeriesUiModel,
|
||||||
|
onBack: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val colors = rememberSeriesColors()
|
val colors = rememberSeriesColors()
|
||||||
|
|
||||||
BoxWithConstraints(
|
Scaffold(
|
||||||
modifier = modifier
|
modifier = modifier,
|
||||||
.fillMaxSize()
|
containerColor = MaterialTheme.colorScheme.background,
|
||||||
.background(colors.background)
|
topBar = {
|
||||||
) {
|
SeriesTopBar(
|
||||||
val heroHeight = maxHeight * 0.4f
|
onBack = onBack,
|
||||||
|
modifier = Modifier
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) { innerPadding ->
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
@@ -68,89 +72,73 @@ private fun SeriesScreenInternal(
|
|||||||
) {
|
) {
|
||||||
SeriesHero(
|
SeriesHero(
|
||||||
imageUrl = series.heroImageUrl,
|
imageUrl = series.heroImageUrl,
|
||||||
height = heroHeight
|
height = 200.dp,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
)
|
)
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.offset(y = (-96).dp)
|
.padding(horizontal = 16.dp)
|
||||||
|
.padding(bottom = innerPadding.calculateBottomPadding())
|
||||||
) {
|
) {
|
||||||
Column(
|
Text(
|
||||||
modifier = Modifier
|
text = series.title,
|
||||||
.fillMaxWidth()
|
color = colors.textPrimary,
|
||||||
.padding(horizontal = 20.dp)
|
fontSize = 30.sp,
|
||||||
) {
|
fontWeight = FontWeight.Bold,
|
||||||
Text(
|
lineHeight = 36.sp
|
||||||
text = series.title,
|
)
|
||||||
color = colors.textPrimary,
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
fontSize = 30.sp,
|
SeriesMetaChips(series = series)
|
||||||
fontWeight = FontWeight.Bold,
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
lineHeight = 36.sp
|
SeriesActionButtons()
|
||||||
)
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Text(
|
||||||
SeriesMetaChips(series = series)
|
text = "Synopsis",
|
||||||
Spacer(modifier = Modifier.height(24.dp))
|
color = colors.textPrimary,
|
||||||
SeriesActionButtons()
|
fontSize = 18.sp,
|
||||||
Spacer(modifier = Modifier.height(24.dp))
|
fontWeight = FontWeight.Bold
|
||||||
Text(
|
)
|
||||||
text = "Synopsis",
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
color = colors.textPrimary,
|
Text(
|
||||||
fontSize = 18.sp,
|
text = series.synopsis,
|
||||||
fontWeight = FontWeight.Bold
|
color = colors.textMutedStrong,
|
||||||
)
|
fontSize = 13.sp,
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
)
|
||||||
Text(
|
Spacer(modifier = Modifier.height(28.dp))
|
||||||
text = series.synopsis,
|
Text(
|
||||||
color = colors.textMutedStrong,
|
text = "Episodes",
|
||||||
fontSize = 13.sp,
|
color = colors.textPrimary,
|
||||||
)
|
fontSize = 18.sp,
|
||||||
Spacer(modifier = Modifier.height(28.dp))
|
fontWeight = FontWeight.Bold
|
||||||
Text(
|
)
|
||||||
text = "Episodes",
|
Spacer(modifier = Modifier.height(28.dp))
|
||||||
color = colors.textPrimary,
|
SeasonTabs(seasons = series.seasonTabs)
|
||||||
fontSize = 18.sp,
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
fontWeight = FontWeight.Bold
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(28.dp))
|
|
||||||
SeasonTabs(seasons = series.seasonTabs)
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
|
||||||
}
|
|
||||||
|
|
||||||
EpisodeCarousel(
|
EpisodeCarousel(
|
||||||
episodes = series.seasonTabs.firstOrNull { it.isSelected }?.episodes
|
episodes = series.seasonTabs.firstOrNull { it.isSelected }?.episodes
|
||||||
?: series.seasonTabs.firstOrNull()?.episodes
|
?: series.seasonTabs.firstOrNull()?.episodes
|
||||||
?: emptyList()
|
?: emptyList()
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(32.dp))
|
Spacer(modifier = Modifier.height(32.dp))
|
||||||
Column(
|
Text(
|
||||||
modifier = Modifier
|
text = "Cast",
|
||||||
.fillMaxWidth()
|
color = colors.textPrimary,
|
||||||
.padding(top = 0.dp, bottom = 0.dp)
|
fontSize = 18.sp,
|
||||||
) {
|
fontWeight = FontWeight.Bold
|
||||||
Text(
|
)
|
||||||
text = "Cast",
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
color = colors.textPrimary,
|
CastRow(cast = series.cast)
|
||||||
fontSize = 18.sp,
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
modifier = Modifier.padding(horizontal = 20.dp)
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(12.dp))
|
|
||||||
CastRow(cast = series.cast)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SeriesTopBar(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(horizontal = 16.dp, vertical = 16.dp)
|
|
||||||
.align(Alignment.TopCenter)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
fun SeriesScreenPreview() {
|
fun SeriesScreenPreview() {
|
||||||
SeriesScreenInternal(series = ContentMockData.series())
|
SeriesScreenInternal(
|
||||||
|
series = ContentMockData.series(),
|
||||||
|
onBack = {}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user