mirror of
https://github.com/bbara04/Purefin.git
synced 2026-07-24 03:36:51 +00:00
feat(tvseries): improve focus management for season and episode selection
This commit is contained in:
@@ -77,20 +77,22 @@ internal fun TvSeriesScreenContent(
|
|||||||
mutableStateOf(defaultSeason.id)
|
mutableStateOf(defaultSeason.id)
|
||||||
}
|
}
|
||||||
val selectedSeason = series.seasons.firstOrNull { it.id == selectedSeasonId } ?: defaultSeason
|
val selectedSeason = series.seasons.firstOrNull { it.id == selectedSeasonId } ?: defaultSeason
|
||||||
val initialFocusedEpisodeId = remember(series.id, focusedSeasonId, focusedEpisodeId) {
|
val initialFocusSeasonId = remember(series.id, focusedSeasonId) { defaultSeason.id }
|
||||||
val focusedEpisode = defaultSeason.episodes.firstOrNull { it.id == focusedEpisodeId }
|
val initialFocusSeason = series.seasons.firstOrNull { it.id == initialFocusSeasonId } ?: defaultSeason
|
||||||
focusedEpisode?.id ?: defaultSeason.nextUpEpisode()?.id
|
val initialFocusedEpisodeId = initialFocusSeason.focusTargetEpisodeId(focusedEpisodeId)
|
||||||
}
|
val seasonTabFocusRequester = remember { FocusRequester() }
|
||||||
val firstContentFocusRequester = remember { FocusRequester() }
|
val waitingForInitialEpisodes = initialFocusedEpisodeId == null &&
|
||||||
|
initialFocusSeason.episodes.isEmpty() &&
|
||||||
|
initialFocusSeason.episodeCount > 0
|
||||||
|
|
||||||
LaunchedEffect(series.id, selectedSeason.id) {
|
LaunchedEffect(series.id, selectedSeason.id) {
|
||||||
onLoadSeasonEpisodes(series.id, selectedSeason.id)
|
onLoadSeasonEpisodes(series.id, selectedSeason.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(series.id, initialFocusedEpisodeId) {
|
LaunchedEffect(series.id, initialFocusedEpisodeId, waitingForInitialEpisodes) {
|
||||||
if (initialFocusedEpisodeId != null) return@LaunchedEffect
|
if (initialFocusedEpisodeId != null || waitingForInitialEpisodes) return@LaunchedEffect
|
||||||
withFrameNanos { }
|
withFrameNanos { }
|
||||||
firstContentFocusRequester.requestFocus()
|
seasonTabFocusRequester.requestFocus()
|
||||||
}
|
}
|
||||||
|
|
||||||
TvMediaDetailScaffold(
|
TvMediaDetailScaffold(
|
||||||
@@ -115,7 +117,7 @@ internal fun TvSeriesScreenContent(
|
|||||||
TvSeasonTabs(
|
TvSeasonTabs(
|
||||||
seasons = series.seasons,
|
seasons = series.seasons,
|
||||||
selectedSeason = selectedSeason,
|
selectedSeason = selectedSeason,
|
||||||
firstItemFocusRequester = firstContentFocusRequester,
|
selectedItemFocusRequester = seasonTabFocusRequester,
|
||||||
firstItemTestTag = SeriesFirstSeasonTabTag,
|
firstItemTestTag = SeriesFirstSeasonTabTag,
|
||||||
onSelect = { selectedSeasonId = it.id },
|
onSelect = { selectedSeasonId = it.id },
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
@@ -143,3 +145,8 @@ private fun Series.defaultSeason(focusedSeasonId: UUID?): Season {
|
|||||||
private fun Season.nextUpEpisode(): Episode? {
|
private fun Season.nextUpEpisode(): Episode? {
|
||||||
return episodes.firstOrNull { !it.watched } ?: episodes.firstOrNull()
|
return episodes.firstOrNull { !it.watched } ?: episodes.firstOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun Season.focusTargetEpisodeId(focusedEpisodeId: UUID?): UUID? {
|
||||||
|
val focusedEpisode = episodes.firstOrNull { it.id == focusedEpisodeId }
|
||||||
|
return focusedEpisode?.id ?: nextUpEpisode()?.id
|
||||||
|
}
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ internal fun TvSeasonTabs(
|
|||||||
seasons: List<Season>,
|
seasons: List<Season>,
|
||||||
selectedSeason: Season?,
|
selectedSeason: Season?,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
firstItemFocusRequester: FocusRequester? = null,
|
selectedItemFocusRequester: FocusRequester? = null,
|
||||||
firstItemTestTag: String? = null,
|
firstItemTestTag: String? = null,
|
||||||
onSelect: (Season) -> Unit
|
onSelect: (Season) -> Unit
|
||||||
) {
|
) {
|
||||||
@@ -104,8 +104,8 @@ internal fun TvSeasonTabs(
|
|||||||
colors = TabDefaults.underlinedIndicatorTabColors(),
|
colors = TabDefaults.underlinedIndicatorTabColors(),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.then(
|
.then(
|
||||||
if (index == 0 && firstItemFocusRequester != null) {
|
if (index == selectedSeasonIndex && selectedItemFocusRequester != null) {
|
||||||
Modifier.focusRequester(firstItemFocusRequester)
|
Modifier.focusRequester(selectedItemFocusRequester)
|
||||||
} else {
|
} else {
|
||||||
Modifier
|
Modifier
|
||||||
}
|
}
|
||||||
@@ -145,6 +145,7 @@ internal fun TvEpisodeCarousel(
|
|||||||
) {
|
) {
|
||||||
val listState = rememberLazyListState()
|
val listState = rememberLazyListState()
|
||||||
val focusedEpisodeFocusRequester = remember { FocusRequester() }
|
val focusedEpisodeFocusRequester = remember { FocusRequester() }
|
||||||
|
var requestedFocusEpisodeId by remember { mutableStateOf<UUID?>(null) }
|
||||||
|
|
||||||
LaunchedEffect(episodes, focusedEpisodeId) {
|
LaunchedEffect(episodes, focusedEpisodeId) {
|
||||||
val focusedEpisodeIndex = focusedEpisodeId?.let { id ->
|
val focusedEpisodeIndex = focusedEpisodeId?.let { id ->
|
||||||
@@ -159,9 +160,10 @@ internal fun TvEpisodeCarousel(
|
|||||||
listState.scrollToItem(0)
|
listState.scrollToItem(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (focusedEpisodeIndex >= 0) {
|
if (focusedEpisodeIndex >= 0 && requestedFocusEpisodeId != focusedEpisodeId) {
|
||||||
withFrameNanos { }
|
withFrameNanos { }
|
||||||
focusedEpisodeFocusRequester.requestFocus()
|
focusedEpisodeFocusRequester.requestFocus()
|
||||||
|
requestedFocusEpisodeId = focusedEpisodeId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user