home: Make the "New local videos" row reachable by DOWN
On the TV, DOWN from "Continue watching" often skipped the row below and landed on "New from your subscriptions": the new-videos row is rendered as a non-focusable "Loading…" while its items are still in flight, and every FocusScrollRow auto-focused its first card when its items arrived, so rows loaded out of order stole focus in a race. Start fetching new local videos eagerly with the rest of the home data, restrict the load-time auto-focus to the first row only, and cancel it when the user has already pressed a D-pad key meanwhile. Also rename the row to "New local videos" to distinguish it from the federated subscription feed. Refs #3
This commit is contained in:
parent
20e6b07107
commit
1c8fff7eca
1 changed files with 31 additions and 5 deletions
|
|
@ -26,6 +26,7 @@ import androidx.compose.runtime.setValue
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.input.key.onPreviewKeyEvent
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
|
|
@ -76,6 +77,10 @@ class HomeRowsViewModel(private val repository: PeerTubeRepository) : ViewModel(
|
|||
if (loaded) return
|
||||
loaded = true
|
||||
fetchContinue()
|
||||
// Populate "New local videos" while the (slow, 6 detail calls) continue row is
|
||||
// still resolving (#3): a user pressing DOWN too early skipped the row while it
|
||||
// was empty and landed on the row below it.
|
||||
recent.loadMore()
|
||||
}
|
||||
|
||||
/** Re-fetch after playback changed server-side positions (UX report BUG-7).
|
||||
|
|
@ -159,10 +164,21 @@ fun HomeRowsScreen(
|
|||
) {
|
||||
val continueRestore = androidx.compose.runtime.remember { androidx.compose.runtime.mutableIntStateOf(-1) }
|
||||
val recentRestore = androidx.compose.runtime.remember { androidx.compose.runtime.mutableIntStateOf(-1) }
|
||||
// Any D-pad press before the (slow) continue row resolves means the user is already
|
||||
// navigating: suppress the then-pending auto-focus of the first row (#3).
|
||||
var userNavigated by remember { mutableStateOf(false) }
|
||||
LaunchedEffect(Unit) { viewModel.load() }
|
||||
LaunchedEffect(restoreToken) { if (restoreToken > 0) viewModel.refresh() }
|
||||
val recentItems = viewModel.recent.items
|
||||
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
// Peek at D-pad input without consuming it: marks that the user is nav.
|
||||
.onPreviewKeyEvent {
|
||||
userNavigated = true
|
||||
false
|
||||
},
|
||||
) {
|
||||
item {
|
||||
Text(
|
||||
text = "Continue watching",
|
||||
|
|
@ -191,12 +207,14 @@ fun HomeRowsScreen(
|
|||
clickedIndex = continueRestore,
|
||||
restoreToken = restoreToken,
|
||||
upToTopBar = upToTopBar,
|
||||
autoFocusOnLoad = true,
|
||||
autoFocusAllowed = { !userNavigated },
|
||||
)
|
||||
}
|
||||
}
|
||||
item {
|
||||
Text(
|
||||
text = "New videos",
|
||||
text = "New local videos",
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = Color.White,
|
||||
modifier = Modifier.padding(start = 16.dp, top = 16.dp, bottom = 4.dp),
|
||||
|
|
@ -261,6 +279,11 @@ fun FocusScrollRow(
|
|||
clickedIndex: androidx.compose.runtime.MutableIntState? = null, // set on card click
|
||||
restoreToken: Int = 0, // bumped when playback ends
|
||||
upToTopBar: (() -> Unit)? = null, // first content row only
|
||||
// Only the FIRST row auto-focuses its first card when items arrive — every other
|
||||
// row would otherwise steal focus whenever it finishes loading (#3).
|
||||
autoFocusOnLoad: Boolean = false,
|
||||
// Screen-level gate: false while the user is already navigating.
|
||||
autoFocusAllowed: () -> Boolean = { true },
|
||||
afterItem: @Composable (index: Int, video: VideoDto) -> Unit = { _, _ -> },
|
||||
) {
|
||||
val rowState = rememberLazyListState()
|
||||
|
|
@ -280,13 +303,16 @@ fun FocusScrollRow(
|
|||
}
|
||||
}
|
||||
// On app start and destination switch: focus the FIRST card automatically
|
||||
// (user request — otherwise invisible focus confuses "where am I").
|
||||
// (user request — otherwise invisible focus confuses "where am I"). Rows that
|
||||
// finish loading later must not steal focus from the user (#3).
|
||||
LaunchedEffect(videos.isNotEmpty()) {
|
||||
if (videos.isNotEmpty() && focusedIndex == -1) {
|
||||
if (videos.isNotEmpty() && autoFocusOnLoad && focusedIndex == -1) {
|
||||
kotlinx.coroutines.delay(150) // let images/measuring settle
|
||||
if (autoFocusAllowed()) {
|
||||
runCatching { focusRequesters[0]?.requestFocus() }
|
||||
}
|
||||
}
|
||||
}
|
||||
LaunchedEffect(focusedIndex) {
|
||||
if (focusedIndex > 0) {
|
||||
val visible = rowState.layoutInfo.visibleItemsInfo
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue