mirror of
https://github.com/bbara04/Purefin.git
synced 2026-07-24 03:36:51 +00:00
feat: enhance TvSeriesScreen to support focused season and episode loading
This commit is contained in:
@@ -41,6 +41,7 @@ fun EntryProviderScope<Route>.tvEpisodeSection() {
|
|||||||
entry<Route.EpisodeRoute> { route ->
|
entry<Route.EpisodeRoute> { route ->
|
||||||
TvSeriesScreen(
|
TvSeriesScreen(
|
||||||
series = SeriesDto(id = route.item.seriesId),
|
series = SeriesDto(id = route.item.seriesId),
|
||||||
|
focusedSeasonId = route.item.seasonId,
|
||||||
focusedEpisodeId = route.item.id
|
focusedEpisodeId = route.item.id
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import java.util.UUID
|
|||||||
@Composable
|
@Composable
|
||||||
fun TvSeriesScreen(
|
fun TvSeriesScreen(
|
||||||
series: SeriesDto,
|
series: SeriesDto,
|
||||||
|
focusedSeasonId: UUID? = null,
|
||||||
focusedEpisodeId: UUID? = null,
|
focusedEpisodeId: UUID? = null,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
viewModel: SeriesViewModel = hiltViewModel()
|
viewModel: SeriesViewModel = hiltViewModel()
|
||||||
@@ -52,6 +53,8 @@ fun TvSeriesScreen(
|
|||||||
TvSeriesScreenContent(
|
TvSeriesScreenContent(
|
||||||
series = seriesData,
|
series = seriesData,
|
||||||
onPlayEpisode = viewModel::onPlayEpisode,
|
onPlayEpisode = viewModel::onPlayEpisode,
|
||||||
|
onLoadSeasonEpisodes = viewModel::loadSeasonEpisodes,
|
||||||
|
focusedSeasonId = focusedSeasonId,
|
||||||
focusedEpisodeId = focusedEpisodeId,
|
focusedEpisodeId = focusedEpisodeId,
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
)
|
)
|
||||||
@@ -64,25 +67,28 @@ fun TvSeriesScreen(
|
|||||||
internal fun TvSeriesScreenContent(
|
internal fun TvSeriesScreenContent(
|
||||||
series: Series,
|
series: Series,
|
||||||
onPlayEpisode: (UUID) -> Unit,
|
onPlayEpisode: (UUID) -> Unit,
|
||||||
|
onLoadSeasonEpisodes: (UUID, UUID) -> Unit = { _, _ -> },
|
||||||
|
focusedSeasonId: UUID? = null,
|
||||||
focusedEpisodeId: UUID? = null,
|
focusedEpisodeId: UUID? = null,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val nextUpEpisode = remember(series.id) { series.nextUpEpisode() }
|
val defaultSeason = series.defaultSeason(focusedSeasonId)
|
||||||
val focusedEpisode = remember(series.id, focusedEpisodeId) {
|
var selectedSeasonId by remember(series.id, focusedSeasonId) {
|
||||||
focusedEpisodeId?.let { id ->
|
mutableStateOf(defaultSeason.id)
|
||||||
series.seasons
|
|
||||||
.flatMap { it.episodes }
|
|
||||||
.firstOrNull { it.id == id }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
val initialEpisode = focusedEpisode ?: nextUpEpisode
|
val selectedSeason = series.seasons.firstOrNull { it.id == selectedSeasonId } ?: defaultSeason
|
||||||
var selectedSeason by remember(series.id, initialEpisode?.seasonId) {
|
val initialFocusedEpisodeId = remember(series.id, focusedSeasonId, focusedEpisodeId) {
|
||||||
mutableStateOf(series.defaultSeason(initialEpisode))
|
val focusedEpisode = defaultSeason.episodes.firstOrNull { it.id == focusedEpisodeId }
|
||||||
|
focusedEpisode?.id ?: defaultSeason.nextUpEpisode()?.id
|
||||||
}
|
}
|
||||||
val firstContentFocusRequester = remember { FocusRequester() }
|
val firstContentFocusRequester = remember { FocusRequester() }
|
||||||
|
|
||||||
LaunchedEffect(series.id, initialEpisode?.id) {
|
LaunchedEffect(series.id, selectedSeason.id) {
|
||||||
if (initialEpisode != null) return@LaunchedEffect
|
onLoadSeasonEpisodes(series.id, selectedSeason.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(series.id, initialFocusedEpisodeId) {
|
||||||
|
if (initialFocusedEpisodeId != null) return@LaunchedEffect
|
||||||
withFrameNanos { }
|
withFrameNanos { }
|
||||||
firstContentFocusRequester.requestFocus()
|
firstContentFocusRequester.requestFocus()
|
||||||
}
|
}
|
||||||
@@ -111,14 +117,14 @@ internal fun TvSeriesScreenContent(
|
|||||||
selectedSeason = selectedSeason,
|
selectedSeason = selectedSeason,
|
||||||
firstItemFocusRequester = firstContentFocusRequester,
|
firstItemFocusRequester = firstContentFocusRequester,
|
||||||
firstItemTestTag = SeriesFirstSeasonTabTag,
|
firstItemTestTag = SeriesFirstSeasonTabTag,
|
||||||
onSelect = { selectedSeason = it },
|
onSelect = { selectedSeasonId = it.id },
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
TvEpisodeCarousel(
|
TvEpisodeCarousel(
|
||||||
episodes = selectedSeason.episodes,
|
episodes = selectedSeason.episodes,
|
||||||
onPlayEpisode = { onPlayEpisode(it.id) },
|
onPlayEpisode = { onPlayEpisode(it.id) },
|
||||||
focusedEpisodeId = initialEpisode?.id,
|
focusedEpisodeId = initialFocusedEpisodeId,
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -126,19 +132,14 @@ internal fun TvSeriesScreenContent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Series.defaultSeason(nextUpEpisode: Episode?): Season {
|
private fun Series.defaultSeason(focusedSeasonId: UUID?): Season {
|
||||||
if (nextUpEpisode != null) {
|
if (focusedSeasonId != null) {
|
||||||
seasons.firstOrNull { it.id == nextUpEpisode.seasonId }?.let { return it }
|
seasons.firstOrNull { it.id == focusedSeasonId }?.let { return it }
|
||||||
}
|
}
|
||||||
|
|
||||||
for (season in seasons) {
|
return seasons.firstOrNull { it.unwatchedEpisodeCount > 0 } ?: seasons.first()
|
||||||
if (season.episodes.any { !it.watched }) {
|
|
||||||
return season
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return seasons.first()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Series.nextUpEpisode() = seasons.firstNotNullOfOrNull { season ->
|
private fun Season.nextUpEpisode(): Episode? {
|
||||||
season.episodes.firstOrNull { !it.watched }
|
return episodes.firstOrNull { !it.watched } ?: episodes.firstOrNull()
|
||||||
} ?: seasons.firstOrNull()?.episodes?.firstOrNull()
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user