mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-08-02 17:51:35 +00:00
feat: remember last used profile
This commit is contained in:
parent
939bca4848
commit
2446465494
9 changed files with 159 additions and 5 deletions
|
|
@ -404,6 +404,7 @@
|
|||
<string name="compose_settings_category_general">General</string>
|
||||
<string name="compose_settings_page_account">Account</string>
|
||||
<string name="compose_settings_page_addons">Addons</string>
|
||||
<string name="compose_settings_page_advanced">Advanced</string>
|
||||
<string name="compose_settings_page_appearance">Layout</string>
|
||||
<string name="compose_settings_page_content_discovery">Content & Discovery</string>
|
||||
<string name="compose_settings_page_continue_watching">Continue Watching</string>
|
||||
|
|
@ -425,6 +426,7 @@
|
|||
<string name="compose_settings_root_about_section">ABOUT</string>
|
||||
<string name="compose_settings_root_account_description">Account and sync status</string>
|
||||
<string name="compose_settings_root_account_section">ACCOUNT</string>
|
||||
<string name="compose_settings_root_advanced_description">Startup and profile behavior.</string>
|
||||
<string name="compose_settings_root_appearance_description">Home structure and poster styles</string>
|
||||
<string name="compose_settings_root_check_updates_description">Download latest release</string>
|
||||
<string name="compose_settings_root_check_updates_title">Check for updates</string>
|
||||
|
|
@ -441,6 +443,9 @@
|
|||
<string name="settings_search_empty">No settings found.</string>
|
||||
<string name="settings_search_placeholder">Search settings...</string>
|
||||
<string name="settings_search_results_section">RESULTS</string>
|
||||
<string name="settings_advanced_section_startup">STARTUP</string>
|
||||
<string name="settings_advanced_remember_last_profile">Remember Last Profile</string>
|
||||
<string name="settings_advanced_remember_last_profile_description">Start with the last selected profile when it does not have a PIN lock.</string>
|
||||
<string name="settings_licenses_attributions_section_app">APP LICENSE</string>
|
||||
<string name="settings_licenses_attributions_section_data">DATA & SERVICES</string>
|
||||
<string name="settings_licenses_attributions_section_playback">PLAYBACK LICENSE</string>
|
||||
|
|
|
|||
|
|
@ -413,6 +413,20 @@ fun App() {
|
|||
var isNewProfile by remember { mutableStateOf(false) }
|
||||
var autoSkipProfileSelection by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
fun rememberedStartupProfile(profiles: List<NuvioProfile>): NuvioProfile? {
|
||||
val currentProfileState = ProfileRepository.state.value
|
||||
if (
|
||||
!currentProfileState.rememberLastProfileEnabled ||
|
||||
!currentProfileState.hasEverSelectedProfile
|
||||
) {
|
||||
return null
|
||||
}
|
||||
|
||||
return profiles
|
||||
.find { it.profileIndex == ProfileRepository.activeProfileId }
|
||||
?.takeUnless { it.pinEnabled }
|
||||
}
|
||||
|
||||
fun enterProfileGate(profiles: List<NuvioProfile>, syncOnEnter: Boolean) {
|
||||
if (profiles.isEmpty()) {
|
||||
autoSkipProfileSelection = true
|
||||
|
|
@ -420,9 +434,23 @@ fun App() {
|
|||
return
|
||||
}
|
||||
|
||||
rememberedStartupProfile(profiles)?.let { profile ->
|
||||
ProfileRepository.selectProfile(profile.profileIndex)
|
||||
if (syncOnEnter) {
|
||||
SyncManager.pullAllForProfile(profile.profileIndex)
|
||||
}
|
||||
gateScreen = AppGateScreen.Main.name
|
||||
autoSkipProfileSelection = false
|
||||
return
|
||||
}
|
||||
|
||||
autoSkipProfileSelection = true
|
||||
if (profiles.size == 1) {
|
||||
val onlyProfile = profiles.first()
|
||||
if (onlyProfile.pinEnabled) {
|
||||
gateScreen = AppGateScreen.ProfileSelection.name
|
||||
return
|
||||
}
|
||||
ProfileRepository.selectProfile(onlyProfile.profileIndex)
|
||||
if (syncOnEnter) {
|
||||
SyncManager.pullAllForProfile(onlyProfile.profileIndex)
|
||||
|
|
@ -473,13 +501,32 @@ fun App() {
|
|||
ProfileRepository.pullProfiles()
|
||||
}
|
||||
|
||||
LaunchedEffect(gateScreen, autoSkipProfileSelection, profileState.profiles) {
|
||||
LaunchedEffect(
|
||||
gateScreen,
|
||||
autoSkipProfileSelection,
|
||||
profileState.profiles,
|
||||
profileState.hasEverSelectedProfile,
|
||||
profileState.rememberLastProfileEnabled,
|
||||
profileState.activeProfile?.profileIndex,
|
||||
profileState.activeProfile?.pinEnabled,
|
||||
) {
|
||||
if (
|
||||
autoSkipProfileSelection &&
|
||||
gateScreen == AppGateScreen.ProfileSelection.name &&
|
||||
profileState.profiles.size == 1
|
||||
gateScreen == AppGateScreen.ProfileSelection.name
|
||||
) {
|
||||
rememberedStartupProfile(profileState.profiles)?.let { profile ->
|
||||
ProfileRepository.selectProfile(profile.profileIndex)
|
||||
SyncManager.pullAllForProfile(profile.profileIndex)
|
||||
gateScreen = AppGateScreen.Main.name
|
||||
autoSkipProfileSelection = false
|
||||
return@LaunchedEffect
|
||||
}
|
||||
|
||||
if (profileState.profiles.size != 1) return@LaunchedEffect
|
||||
|
||||
val onlyProfile = profileState.profiles.first()
|
||||
if (onlyProfile.pinEnabled) return@LaunchedEffect
|
||||
|
||||
ProfileRepository.selectProfile(onlyProfile.profileIndex)
|
||||
SyncManager.pullAllForProfile(onlyProfile.profileIndex)
|
||||
gateScreen = AppGateScreen.Main.name
|
||||
|
|
|
|||
|
|
@ -43,6 +43,8 @@ data class ProfileState(
|
|||
val profiles: List<NuvioProfile> = emptyList(),
|
||||
val activeProfile: NuvioProfile? = null,
|
||||
val isLoaded: Boolean = false,
|
||||
val hasEverSelectedProfile: Boolean = false,
|
||||
val rememberLastProfileEnabled: Boolean = false,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ import org.jetbrains.compose.resources.getString
|
|||
private data class StoredProfilePayload(
|
||||
val userId: String,
|
||||
val activeProfileIndex: Int = 1,
|
||||
val hasEverSelectedProfile: Boolean = false,
|
||||
val rememberLastProfileEnabled: Boolean = false,
|
||||
val profiles: List<NuvioProfile> = emptyList(),
|
||||
)
|
||||
|
||||
|
|
@ -70,6 +72,13 @@ object ProfileRepository {
|
|||
|
||||
val activeProfileId: Int get() = activeProfileIndex
|
||||
|
||||
fun setRememberLastProfileEnabled(enabled: Boolean) {
|
||||
if (_state.value.rememberLastProfileEnabled == enabled) return
|
||||
|
||||
_state.value = _state.value.copy(rememberLastProfileEnabled = enabled)
|
||||
persist()
|
||||
}
|
||||
|
||||
fun loadCachedProfiles(): Boolean {
|
||||
val stored = decodeStoredPayload() ?: return false
|
||||
loadedCacheForUserId = stored.userId
|
||||
|
|
@ -134,8 +143,10 @@ object ProfileRepository {
|
|||
|
||||
fun selectProfile(profileIndex: Int) {
|
||||
activeProfileIndex = profileIndex
|
||||
val selectedProfile = _state.value.profiles.find { it.profileIndex == profileIndex }
|
||||
_state.value = _state.value.copy(
|
||||
activeProfile = _state.value.profiles.find { it.profileIndex == profileIndex },
|
||||
activeProfile = selectedProfile,
|
||||
hasEverSelectedProfile = selectedProfile != null || _state.value.hasEverSelectedProfile,
|
||||
)
|
||||
persist()
|
||||
WatchedRepository.onProfileChanged(profileIndex)
|
||||
|
|
@ -402,6 +413,8 @@ object ProfileRepository {
|
|||
profiles = profiles,
|
||||
activeProfile = profiles.find { it.profileIndex == activeProfileIndex } ?: profiles.firstOrNull(),
|
||||
isLoaded = profiles.isNotEmpty(),
|
||||
hasEverSelectedProfile = stored.hasEverSelectedProfile,
|
||||
rememberLastProfileEnabled = stored.rememberLastProfileEnabled,
|
||||
)
|
||||
_state.value.activeProfile?.let { activeProfileIndex = it.profileIndex }
|
||||
syncPinCache(profiles)
|
||||
|
|
@ -490,12 +503,15 @@ object ProfileRepository {
|
|||
|
||||
private fun persist() {
|
||||
val authState = AuthRepository.state.value as? AuthState.Authenticated ?: return
|
||||
val state = _state.value
|
||||
ProfileStorage.savePayload(
|
||||
json.encodeToString(
|
||||
StoredProfilePayload(
|
||||
userId = authState.userId,
|
||||
activeProfileIndex = activeProfileIndex,
|
||||
profiles = _state.value.profiles,
|
||||
hasEverSelectedProfile = state.hasEverSelectedProfile,
|
||||
rememberLastProfileEnabled = state.rememberLastProfileEnabled,
|
||||
profiles = state.profiles,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
package com.nuvio.app.features.settings
|
||||
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import com.nuvio.app.features.profiles.ProfileRepository
|
||||
import nuvio.composeapp.generated.resources.Res
|
||||
import nuvio.composeapp.generated.resources.settings_advanced_remember_last_profile
|
||||
import nuvio.composeapp.generated.resources.settings_advanced_remember_last_profile_description
|
||||
import nuvio.composeapp.generated.resources.settings_advanced_section_startup
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
||||
internal fun LazyListScope.advancedSettingsContent(
|
||||
isTablet: Boolean,
|
||||
rememberLastProfileEnabled: Boolean,
|
||||
) {
|
||||
item {
|
||||
SettingsSection(
|
||||
title = stringResource(Res.string.settings_advanced_section_startup),
|
||||
isTablet = isTablet,
|
||||
) {
|
||||
SettingsGroup(isTablet = isTablet) {
|
||||
SettingsSwitchRow(
|
||||
title = stringResource(Res.string.settings_advanced_remember_last_profile),
|
||||
description = stringResource(Res.string.settings_advanced_remember_last_profile_description),
|
||||
checked = rememberLastProfileEnabled,
|
||||
isTablet = isTablet,
|
||||
onCheckedChange = ProfileRepository::setRememberLastProfileEnabled,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ import nuvio.composeapp.generated.resources.compose_settings_category_about
|
|||
import nuvio.composeapp.generated.resources.compose_settings_category_general
|
||||
import nuvio.composeapp.generated.resources.compose_settings_page_account
|
||||
import nuvio.composeapp.generated.resources.compose_settings_page_addons
|
||||
import nuvio.composeapp.generated.resources.compose_settings_page_advanced
|
||||
import nuvio.composeapp.generated.resources.compose_settings_page_appearance
|
||||
import nuvio.composeapp.generated.resources.compose_settings_page_content_discovery
|
||||
import nuvio.composeapp.generated.resources.compose_settings_page_debrid
|
||||
|
|
@ -81,6 +82,11 @@ internal enum class SettingsPage(
|
|||
category = SettingsCategory.General,
|
||||
parentPage = Root,
|
||||
),
|
||||
Advanced(
|
||||
titleRes = Res.string.compose_settings_page_advanced,
|
||||
category = SettingsCategory.General,
|
||||
parentPage = Root,
|
||||
),
|
||||
Notifications(
|
||||
titleRes = Res.string.compose_settings_page_notifications,
|
||||
category = SettingsCategory.General,
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import androidx.compose.material.icons.rounded.Palette
|
|||
import androidx.compose.material.icons.rounded.People
|
||||
import androidx.compose.material.icons.rounded.PlayArrow
|
||||
import androidx.compose.material.icons.rounded.Style
|
||||
import androidx.compose.material.icons.rounded.Tune
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -25,6 +26,7 @@ import nuvio.composeapp.generated.resources.Res
|
|||
import nuvio.composeapp.generated.resources.compose_about_made_with
|
||||
import nuvio.composeapp.generated.resources.compose_about_version_format
|
||||
import nuvio.composeapp.generated.resources.compose_settings_page_account
|
||||
import nuvio.composeapp.generated.resources.compose_settings_page_advanced
|
||||
import nuvio.composeapp.generated.resources.compose_settings_page_appearance
|
||||
import nuvio.composeapp.generated.resources.compose_settings_page_integrations
|
||||
import nuvio.composeapp.generated.resources.compose_settings_page_licenses_attributions
|
||||
|
|
@ -48,6 +50,7 @@ import nuvio.composeapp.generated.resources.compose_settings_root_switch_profile
|
|||
import nuvio.composeapp.generated.resources.compose_settings_root_trakt_description
|
||||
import nuvio.composeapp.generated.resources.compose_settings_root_about_section
|
||||
import nuvio.composeapp.generated.resources.compose_settings_root_account_section
|
||||
import nuvio.composeapp.generated.resources.compose_settings_root_advanced_description
|
||||
import nuvio.composeapp.generated.resources.compose_settings_page_content_discovery
|
||||
import nuvio.composeapp.generated.resources.compose_settings_page_trakt
|
||||
import nuvio.composeapp.generated.resources.settings_playback_subtitle
|
||||
|
|
@ -60,6 +63,7 @@ internal fun LazyListScope.settingsRootContent(
|
|||
onPlaybackClick: () -> Unit,
|
||||
onStreamsClick: () -> Unit,
|
||||
onAppearanceClick: () -> Unit,
|
||||
onAdvancedClick: () -> Unit,
|
||||
onNotificationsClick: () -> Unit,
|
||||
onContentDiscoveryClick: () -> Unit,
|
||||
onIntegrationsClick: () -> Unit,
|
||||
|
|
@ -125,6 +129,14 @@ internal fun LazyListScope.settingsRootContent(
|
|||
onClick = onAppearanceClick,
|
||||
)
|
||||
SettingsGroupDivider(isTablet = isTablet)
|
||||
SettingsNavigationRow(
|
||||
title = stringResource(Res.string.compose_settings_page_advanced),
|
||||
description = stringResource(Res.string.compose_settings_root_advanced_description),
|
||||
icon = Icons.Rounded.Tune,
|
||||
isTablet = isTablet,
|
||||
onClick = onAdvancedClick,
|
||||
)
|
||||
SettingsGroupDivider(isTablet = isTablet)
|
||||
SettingsNavigationRow(
|
||||
title = stringResource(Res.string.compose_settings_page_content_discovery),
|
||||
description = stringResource(Res.string.compose_settings_root_content_discovery_description),
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ import com.nuvio.app.features.mdblist.MdbListSettingsRepository
|
|||
import com.nuvio.app.features.notifications.EpisodeReleaseNotificationsRepository
|
||||
import com.nuvio.app.features.notifications.EpisodeReleaseNotificationsUiState
|
||||
import com.nuvio.app.features.player.PlayerSettingsRepository
|
||||
import com.nuvio.app.features.profiles.ProfileRepository
|
||||
import com.nuvio.app.features.trakt.TraktAuthUiState
|
||||
import com.nuvio.app.features.trakt.TraktAuthRepository
|
||||
import com.nuvio.app.features.trakt.TraktCommentsSettings
|
||||
|
|
@ -194,6 +195,9 @@ fun SettingsScreen(
|
|||
EpisodeReleaseNotificationsRepository.ensureLoaded()
|
||||
EpisodeReleaseNotificationsRepository.uiState
|
||||
}.collectAsStateWithLifecycle()
|
||||
val profileSettingsState by remember {
|
||||
ProfileRepository.state
|
||||
}.collectAsStateWithLifecycle()
|
||||
|
||||
LaunchedEffect(homescreenCatalogRefreshKey) {
|
||||
if (homescreenCatalogRefreshKey.isEmpty()) return@LaunchedEffect
|
||||
|
|
@ -258,6 +262,7 @@ fun SettingsScreen(
|
|||
tunnelingEnabled = playerSettingsUiState.tunnelingEnabled,
|
||||
useLibass = playerSettingsUiState.useLibass,
|
||||
libassRenderType = playerSettingsUiState.libassRenderType,
|
||||
rememberLastProfileEnabled = profileSettingsState.rememberLastProfileEnabled,
|
||||
selectedTheme = selectedTheme,
|
||||
onThemeSelected = ThemeSettingsRepository::setTheme,
|
||||
amoledEnabled = amoledEnabled,
|
||||
|
|
@ -307,6 +312,7 @@ fun SettingsScreen(
|
|||
tunnelingEnabled = playerSettingsUiState.tunnelingEnabled,
|
||||
useLibass = playerSettingsUiState.useLibass,
|
||||
libassRenderType = playerSettingsUiState.libassRenderType,
|
||||
rememberLastProfileEnabled = profileSettingsState.rememberLastProfileEnabled,
|
||||
selectedTheme = selectedTheme,
|
||||
onThemeSelected = ThemeSettingsRepository::setTheme,
|
||||
amoledEnabled = amoledEnabled,
|
||||
|
|
@ -366,6 +372,7 @@ private fun MobileSettingsScreen(
|
|||
tunnelingEnabled: Boolean,
|
||||
useLibass: Boolean,
|
||||
libassRenderType: String,
|
||||
rememberLastProfileEnabled: Boolean,
|
||||
selectedTheme: AppTheme,
|
||||
onThemeSelected: (AppTheme) -> Unit,
|
||||
amoledEnabled: Boolean,
|
||||
|
|
@ -496,6 +503,7 @@ private fun MobileSettingsScreen(
|
|||
onPlaybackClick = { onPageChange(SettingsPage.Playback) },
|
||||
onStreamsClick = { onPageChange(SettingsPage.Streams) },
|
||||
onAppearanceClick = { onPageChange(SettingsPage.Appearance) },
|
||||
onAdvancedClick = { onPageChange(SettingsPage.Advanced) },
|
||||
onNotificationsClick = { onPageChange(SettingsPage.Notifications) },
|
||||
onContentDiscoveryClick = { onPageChange(SettingsPage.ContentDiscovery) },
|
||||
onIntegrationsClick = { onPageChange(SettingsPage.Integrations) },
|
||||
|
|
@ -552,6 +560,10 @@ private fun MobileSettingsScreen(
|
|||
onContinueWatchingClick = onContinueWatchingClick,
|
||||
onPosterCustomizationClick = { onPageChange(SettingsPage.PosterCustomization) },
|
||||
)
|
||||
SettingsPage.Advanced -> advancedSettingsContent(
|
||||
isTablet = false,
|
||||
rememberLastProfileEnabled = rememberLastProfileEnabled,
|
||||
)
|
||||
SettingsPage.Notifications -> notificationsSettingsContent(
|
||||
isTablet = false,
|
||||
uiState = episodeReleaseNotificationsUiState,
|
||||
|
|
@ -684,6 +696,7 @@ private fun TabletSettingsScreen(
|
|||
tunnelingEnabled: Boolean,
|
||||
useLibass: Boolean,
|
||||
libassRenderType: String,
|
||||
rememberLastProfileEnabled: Boolean,
|
||||
selectedTheme: AppTheme,
|
||||
onThemeSelected: (AppTheme) -> Unit,
|
||||
amoledEnabled: Boolean,
|
||||
|
|
@ -869,6 +882,7 @@ private fun TabletSettingsScreen(
|
|||
onPlaybackClick = { openInlinePage(SettingsPage.Playback) },
|
||||
onStreamsClick = { openInlinePage(SettingsPage.Streams) },
|
||||
onAppearanceClick = { openInlinePage(SettingsPage.Appearance) },
|
||||
onAdvancedClick = { openInlinePage(SettingsPage.Advanced) },
|
||||
onNotificationsClick = { openInlinePage(SettingsPage.Notifications) },
|
||||
onContentDiscoveryClick = { openInlinePage(SettingsPage.ContentDiscovery) },
|
||||
onIntegrationsClick = { openInlinePage(SettingsPage.Integrations) },
|
||||
|
|
@ -928,6 +942,10 @@ private fun TabletSettingsScreen(
|
|||
onContinueWatchingClick = { openInlinePage(SettingsPage.ContinueWatching) },
|
||||
onPosterCustomizationClick = { openInlinePage(SettingsPage.PosterCustomization) },
|
||||
)
|
||||
SettingsPage.Advanced -> advancedSettingsContent(
|
||||
isTablet = true,
|
||||
rememberLastProfileEnabled = rememberLastProfileEnabled,
|
||||
)
|
||||
SettingsPage.Notifications -> notificationsSettingsContent(
|
||||
isTablet = true,
|
||||
uiState = episodeReleaseNotificationsUiState,
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ internal fun settingsSearchEntries(
|
|||
val accountPage = stringResource(Res.string.compose_settings_page_account)
|
||||
val traktPage = stringResource(Res.string.compose_settings_page_trakt)
|
||||
val layoutPage = stringResource(Res.string.compose_settings_page_appearance)
|
||||
val advancedPage = stringResource(Res.string.compose_settings_page_advanced)
|
||||
val contentDiscoveryPage = stringResource(Res.string.compose_settings_page_content_discovery)
|
||||
val downloadsPage = stringResource(Res.string.compose_settings_root_downloads_title)
|
||||
val playbackPage = stringResource(Res.string.compose_settings_page_playback)
|
||||
|
|
@ -207,6 +208,13 @@ internal fun settingsSearchEntries(
|
|||
description = stringResource(Res.string.compose_settings_root_appearance_description),
|
||||
icon = Icons.Rounded.Palette,
|
||||
)
|
||||
addPage(
|
||||
page = SettingsPage.Advanced,
|
||||
key = "advanced",
|
||||
title = advancedPage,
|
||||
description = stringResource(Res.string.compose_settings_root_advanced_description),
|
||||
icon = Icons.Rounded.Tune,
|
||||
)
|
||||
addPage(
|
||||
page = SettingsPage.ContentDiscovery,
|
||||
key = "content-discovery",
|
||||
|
|
@ -368,6 +376,15 @@ internal fun settingsSearchEntries(
|
|||
section = stringResource(Res.string.settings_appearance_section_display),
|
||||
icon = Icons.Rounded.Language,
|
||||
)
|
||||
addRow(
|
||||
page = SettingsPage.Advanced,
|
||||
key = "remember-last-profile",
|
||||
title = stringResource(Res.string.settings_advanced_remember_last_profile),
|
||||
description = stringResource(Res.string.settings_advanced_remember_last_profile_description),
|
||||
pageLabel = advancedPage,
|
||||
section = stringResource(Res.string.settings_advanced_section_startup),
|
||||
icon = Icons.Rounded.Tune,
|
||||
)
|
||||
addPage(
|
||||
page = SettingsPage.ContinueWatching,
|
||||
key = "continue-watching",
|
||||
|
|
|
|||
Loading…
Reference in a new issue