fix: stabilize continue watching scroll position

This commit is contained in:
tapframe 2026-07-14 16:20:29 +05:30
parent f74ae8182e
commit 89149fb063
3 changed files with 39 additions and 0 deletions

View file

@ -17,7 +17,9 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.rounded.KeyboardArrowRight
@ -70,6 +72,7 @@ fun <T> NuvioShelfSection(
viewAllPillSize: NuvioViewAllPillSize = NuvioViewAllPillSize.Default,
key: ((T) -> Any)? = null,
animatePlacement: Boolean = false,
state: LazyListState = rememberLazyListState(),
itemContent: @Composable (T) -> Unit,
) {
val tokens = MaterialTheme.nuvio
@ -87,6 +90,7 @@ fun <T> NuvioShelfSection(
)
}
LazyRow(
state = state,
contentPadding = rowContentPadding,
horizontalArrangement = Arrangement.spacedBy(itemSpacing),
) {

View file

@ -9,6 +9,7 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.snapshotFlow
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@ -127,6 +128,7 @@ fun HomeScreen(
HomeCatalogSettingsRepository.uiState
}.collectAsStateWithLifecycle()
val homeListState = rememberLazyListState()
val continueWatchingListState = rememberLazyListState()
val collections by CollectionRepository.collections.collectAsStateWithLifecycle()
val continueWatchingPreferences by ContinueWatchingPreferencesRepository.uiState.collectAsStateWithLifecycle()
val watchedUiState by WatchedRepository.uiState.collectAsStateWithLifecycle()
@ -280,6 +282,13 @@ fun HomeScreen(
val profileState by ProfileRepository.state.collectAsStateWithLifecycle()
val activeProfileId = profileState.activeProfile?.profileIndex ?: 1
val cwCacheGeneration by ContinueWatchingEnrichmentCache.generation.collectAsStateWithLifecycle()
var hasUserScrolledContinueWatching by remember(activeProfileId) { mutableStateOf(false) }
LaunchedEffect(activeProfileId, continueWatchingListState) {
snapshotFlow { continueWatchingListState.isScrollInProgress }.collect { isScrolling ->
if (isScrolling) hasUserScrolledContinueWatching = true
}
}
var nextUpItemsBySeries by remember(activeProfileId, effectiveWatchProgressSource) {
mutableStateOf<Map<String, Pair<Long, ContinueWatchingItem>>>(emptyMap())
@ -427,6 +436,22 @@ fun HomeScreen(
cloudLibraryUiState = cloudLibraryUiState,
)
}
LaunchedEffect(activeProfileId, continueWatchingItems.isNotEmpty(), hasUserScrolledContinueWatching) {
if (!hasUserScrolledContinueWatching && continueWatchingItems.isNotEmpty()) {
snapshotFlow {
continueWatchingListState.firstVisibleItemIndex to
continueWatchingListState.firstVisibleItemScrollOffset
}.collect { (index, offset) ->
if (
!hasUserScrolledContinueWatching &&
!continueWatchingListState.isScrollInProgress &&
(index != 0 || offset != 0)
) {
continueWatchingListState.scrollToItem(0)
}
}
}
}
val enabledAddons = remember(addonsUiState.addons) {
addonsUiState.addons.enabledAddons()
}
@ -853,6 +878,7 @@ fun HomeScreen(
modifier = Modifier.padding(bottom = HomeContinueWatchingSectionBottomPadding),
sectionPadding = homeSectionPadding,
layout = continueWatchingLayout,
listState = continueWatchingListState,
onItemClick = onContinueWatchingClick,
onItemLongPress = onContinueWatchingLongPress,
)
@ -878,6 +904,7 @@ fun HomeScreen(
modifier = Modifier.padding(bottom = HomeContinueWatchingSectionBottomPadding),
sectionPadding = homeSectionPadding,
layout = continueWatchingLayout,
listState = continueWatchingListState,
onItemClick = onContinueWatchingClick,
onItemLongPress = onContinueWatchingLongPress,
)
@ -926,6 +953,7 @@ fun HomeScreen(
modifier = Modifier.padding(bottom = HomeContinueWatchingSectionBottomPadding),
sectionPadding = homeSectionPadding,
layout = continueWatchingLayout,
listState = continueWatchingListState,
onItemClick = onContinueWatchingClick,
onItemLongPress = onContinueWatchingLongPress,
)

View file

@ -19,6 +19,8 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
@ -235,6 +237,7 @@ internal fun HomeContinueWatchingSection(
modifier: Modifier = Modifier,
sectionPadding: Dp? = null,
layout: ContinueWatchingLayout? = null,
listState: LazyListState = rememberLazyListState(),
onItemClick: ((ContinueWatchingItem) -> Unit)? = null,
onItemLongPress: ((ContinueWatchingItem) -> Unit)? = null,
) {
@ -249,6 +252,7 @@ internal fun HomeContinueWatchingSection(
modifier = modifier.fillMaxWidth(),
sectionPadding = sectionPadding,
layout = layout,
listState = listState,
onItemClick = onItemClick,
onItemLongPress = onItemLongPress,
)
@ -262,6 +266,7 @@ internal fun HomeContinueWatchingSection(
modifier = Modifier.fillMaxWidth(),
sectionPadding = homeSectionHorizontalPaddingForWidth(maxWidth.value),
layout = rememberContinueWatchingLayout(maxWidth.value),
listState = listState,
onItemClick = onItemClick,
onItemLongPress = onItemLongPress,
)
@ -278,6 +283,7 @@ private fun HomeContinueWatchingSectionContent(
modifier: Modifier,
sectionPadding: Dp,
layout: ContinueWatchingLayout,
listState: LazyListState,
onItemClick: ((ContinueWatchingItem) -> Unit)?,
onItemLongPress: ((ContinueWatchingItem) -> Unit)?,
) {
@ -299,6 +305,7 @@ private fun HomeContinueWatchingSectionContent(
showHeaderAccent = !homeCatalogSettings.hideCatalogUnderline,
key = { entry -> entry.videoId },
animatePlacement = true,
state = listState,
) { entry ->
val item = entry.item
val onClick = if (entry.exiting) null else onItemClick?.let { { it(item) } }