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("Severance").assertIsDisplayed()
|
||||||
composeRule.onNodeWithText("Overview").assertIsDisplayed()
|
composeRule.onNodeWithText("Overview").assertIsDisplayed()
|
||||||
composeRule.onNodeWithText("Up Next").assertIsDisplayed()
|
composeRule.onNodeWithText("Continue Watching").assertIsDisplayed()
|
||||||
composeRule.onNodeWithTag(SeriesPlayButtonTag).assertIsDisplayed()
|
composeRule.onNodeWithTag(SeriesPlayButtonTag).assertIsDisplayed()
|
||||||
composeRule.onNodeWithText("Season 1").assertIsDisplayed()
|
composeRule.onNodeWithText("Season 1").assertIsDisplayed()
|
||||||
composeRule.onNodeWithText("Good News About Hell").assertIsDisplayed()
|
composeRule.onNodeWithText("Good News About Hell").assertIsDisplayed()
|
||||||
|
composeRule.onNodeWithText("Episode 1 • 57m").assertIsDisplayed()
|
||||||
composeRule.onNodeWithContentDescription("Back")
|
composeRule.onNodeWithContentDescription("Back")
|
||||||
.assertIsDisplayed()
|
.assertIsDisplayed()
|
||||||
.assertIsFocused()
|
.assertIsFocused()
|
||||||
@@ -70,7 +71,7 @@ class SeriesScreenContentTest {
|
|||||||
composeRule.waitForIdle()
|
composeRule.waitForIdle()
|
||||||
|
|
||||||
composeRule.onNodeWithText("Overview").assertIsDisplayed()
|
composeRule.onNodeWithText("Overview").assertIsDisplayed()
|
||||||
composeRule.onNodeWithText("Library Status").assertIsDisplayed()
|
composeRule.onNodeWithText("Choose a season below to start watching.").assertIsDisplayed()
|
||||||
composeRule.onNodeWithTag(SeriesFirstSeasonTabTag).assertIsDisplayed()
|
composeRule.onNodeWithTag(SeriesFirstSeasonTabTag).assertIsDisplayed()
|
||||||
composeRule.onNodeWithContentDescription("Back")
|
composeRule.onNodeWithContentDescription("Back")
|
||||||
.assertIsDisplayed()
|
.assertIsDisplayed()
|
||||||
|
|||||||
@@ -56,7 +56,6 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
|||||||
import hu.bbara.purefin.common.ui.MediaCastRow
|
import hu.bbara.purefin.common.ui.MediaCastRow
|
||||||
import hu.bbara.purefin.common.ui.MediaMetaChip
|
import hu.bbara.purefin.common.ui.MediaMetaChip
|
||||||
import hu.bbara.purefin.common.ui.components.MediaDetailsTopBar
|
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.MediaProgressBar
|
||||||
import hu.bbara.purefin.common.ui.components.MediaResumeButton
|
import hu.bbara.purefin.common.ui.components.MediaResumeButton
|
||||||
import hu.bbara.purefin.common.ui.components.PurefinAsyncImage
|
import hu.bbara.purefin.common.ui.components.PurefinAsyncImage
|
||||||
@@ -228,6 +227,33 @@ internal fun SeriesHeroSection(
|
|||||||
SeriesMetaChips(series = series)
|
SeriesMetaChips(series = series)
|
||||||
Spacer(modifier = Modifier.height(24.dp))
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
if (nextUpEpisode != null) {
|
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(
|
MediaResumeButton(
|
||||||
text = nextUpEpisode.playButtonText(),
|
text = nextUpEpisode.playButtonText(),
|
||||||
progress = nextUpEpisode.progress?.div(100)?.toFloat() ?: 0f,
|
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
|
@Composable
|
||||||
private fun EpisodeCard(
|
private fun EpisodeCard(
|
||||||
viewModel: SeriesViewModel = hiltViewModel(),
|
viewModel: SeriesViewModel = hiltViewModel(),
|
||||||
@@ -409,3 +393,7 @@ internal fun CastRow(cast: List<CastMember>, modifier: Modifier = Modifier) {
|
|||||||
private fun Episode.playButtonText(): String {
|
private fun Episode.playButtonText(): String {
|
||||||
return if ((progress ?: 0.0) > 0.0 && !watched) "Resume" else "Play"
|
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.compose.ui.unit.dp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import hu.bbara.purefin.common.ui.PurefinWaitingScreen
|
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.MediaDetailOverviewSection
|
||||||
import hu.bbara.purefin.common.ui.components.MediaDetailSectionTitle
|
import hu.bbara.purefin.common.ui.components.MediaDetailSectionTitle
|
||||||
import hu.bbara.purefin.common.ui.components.TvMediaDetailScaffold
|
import hu.bbara.purefin.common.ui.components.TvMediaDetailScaffold
|
||||||
import hu.bbara.purefin.core.data.navigation.SeriesDto
|
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.Season
|
||||||
import hu.bbara.purefin.core.model.Series
|
import hu.bbara.purefin.core.model.Series
|
||||||
import hu.bbara.purefin.feature.shared.content.series.SeriesViewModel
|
import hu.bbara.purefin.feature.shared.content.series.SeriesViewModel
|
||||||
@@ -92,25 +90,13 @@ internal fun SeriesScreenContent(
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
heroContent = {
|
heroContent = {
|
||||||
MediaDetailHeaderRow(
|
SeriesHeroSection(
|
||||||
leftContent = { headerModifier ->
|
series = series,
|
||||||
SeriesHeroSection(
|
nextUpEpisode = nextUpEpisode,
|
||||||
series = series,
|
onPlayEpisode = { onPlayEpisode(it.id) },
|
||||||
nextUpEpisode = nextUpEpisode,
|
playFocusRequester = playFocusRequester,
|
||||||
onPlayEpisode = { onPlayEpisode(it.id) },
|
firstContentFocusRequester = firstContentFocusRequester,
|
||||||
playFocusRequester = playFocusRequester,
|
modifier = Modifier.fillMaxWidth()
|
||||||
firstContentFocusRequester = firstContentFocusRequester,
|
|
||||||
modifier = headerModifier
|
|
||||||
)
|
|
||||||
},
|
|
||||||
rightContent = { panelModifier ->
|
|
||||||
SeriesStatusPanel(
|
|
||||||
nextUpEpisode = nextUpEpisode,
|
|
||||||
seasonCount = series.seasonCount,
|
|
||||||
unwatchedEpisodeCount = series.unwatchedEpisodeCount,
|
|
||||||
modifier = panelModifier
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(12.dp))
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
package hu.bbara.purefin.common.ui.components
|
package hu.bbara.purefin.common.ui.components
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.BoxScope
|
import androidx.compose.foundation.layout.BoxScope
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.ColumnScope
|
import androidx.compose.foundation.layout.ColumnScope
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
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
|
||||||
@@ -15,12 +13,10 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.LazyListScope
|
import androidx.compose.foundation.lazy.LazyListScope
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
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.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Brush
|
import androidx.compose.ui.graphics.Brush
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
@@ -29,7 +25,6 @@ import androidx.compose.ui.unit.sp
|
|||||||
import hu.bbara.purefin.common.ui.MediaSynopsis
|
import hu.bbara.purefin.common.ui.MediaSynopsis
|
||||||
|
|
||||||
internal val MediaDetailHorizontalPadding = 48.dp
|
internal val MediaDetailHorizontalPadding = 48.dp
|
||||||
private val MediaDetailPanelShape = RoundedCornerShape(28.dp)
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun TvMediaDetailScaffold(
|
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
|
@Composable
|
||||||
internal fun MediaDetailSectionTitle(
|
internal fun MediaDetailSectionTitle(
|
||||||
text: String,
|
text: String,
|
||||||
|
|||||||
Reference in New Issue
Block a user