ref(settings): save scrollstate

This commit is contained in:
tapframe 2026-05-05 13:04:51 +05:30
parent b2df0dfd91
commit 17b1b46164

View file

@ -16,9 +16,10 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.statusBars import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
@ -30,6 +31,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.saveable.rememberSaveableStateHolder
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@ -296,118 +298,121 @@ private fun MobileSettingsScreen(
onCheckForUpdatesClick: (() -> Unit)? = null, onCheckForUpdatesClick: (() -> Unit)? = null,
onCollectionsClick: () -> Unit = {}, onCollectionsClick: () -> Unit = {},
) { ) {
NuvioScreen { val saveableStateHolder = rememberSaveableStateHolder()
stickyHeader { saveableStateHolder.SaveableStateProvider(page.name) {
val previousPage = page.previousPage() NuvioScreen {
NuvioScreenHeader( stickyHeader {
title = stringResource(page.titleRes), val previousPage = page.previousPage()
onBack = previousPage?.let { { onPageChange(it) } }, NuvioScreenHeader(
) title = stringResource(page.titleRes),
} onBack = previousPage?.let { { onPageChange(it) } },
)
}
when (page) { when (page) {
SettingsPage.Root -> settingsRootContent( SettingsPage.Root -> settingsRootContent(
isTablet = false, isTablet = false,
onPlaybackClick = { onPageChange(SettingsPage.Playback) }, onPlaybackClick = { onPageChange(SettingsPage.Playback) },
onAppearanceClick = { onPageChange(SettingsPage.Appearance) }, onAppearanceClick = { onPageChange(SettingsPage.Appearance) },
onNotificationsClick = { onPageChange(SettingsPage.Notifications) }, onNotificationsClick = { onPageChange(SettingsPage.Notifications) },
onContentDiscoveryClick = { onPageChange(SettingsPage.ContentDiscovery) }, onContentDiscoveryClick = { onPageChange(SettingsPage.ContentDiscovery) },
onIntegrationsClick = { onPageChange(SettingsPage.Integrations) }, onIntegrationsClick = { onPageChange(SettingsPage.Integrations) },
onTraktClick = { onPageChange(SettingsPage.TraktAuthentication) }, onTraktClick = { onPageChange(SettingsPage.TraktAuthentication) },
onSupportersContributorsClick = onSupportersContributorsClick, onSupportersContributorsClick = onSupportersContributorsClick,
onCheckForUpdatesClick = onCheckForUpdatesClick, onCheckForUpdatesClick = onCheckForUpdatesClick,
onDownloadsClick = onDownloadsClick, onDownloadsClick = onDownloadsClick,
onAccountClick = onAccountClick, onAccountClick = onAccountClick,
onSwitchProfileClick = onSwitchProfile, onSwitchProfileClick = onSwitchProfile,
) )
SettingsPage.Account -> accountSettingsContent( SettingsPage.Account -> accountSettingsContent(
isTablet = false, isTablet = false,
) )
SettingsPage.SupportersContributors -> supportersContributorsContent( SettingsPage.SupportersContributors -> supportersContributorsContent(
isTablet = false, isTablet = false,
) )
SettingsPage.Playback -> playbackSettingsContent( SettingsPage.Playback -> playbackSettingsContent(
isTablet = false, isTablet = false,
showLoadingOverlay = showLoadingOverlay, showLoadingOverlay = showLoadingOverlay,
holdToSpeedEnabled = holdToSpeedEnabled, holdToSpeedEnabled = holdToSpeedEnabled,
holdToSpeedValue = holdToSpeedValue, holdToSpeedValue = holdToSpeedValue,
preferredAudioLanguage = preferredAudioLanguage, preferredAudioLanguage = preferredAudioLanguage,
secondaryPreferredAudioLanguage = secondaryPreferredAudioLanguage, secondaryPreferredAudioLanguage = secondaryPreferredAudioLanguage,
preferredSubtitleLanguage = preferredSubtitleLanguage, preferredSubtitleLanguage = preferredSubtitleLanguage,
secondaryPreferredSubtitleLanguage = secondaryPreferredSubtitleLanguage, secondaryPreferredSubtitleLanguage = secondaryPreferredSubtitleLanguage,
streamReuseLastLinkEnabled = streamReuseLastLinkEnabled, streamReuseLastLinkEnabled = streamReuseLastLinkEnabled,
streamReuseLastLinkCacheHours = streamReuseLastLinkCacheHours, streamReuseLastLinkCacheHours = streamReuseLastLinkCacheHours,
decoderPriority = decoderPriority, decoderPriority = decoderPriority,
mapDV7ToHevc = mapDV7ToHevc, mapDV7ToHevc = mapDV7ToHevc,
tunnelingEnabled = tunnelingEnabled, tunnelingEnabled = tunnelingEnabled,
useLibass = useLibass, useLibass = useLibass,
libassRenderType = libassRenderType, libassRenderType = libassRenderType,
) )
SettingsPage.Appearance -> appearanceSettingsContent( SettingsPage.Appearance -> appearanceSettingsContent(
isTablet = false, isTablet = false,
selectedTheme = selectedTheme, selectedTheme = selectedTheme,
onThemeSelected = onThemeSelected, onThemeSelected = onThemeSelected,
amoledEnabled = amoledEnabled, amoledEnabled = amoledEnabled,
onAmoledToggle = onAmoledToggle, onAmoledToggle = onAmoledToggle,
selectedAppLanguage = selectedAppLanguage, selectedAppLanguage = selectedAppLanguage,
onAppLanguageSelected = onAppLanguageSelected, onAppLanguageSelected = onAppLanguageSelected,
onContinueWatchingClick = onContinueWatchingClick, onContinueWatchingClick = onContinueWatchingClick,
onPosterCustomizationClick = { onPageChange(SettingsPage.PosterCustomization) }, onPosterCustomizationClick = { onPageChange(SettingsPage.PosterCustomization) },
) )
SettingsPage.Notifications -> notificationsSettingsContent( SettingsPage.Notifications -> notificationsSettingsContent(
isTablet = false, isTablet = false,
uiState = episodeReleaseNotificationsUiState, uiState = episodeReleaseNotificationsUiState,
) )
SettingsPage.ContinueWatching -> continueWatchingSettingsContent( SettingsPage.ContinueWatching -> continueWatchingSettingsContent(
isTablet = false, isTablet = false,
isVisible = continueWatchingPreferencesUiState.isVisible, isVisible = continueWatchingPreferencesUiState.isVisible,
style = continueWatchingPreferencesUiState.style, style = continueWatchingPreferencesUiState.style,
upNextFromFurthestEpisode = continueWatchingPreferencesUiState.upNextFromFurthestEpisode, upNextFromFurthestEpisode = continueWatchingPreferencesUiState.upNextFromFurthestEpisode,
showResumePromptOnLaunch = continueWatchingPreferencesUiState.showResumePromptOnLaunch, showResumePromptOnLaunch = continueWatchingPreferencesUiState.showResumePromptOnLaunch,
) )
SettingsPage.PosterCustomization -> posterCustomizationSettingsContent( SettingsPage.PosterCustomization -> posterCustomizationSettingsContent(
isTablet = false, isTablet = false,
uiState = posterCardStyleUiState, uiState = posterCardStyleUiState,
) )
SettingsPage.ContentDiscovery -> contentDiscoveryContent( SettingsPage.ContentDiscovery -> contentDiscoveryContent(
isTablet = false, isTablet = false,
showPluginsEntry = AppFeaturePolicy.pluginsEnabled, showPluginsEntry = AppFeaturePolicy.pluginsEnabled,
onAddonsClick = onAddonsClick, onAddonsClick = onAddonsClick,
onPluginsClick = onPluginsClick, onPluginsClick = onPluginsClick,
onHomescreenClick = onHomescreenClick, onHomescreenClick = onHomescreenClick,
onMetaScreenClick = onMetaScreenClick, onMetaScreenClick = onMetaScreenClick,
onCollectionsClick = onCollectionsClick, onCollectionsClick = onCollectionsClick,
) )
SettingsPage.Addons -> addonsSettingsContent() SettingsPage.Addons -> addonsSettingsContent()
SettingsPage.Plugins -> if (AppFeaturePolicy.pluginsEnabled) pluginsSettingsContent() else addonsSettingsContent() SettingsPage.Plugins -> if (AppFeaturePolicy.pluginsEnabled) pluginsSettingsContent() else addonsSettingsContent()
SettingsPage.Homescreen -> homescreenSettingsContent( SettingsPage.Homescreen -> homescreenSettingsContent(
isTablet = false, isTablet = false,
heroEnabled = homescreenHeroEnabled, heroEnabled = homescreenHeroEnabled,
items = homescreenItems, items = homescreenItems,
) )
SettingsPage.MetaScreen -> metaScreenSettingsContent( SettingsPage.MetaScreen -> metaScreenSettingsContent(
isTablet = false, isTablet = false,
uiState = metaScreenSettingsUiState, uiState = metaScreenSettingsUiState,
) )
SettingsPage.Integrations -> integrationsContent( SettingsPage.Integrations -> integrationsContent(
isTablet = false, isTablet = false,
onTmdbClick = { onPageChange(SettingsPage.TmdbEnrichment) }, onTmdbClick = { onPageChange(SettingsPage.TmdbEnrichment) },
onMdbListClick = { onPageChange(SettingsPage.MdbListRatings) }, onMdbListClick = { onPageChange(SettingsPage.MdbListRatings) },
) )
SettingsPage.TmdbEnrichment -> tmdbSettingsContent( SettingsPage.TmdbEnrichment -> tmdbSettingsContent(
isTablet = false, isTablet = false,
settings = tmdbSettings, settings = tmdbSettings,
) )
SettingsPage.MdbListRatings -> mdbListSettingsContent( SettingsPage.MdbListRatings -> mdbListSettingsContent(
isTablet = false, isTablet = false,
settings = mdbListSettings, settings = mdbListSettings,
) )
SettingsPage.TraktAuthentication -> traktSettingsContent( SettingsPage.TraktAuthentication -> traktSettingsContent(
isTablet = false, isTablet = false,
uiState = traktAuthUiState, uiState = traktAuthUiState,
commentsEnabled = traktCommentsEnabled, commentsEnabled = traktCommentsEnabled,
onCommentsEnabledChange = TraktCommentsSettings::setEnabled, onCommentsEnabledChange = TraktCommentsSettings::setEnabled,
) )
}
} }
} }
} }
@ -468,6 +473,8 @@ private fun TabletSettingsScreen(
onPageChange(page) onPageChange(page)
} }
val saveableStateHolder = rememberSaveableStateHolder()
Row(modifier = Modifier.fillMaxSize()) { Row(modifier = Modifier.fillMaxSize()) {
Surface( Surface(
modifier = Modifier modifier = Modifier
@ -510,134 +517,138 @@ private fun TabletSettingsScreen(
} }
} }
LazyColumn( saveableStateHolder.SaveableStateProvider(page.name) {
modifier = Modifier.fillMaxSize(), val listState = rememberLazyListState()
contentPadding = PaddingValues( LazyColumn(
start = 40.dp, state = listState,
top = topOffset, modifier = Modifier.fillMaxSize(),
end = 40.dp, contentPadding = PaddingValues(
bottom = 40.dp, start = 40.dp,
), top = topOffset,
verticalArrangement = Arrangement.spacedBy(18.dp), end = 40.dp,
) { bottom = 40.dp,
item { ),
val previousPage = page.previousPage() verticalArrangement = Arrangement.spacedBy(18.dp),
TabletPageHeader( ) {
title = if (page == SettingsPage.Root) { item {
stringResource(activeCategory.labelRes) val previousPage = page.previousPage()
} else { TabletPageHeader(
stringResource(page.titleRes) title = if (page == SettingsPage.Root) {
}, stringResource(activeCategory.labelRes)
showBack = previousPage != null, } else {
onBack = { previousPage?.let(onPageChange) }, stringResource(page.titleRes)
) },
} showBack = previousPage != null,
when (page) { onBack = { previousPage?.let(onPageChange) },
SettingsPage.Root -> settingsRootContent( )
isTablet = true, }
onPlaybackClick = { openInlinePage(SettingsPage.Playback) }, when (page) {
onAppearanceClick = { openInlinePage(SettingsPage.Appearance) }, SettingsPage.Root -> settingsRootContent(
onNotificationsClick = { openInlinePage(SettingsPage.Notifications) }, isTablet = true,
onContentDiscoveryClick = { openInlinePage(SettingsPage.ContentDiscovery) }, onPlaybackClick = { openInlinePage(SettingsPage.Playback) },
onIntegrationsClick = { openInlinePage(SettingsPage.Integrations) }, onAppearanceClick = { openInlinePage(SettingsPage.Appearance) },
onTraktClick = { openInlinePage(SettingsPage.TraktAuthentication) }, onNotificationsClick = { openInlinePage(SettingsPage.Notifications) },
onSupportersContributorsClick = { openInlinePage(SettingsPage.SupportersContributors) }, onContentDiscoveryClick = { openInlinePage(SettingsPage.ContentDiscovery) },
onCheckForUpdatesClick = onCheckForUpdatesClick, onIntegrationsClick = { openInlinePage(SettingsPage.Integrations) },
onDownloadsClick = onDownloadsClick, onTraktClick = { openInlinePage(SettingsPage.TraktAuthentication) },
onAccountClick = { openInlinePage(SettingsPage.Account) }, onSupportersContributorsClick = { openInlinePage(SettingsPage.SupportersContributors) },
onSwitchProfileClick = onSwitchProfile, onCheckForUpdatesClick = onCheckForUpdatesClick,
showAccountSection = activeCategory == SettingsCategory.Account, onDownloadsClick = onDownloadsClick,
showGeneralSection = activeCategory == SettingsCategory.General, onAccountClick = { openInlinePage(SettingsPage.Account) },
showAboutSection = activeCategory == SettingsCategory.About, onSwitchProfileClick = onSwitchProfile,
) showAccountSection = activeCategory == SettingsCategory.Account,
SettingsPage.Account -> accountSettingsContent( showGeneralSection = activeCategory == SettingsCategory.General,
isTablet = true, showAboutSection = activeCategory == SettingsCategory.About,
) )
SettingsPage.SupportersContributors -> supportersContributorsContent( SettingsPage.Account -> accountSettingsContent(
isTablet = true, isTablet = true,
) )
SettingsPage.Playback -> playbackSettingsContent( SettingsPage.SupportersContributors -> supportersContributorsContent(
isTablet = true, isTablet = true,
showLoadingOverlay = showLoadingOverlay, )
holdToSpeedEnabled = holdToSpeedEnabled, SettingsPage.Playback -> playbackSettingsContent(
holdToSpeedValue = holdToSpeedValue, isTablet = true,
preferredAudioLanguage = preferredAudioLanguage, showLoadingOverlay = showLoadingOverlay,
secondaryPreferredAudioLanguage = secondaryPreferredAudioLanguage, holdToSpeedEnabled = holdToSpeedEnabled,
preferredSubtitleLanguage = preferredSubtitleLanguage, holdToSpeedValue = holdToSpeedValue,
secondaryPreferredSubtitleLanguage = secondaryPreferredSubtitleLanguage, preferredAudioLanguage = preferredAudioLanguage,
streamReuseLastLinkEnabled = streamReuseLastLinkEnabled, secondaryPreferredAudioLanguage = secondaryPreferredAudioLanguage,
streamReuseLastLinkCacheHours = streamReuseLastLinkCacheHours, preferredSubtitleLanguage = preferredSubtitleLanguage,
decoderPriority = decoderPriority, secondaryPreferredSubtitleLanguage = secondaryPreferredSubtitleLanguage,
mapDV7ToHevc = mapDV7ToHevc, streamReuseLastLinkEnabled = streamReuseLastLinkEnabled,
tunnelingEnabled = tunnelingEnabled, streamReuseLastLinkCacheHours = streamReuseLastLinkCacheHours,
useLibass = useLibass, decoderPriority = decoderPriority,
libassRenderType = libassRenderType, mapDV7ToHevc = mapDV7ToHevc,
) tunnelingEnabled = tunnelingEnabled,
SettingsPage.Appearance -> appearanceSettingsContent( useLibass = useLibass,
isTablet = true, libassRenderType = libassRenderType,
selectedTheme = selectedTheme, )
onThemeSelected = onThemeSelected, SettingsPage.Appearance -> appearanceSettingsContent(
amoledEnabled = amoledEnabled, isTablet = true,
onAmoledToggle = onAmoledToggle, selectedTheme = selectedTheme,
selectedAppLanguage = selectedAppLanguage, onThemeSelected = onThemeSelected,
onAppLanguageSelected = onAppLanguageSelected, amoledEnabled = amoledEnabled,
onContinueWatchingClick = { openInlinePage(SettingsPage.ContinueWatching) }, onAmoledToggle = onAmoledToggle,
onPosterCustomizationClick = { openInlinePage(SettingsPage.PosterCustomization) }, selectedAppLanguage = selectedAppLanguage,
) onAppLanguageSelected = onAppLanguageSelected,
SettingsPage.Notifications -> notificationsSettingsContent( onContinueWatchingClick = { openInlinePage(SettingsPage.ContinueWatching) },
isTablet = true, onPosterCustomizationClick = { openInlinePage(SettingsPage.PosterCustomization) },
uiState = episodeReleaseNotificationsUiState, )
) SettingsPage.Notifications -> notificationsSettingsContent(
SettingsPage.ContinueWatching -> continueWatchingSettingsContent( isTablet = true,
isTablet = true, uiState = episodeReleaseNotificationsUiState,
isVisible = continueWatchingPreferencesUiState.isVisible, )
style = continueWatchingPreferencesUiState.style, SettingsPage.ContinueWatching -> continueWatchingSettingsContent(
upNextFromFurthestEpisode = continueWatchingPreferencesUiState.upNextFromFurthestEpisode, isTablet = true,
showResumePromptOnLaunch = continueWatchingPreferencesUiState.showResumePromptOnLaunch, isVisible = continueWatchingPreferencesUiState.isVisible,
) style = continueWatchingPreferencesUiState.style,
SettingsPage.PosterCustomization -> posterCustomizationSettingsContent( upNextFromFurthestEpisode = continueWatchingPreferencesUiState.upNextFromFurthestEpisode,
isTablet = true, showResumePromptOnLaunch = continueWatchingPreferencesUiState.showResumePromptOnLaunch,
uiState = posterCardStyleUiState, )
) SettingsPage.PosterCustomization -> posterCustomizationSettingsContent(
SettingsPage.ContentDiscovery -> contentDiscoveryContent( isTablet = true,
isTablet = true, uiState = posterCardStyleUiState,
showPluginsEntry = AppFeaturePolicy.pluginsEnabled, )
onAddonsClick = { openInlinePage(SettingsPage.Addons) }, SettingsPage.ContentDiscovery -> contentDiscoveryContent(
onPluginsClick = { openInlinePage(SettingsPage.Plugins) }, isTablet = true,
onHomescreenClick = { openInlinePage(SettingsPage.Homescreen) }, showPluginsEntry = AppFeaturePolicy.pluginsEnabled,
onMetaScreenClick = { openInlinePage(SettingsPage.MetaScreen) }, onAddonsClick = { openInlinePage(SettingsPage.Addons) },
onCollectionsClick = onCollectionsClick, onPluginsClick = { openInlinePage(SettingsPage.Plugins) },
) onHomescreenClick = { openInlinePage(SettingsPage.Homescreen) },
SettingsPage.Addons -> addonsSettingsContent() onMetaScreenClick = { openInlinePage(SettingsPage.MetaScreen) },
SettingsPage.Plugins -> if (AppFeaturePolicy.pluginsEnabled) pluginsSettingsContent() else addonsSettingsContent() onCollectionsClick = onCollectionsClick,
SettingsPage.Homescreen -> homescreenSettingsContent( )
isTablet = true, SettingsPage.Addons -> addonsSettingsContent()
heroEnabled = homescreenHeroEnabled, SettingsPage.Plugins -> if (AppFeaturePolicy.pluginsEnabled) pluginsSettingsContent() else addonsSettingsContent()
items = homescreenItems, SettingsPage.Homescreen -> homescreenSettingsContent(
) isTablet = true,
SettingsPage.MetaScreen -> metaScreenSettingsContent( heroEnabled = homescreenHeroEnabled,
isTablet = true, items = homescreenItems,
uiState = metaScreenSettingsUiState, )
) SettingsPage.MetaScreen -> metaScreenSettingsContent(
SettingsPage.Integrations -> integrationsContent( isTablet = true,
isTablet = true, uiState = metaScreenSettingsUiState,
onTmdbClick = { onPageChange(SettingsPage.TmdbEnrichment) }, )
onMdbListClick = { onPageChange(SettingsPage.MdbListRatings) }, SettingsPage.Integrations -> integrationsContent(
) isTablet = true,
SettingsPage.TmdbEnrichment -> tmdbSettingsContent( onTmdbClick = { onPageChange(SettingsPage.TmdbEnrichment) },
isTablet = true, onMdbListClick = { onPageChange(SettingsPage.MdbListRatings) },
settings = tmdbSettings, )
) SettingsPage.TmdbEnrichment -> tmdbSettingsContent(
SettingsPage.MdbListRatings -> mdbListSettingsContent( isTablet = true,
isTablet = true, settings = tmdbSettings,
settings = mdbListSettings, )
) SettingsPage.MdbListRatings -> mdbListSettingsContent(
SettingsPage.TraktAuthentication -> traktSettingsContent( isTablet = true,
isTablet = true, settings = mdbListSettings,
uiState = traktAuthUiState, )
commentsEnabled = traktCommentsEnabled, SettingsPage.TraktAuthentication -> traktSettingsContent(
onCommentsEnabledChange = TraktCommentsSettings::setEnabled, isTablet = true,
) uiState = traktAuthUiState,
commentsEnabled = traktCommentsEnabled,
onCommentsEnabledChange = TraktCommentsSettings::setEnabled,
)
}
} }
} }
} }