diff --git a/app-tv/src/androidTest/java/hu/bbara/purefin/app/content/series/SeriesScreenContentTest.kt b/app-tv/src/androidTest/java/hu/bbara/purefin/app/content/series/SeriesScreenContentTest.kt index 0bbaf43..3ba621a 100644 --- a/app-tv/src/androidTest/java/hu/bbara/purefin/app/content/series/SeriesScreenContentTest.kt +++ b/app-tv/src/androidTest/java/hu/bbara/purefin/app/content/series/SeriesScreenContentTest.kt @@ -42,10 +42,11 @@ class SeriesScreenContentTest { composeRule.onNodeWithText("Severance").assertIsDisplayed() composeRule.onNodeWithText("Overview").assertIsDisplayed() - composeRule.onNodeWithText("Up Next").assertIsDisplayed() + composeRule.onNodeWithText("Continue Watching").assertIsDisplayed() composeRule.onNodeWithTag(SeriesPlayButtonTag).assertIsDisplayed() composeRule.onNodeWithText("Season 1").assertIsDisplayed() composeRule.onNodeWithText("Good News About Hell").assertIsDisplayed() + composeRule.onNodeWithText("Episode 1 • 57m").assertIsDisplayed() composeRule.onNodeWithContentDescription("Back") .assertIsDisplayed() .assertIsFocused() @@ -70,7 +71,7 @@ class SeriesScreenContentTest { composeRule.waitForIdle() composeRule.onNodeWithText("Overview").assertIsDisplayed() - composeRule.onNodeWithText("Library Status").assertIsDisplayed() + composeRule.onNodeWithText("Choose a season below to start watching.").assertIsDisplayed() composeRule.onNodeWithTag(SeriesFirstSeasonTabTag).assertIsDisplayed() composeRule.onNodeWithContentDescription("Back") .assertIsDisplayed() diff --git a/app-tv/src/main/java/hu/bbara/purefin/app/content/series/SeriesComponents.kt b/app-tv/src/main/java/hu/bbara/purefin/app/content/series/SeriesComponents.kt index 3611675..7467b23 100644 --- a/app-tv/src/main/java/hu/bbara/purefin/app/content/series/SeriesComponents.kt +++ b/app-tv/src/main/java/hu/bbara/purefin/app/content/series/SeriesComponents.kt @@ -56,7 +56,6 @@ import androidx.hilt.navigation.compose.hiltViewModel import hu.bbara.purefin.common.ui.MediaCastRow import hu.bbara.purefin.common.ui.MediaMetaChip import hu.bbara.purefin.common.ui.components.MediaDetailsTopBar -import hu.bbara.purefin.common.ui.components.MediaDetailSectionTitle import hu.bbara.purefin.common.ui.components.MediaProgressBar import hu.bbara.purefin.common.ui.components.MediaResumeButton import hu.bbara.purefin.common.ui.components.PurefinAsyncImage @@ -228,6 +227,33 @@ internal fun SeriesHeroSection( SeriesMetaChips(series = series) Spacer(modifier = Modifier.height(24.dp)) if (nextUpEpisode != null) { + Text( + text = nextUpEpisode.heroStatusText(), + color = scheme.primary, + fontSize = 18.sp, + fontWeight = FontWeight.SemiBold, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + Spacer(modifier = Modifier.height(10.dp)) + Text( + text = nextUpEpisode.title, + color = scheme.onBackground, + fontSize = 22.sp, + fontWeight = FontWeight.SemiBold, + maxLines = 2, + overflow = TextOverflow.Ellipsis + ) + Spacer(modifier = Modifier.height(6.dp)) + Text( + text = "Episode ${nextUpEpisode.index} • ${nextUpEpisode.runtime}", + color = mutedStrong, + fontSize = 14.sp, + fontWeight = FontWeight.Medium, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + Spacer(modifier = Modifier.height(24.dp)) MediaResumeButton( text = nextUpEpisode.playButtonText(), progress = nextUpEpisode.progress?.div(100)?.toFloat() ?: 0f, @@ -249,48 +275,6 @@ internal fun SeriesHeroSection( } } -@Composable -internal fun SeriesStatusPanel( - nextUpEpisode: Episode?, - seasonCount: Int, - unwatchedEpisodeCount: Int, - modifier: Modifier = Modifier -) { - val scheme = MaterialTheme.colorScheme - val mutedStrong = scheme.onSurfaceVariant.copy(alpha = 0.85f) - - Column(modifier = modifier) { - MediaDetailSectionTitle( - text = if (nextUpEpisode != null) "Up Next" else "Library Status", - modifier = Modifier.fillMaxWidth() - ) - Spacer(modifier = Modifier.height(14.dp)) - Text( - text = if (nextUpEpisode != null) { - nextUpEpisode.title - } else { - "$seasonCount seasons ready to browse" - }, - color = scheme.onSurface, - fontSize = 18.sp, - fontWeight = FontWeight.SemiBold, - maxLines = 2, - overflow = TextOverflow.Ellipsis - ) - Spacer(modifier = Modifier.height(6.dp)) - Text( - text = if (nextUpEpisode != null) { - "Episode ${nextUpEpisode.index} • ${nextUpEpisode.runtime}" - } else { - "$unwatchedEpisodeCount unwatched episodes" - }, - color = mutedStrong, - fontSize = 14.sp, - fontWeight = FontWeight.Medium - ) - } -} - @Composable private fun EpisodeCard( viewModel: SeriesViewModel = hiltViewModel(), @@ -409,3 +393,7 @@ internal fun CastRow(cast: List, modifier: Modifier = Modifier) { private fun Episode.playButtonText(): String { return if ((progress ?: 0.0) > 0.0 && !watched) "Resume" else "Play" } + +private fun Episode.heroStatusText(): String { + return if ((progress ?: 0.0) > 0.0 && !watched) "Continue Watching" else "Up Next" +} diff --git a/app-tv/src/main/java/hu/bbara/purefin/app/content/series/SeriesScreen.kt b/app-tv/src/main/java/hu/bbara/purefin/app/content/series/SeriesScreen.kt index ef8bee1..9b77274 100644 --- a/app-tv/src/main/java/hu/bbara/purefin/app/content/series/SeriesScreen.kt +++ b/app-tv/src/main/java/hu/bbara/purefin/app/content/series/SeriesScreen.kt @@ -15,12 +15,10 @@ import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import hu.bbara.purefin.common.ui.PurefinWaitingScreen -import hu.bbara.purefin.common.ui.components.MediaDetailHeaderRow import hu.bbara.purefin.common.ui.components.MediaDetailOverviewSection import hu.bbara.purefin.common.ui.components.MediaDetailSectionTitle import hu.bbara.purefin.common.ui.components.TvMediaDetailScaffold import hu.bbara.purefin.core.data.navigation.SeriesDto -import hu.bbara.purefin.core.model.Episode import hu.bbara.purefin.core.model.Season import hu.bbara.purefin.core.model.Series import hu.bbara.purefin.feature.shared.content.series.SeriesViewModel @@ -92,25 +90,13 @@ internal fun SeriesScreenContent( ) }, heroContent = { - MediaDetailHeaderRow( - leftContent = { headerModifier -> - SeriesHeroSection( - series = series, - nextUpEpisode = nextUpEpisode, - onPlayEpisode = { onPlayEpisode(it.id) }, - playFocusRequester = playFocusRequester, - firstContentFocusRequester = firstContentFocusRequester, - modifier = headerModifier - ) - }, - rightContent = { panelModifier -> - SeriesStatusPanel( - nextUpEpisode = nextUpEpisode, - seasonCount = series.seasonCount, - unwatchedEpisodeCount = series.unwatchedEpisodeCount, - modifier = panelModifier - ) - } + SeriesHeroSection( + series = series, + nextUpEpisode = nextUpEpisode, + onPlayEpisode = { onPlayEpisode(it.id) }, + playFocusRequester = playFocusRequester, + firstContentFocusRequester = firstContentFocusRequester, + modifier = Modifier.fillMaxWidth() ) Spacer(modifier = Modifier.height(12.dp)) } diff --git a/app-tv/src/main/java/hu/bbara/purefin/common/ui/components/MediaDetailShell.kt b/app-tv/src/main/java/hu/bbara/purefin/common/ui/components/MediaDetailShell.kt index a84346b..6685e07 100644 --- a/app-tv/src/main/java/hu/bbara/purefin/common/ui/components/MediaDetailShell.kt +++ b/app-tv/src/main/java/hu/bbara/purefin/common/ui/components/MediaDetailShell.kt @@ -1,12 +1,10 @@ package hu.bbara.purefin.common.ui.components import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -15,12 +13,10 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Brush import androidx.compose.ui.text.font.FontWeight @@ -29,7 +25,6 @@ import androidx.compose.ui.unit.sp import hu.bbara.purefin.common.ui.MediaSynopsis internal val MediaDetailHorizontalPadding = 48.dp -private val MediaDetailPanelShape = RoundedCornerShape(28.dp) @Composable internal fun TvMediaDetailScaffold( @@ -81,37 +76,6 @@ internal fun TvMediaDetailScaffold( } } -@Composable -internal fun MediaDetailHeaderRow( - modifier: Modifier = Modifier, - leftWeight: Float = 1.1f, - rightWeight: Float = 0.9f, - verticalAlignment: Alignment.Vertical = Alignment.Bottom, - leftContent: @Composable (Modifier) -> Unit, - rightContent: @Composable ColumnScope.(Modifier) -> Unit -) { - val scheme = MaterialTheme.colorScheme - - Row( - modifier = modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(40.dp), - verticalAlignment = verticalAlignment - ) { - leftContent(Modifier.weight(leftWeight)) - Column( - modifier = Modifier - .weight(rightWeight) - .background( - color = scheme.surface.copy(alpha = 0.9f), - shape = MediaDetailPanelShape - ) - .padding(28.dp) - ) { - rightContent(Modifier.fillMaxWidth()) - } - } -} - @Composable internal fun MediaDetailSectionTitle( text: String,