mirror of
https://github.com/bbara04/Purefin.git
synced 2026-03-31 17:10:08 +02:00
Refactor SeriesScreen components and update UI text for clarity
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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<CastMember>, 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"
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user