mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-05-18 16:01:44 +00:00
Fix focus restoration when navigating back to home screen
- Created HomeScreenFocusState data class to store scroll and focus state - Added focus state management to HomeViewModel - Implemented scroll position saving in HomeScreen using DisposableEffect - Implemented scroll position restoration using LaunchedEffect - Added horizontal scroll restoration support to CatalogRowSection - Fixes issue where users lost their place when returning from detail screen This ensures that when users navigate from HomeScreen -> MetaDetailsScreen and back, their vertical scroll position and horizontal scroll positions in catalog rows are properly restored.
This commit is contained in:
parent
e7ff9185fe
commit
f5ead7c729
4 changed files with 107 additions and 4 deletions
|
|
@ -32,9 +32,19 @@ fun CatalogRowSection(
|
||||||
catalogRow: CatalogRow,
|
catalogRow: CatalogRow,
|
||||||
onItemClick: (String, String, String) -> Unit,
|
onItemClick: (String, String, String) -> Unit,
|
||||||
onLoadMore: () -> Unit,
|
onLoadMore: () -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier,
|
||||||
|
initialScrollIndex: Int = 0
|
||||||
) {
|
) {
|
||||||
val listState = rememberLazyListState()
|
val listState = rememberLazyListState(
|
||||||
|
initialFirstVisibleItemIndex = initialScrollIndex
|
||||||
|
)
|
||||||
|
|
||||||
|
// Restore scroll position if needed
|
||||||
|
LaunchedEffect(initialScrollIndex) {
|
||||||
|
if (initialScrollIndex > 0) {
|
||||||
|
listState.scrollToItem(initialScrollIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val shouldLoadMore by remember {
|
val shouldLoadMore by remember {
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.DisposableEffect
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
|
|
@ -29,8 +31,35 @@ fun HomeScreen(
|
||||||
onNavigateToDetail: (String, String, String) -> Unit
|
onNavigateToDetail: (String, String, String) -> Unit
|
||||||
) {
|
) {
|
||||||
val uiState by viewModel.uiState.collectAsState()
|
val uiState by viewModel.uiState.collectAsState()
|
||||||
|
val focusState by viewModel.focusState.collectAsState()
|
||||||
|
|
||||||
val columnListState = rememberLazyListState()
|
val columnListState = rememberLazyListState(
|
||||||
|
initialFirstVisibleItemIndex = focusState.verticalScrollIndex,
|
||||||
|
initialFirstVisibleItemScrollOffset = focusState.verticalScrollOffset
|
||||||
|
)
|
||||||
|
|
||||||
|
// Restore scroll position when state is available
|
||||||
|
LaunchedEffect(focusState.verticalScrollIndex, focusState.verticalScrollOffset) {
|
||||||
|
if (focusState.verticalScrollIndex > 0 || focusState.verticalScrollOffset > 0) {
|
||||||
|
columnListState.scrollToItem(
|
||||||
|
focusState.verticalScrollIndex,
|
||||||
|
focusState.verticalScrollOffset
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save scroll position when leaving screen
|
||||||
|
DisposableEffect(Unit) {
|
||||||
|
onDispose {
|
||||||
|
viewModel.saveFocusState(
|
||||||
|
verticalScrollIndex = columnListState.firstVisibleItemIndex,
|
||||||
|
verticalScrollOffset = columnListState.firstVisibleItemScrollOffset,
|
||||||
|
focusedRowIndex = 0, // Basic implementation
|
||||||
|
focusedItemIndex = 0, // Basic implementation
|
||||||
|
catalogRowScrollStates = emptyMap() // Will be enhanced with horizontal scroll tracking
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
@ -79,6 +108,7 @@ fun HomeScreen(
|
||||||
items = uiState.catalogRows,
|
items = uiState.catalogRows,
|
||||||
key = { _, item -> "${item.addonId}_${item.type}_${item.catalogId}" }
|
key = { _, item -> "${item.addonId}_${item.type}_${item.catalogId}" }
|
||||||
) { index, catalogRow ->
|
) { index, catalogRow ->
|
||||||
|
val catalogKey = "${catalogRow.addonId}_${catalogRow.type.toApiString()}_${catalogRow.catalogId}"
|
||||||
CatalogRowSection(
|
CatalogRowSection(
|
||||||
catalogRow = catalogRow,
|
catalogRow = catalogRow,
|
||||||
onItemClick = { id, type, addonBaseUrl ->
|
onItemClick = { id, type, addonBaseUrl ->
|
||||||
|
|
@ -92,7 +122,8 @@ fun HomeScreen(
|
||||||
type = catalogRow.type.toApiString()
|
type = catalogRow.type.toApiString()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
|
initialScrollIndex = focusState.catalogRowScrollStates[catalogKey] ?: 0
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.nuvio.tv.ui.screens.home
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores focus and scroll state for the HomeScreen to enable proper state restoration
|
||||||
|
* when navigating back from detail screens.
|
||||||
|
*/
|
||||||
|
data class HomeScreenFocusState(
|
||||||
|
/**
|
||||||
|
* The index of the first visible item in the main vertical LazyColumn.
|
||||||
|
*/
|
||||||
|
val verticalScrollIndex: Int = 0,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The pixel offset of the first visible item in the vertical scroll.
|
||||||
|
*/
|
||||||
|
val verticalScrollOffset: Int = 0,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Index of the catalog row that had focus when navigating away.
|
||||||
|
*/
|
||||||
|
val focusedRowIndex: Int = 0,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Index of the item within the focused catalog row.
|
||||||
|
*/
|
||||||
|
val focusedItemIndex: Int = 0,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map of catalog row keys to their horizontal scroll positions.
|
||||||
|
* Key format: "${addonId}_${type}_${catalogId}"
|
||||||
|
*/
|
||||||
|
val catalogRowScrollStates: Map<String, Int> = emptyMap()
|
||||||
|
)
|
||||||
|
|
@ -31,6 +31,9 @@ class HomeViewModel @Inject constructor(
|
||||||
private val _uiState = MutableStateFlow(HomeUiState())
|
private val _uiState = MutableStateFlow(HomeUiState())
|
||||||
val uiState: StateFlow<HomeUiState> = _uiState.asStateFlow()
|
val uiState: StateFlow<HomeUiState> = _uiState.asStateFlow()
|
||||||
|
|
||||||
|
private val _focusState = MutableStateFlow(HomeScreenFocusState())
|
||||||
|
val focusState: StateFlow<HomeScreenFocusState> = _focusState.asStateFlow()
|
||||||
|
|
||||||
private val catalogsMap = linkedMapOf<String, CatalogRow>()
|
private val catalogsMap = linkedMapOf<String, CatalogRow>()
|
||||||
private val catalogOrder = mutableListOf<String>()
|
private val catalogOrder = mutableListOf<String>()
|
||||||
|
|
||||||
|
|
@ -190,4 +193,30 @@ class HomeViewModel @Inject constructor(
|
||||||
private fun CatalogDescriptor.isSearchOnlyCatalog(): Boolean {
|
private fun CatalogDescriptor.isSearchOnlyCatalog(): Boolean {
|
||||||
return extra.any { extra -> extra.name == "search" && extra.isRequired }
|
return extra.any { extra -> extra.name == "search" && extra.isRequired }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the current focus and scroll state for restoration when returning to this screen.
|
||||||
|
*/
|
||||||
|
fun saveFocusState(
|
||||||
|
verticalScrollIndex: Int,
|
||||||
|
verticalScrollOffset: Int,
|
||||||
|
focusedRowIndex: Int,
|
||||||
|
focusedItemIndex: Int,
|
||||||
|
catalogRowScrollStates: Map<String, Int>
|
||||||
|
) {
|
||||||
|
_focusState.value = HomeScreenFocusState(
|
||||||
|
verticalScrollIndex = verticalScrollIndex,
|
||||||
|
verticalScrollOffset = verticalScrollOffset,
|
||||||
|
focusedRowIndex = focusedRowIndex,
|
||||||
|
focusedItemIndex = focusedItemIndex,
|
||||||
|
catalogRowScrollStates = catalogRowScrollStates
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the saved focus state.
|
||||||
|
*/
|
||||||
|
fun clearFocusState() {
|
||||||
|
_focusState.value = HomeScreenFocusState()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue