feat: Add settings screens for Homescreen, Continue Watching, Addons, and Account; update navigation and settings structure

This commit is contained in:
tapframe 2026-03-29 12:03:21 +05:30
parent 70a9dd7213
commit f3c854751e
4 changed files with 199 additions and 80 deletions

View file

@ -78,6 +78,10 @@ import com.nuvio.app.features.profiles.ProfileRepository
import com.nuvio.app.features.profiles.ProfileSelectionScreen
import com.nuvio.app.features.search.SearchScreen
import com.nuvio.app.features.settings.SettingsScreen
import com.nuvio.app.features.settings.HomescreenSettingsScreen
import com.nuvio.app.features.settings.ContinueWatchingSettingsScreen
import com.nuvio.app.features.settings.AddonsSettingsScreen
import com.nuvio.app.features.settings.AccountSettingsScreen
import com.nuvio.app.features.streams.StreamsRepository
import com.nuvio.app.features.streams.StreamsScreen
import com.nuvio.app.features.watched.WatchedRepository
@ -92,6 +96,18 @@ object TabsRoute
@Serializable
data class DetailRoute(val type: String, val id: String)
@Serializable
object HomescreenSettingsRoute
@Serializable
object ContinueWatchingSettingsRoute
@Serializable
object AddonsSettingsRoute
@Serializable
object AccountSettingsRoute
@Serializable
data class StreamRoute(
val type: String,
@ -393,6 +409,10 @@ private fun MainAppContent(
onContinueWatchingClick = onContinueWatchingClick,
onContinueWatchingLongPress = onContinueWatchingLongPress,
onSwitchProfile = onSwitchProfile,
onHomescreenSettingsClick = { navController.navigate(HomescreenSettingsRoute) },
onContinueWatchingSettingsClick = { navController.navigate(ContinueWatchingSettingsRoute) },
onAddonsSettingsClick = { navController.navigate(AddonsSettingsRoute) },
onAccountSettingsClick = { navController.navigate(AccountSettingsRoute) },
)
}
}
@ -518,6 +538,26 @@ private fun MainAppContent(
modifier = Modifier.fillMaxSize(),
)
}
composable<HomescreenSettingsRoute> {
HomescreenSettingsScreen(
onBack = { navController.popBackStack() },
)
}
composable<ContinueWatchingSettingsRoute> {
ContinueWatchingSettingsScreen(
onBack = { navController.popBackStack() },
)
}
composable<AddonsSettingsRoute> {
AddonsSettingsScreen(
onBack = { navController.popBackStack() },
)
}
composable<AccountSettingsRoute> {
AccountSettingsScreen(
onBack = { navController.popBackStack() },
)
}
}
NuvioPosterActionSheet(
@ -554,6 +594,10 @@ private fun AppTabHost(
onContinueWatchingClick: ((ContinueWatchingItem) -> Unit)? = null,
onContinueWatchingLongPress: ((ContinueWatchingItem) -> Unit)? = null,
onSwitchProfile: (() -> Unit)? = null,
onHomescreenSettingsClick: () -> Unit = {},
onContinueWatchingSettingsClick: () -> Unit = {},
onAddonsSettingsClick: () -> Unit = {},
onAccountSettingsClick: () -> Unit = {},
) {
Box(modifier = modifier.fillMaxSize()) {
keepAliveTab(
@ -591,6 +635,10 @@ private fun AppTabHost(
SettingsScreen(
modifier = Modifier.fillMaxSize(),
onSwitchProfile = onSwitchProfile,
onHomescreenClick = onHomescreenSettingsClick,
onContinueWatchingClick = onContinueWatchingSettingsClick,
onAddonsClick = onAddonsSettingsClick,
onAccountClick = onAccountSettingsClick,
)
}
}

View file

@ -0,0 +1,123 @@
package com.nuvio.app.features.settings
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.nuvio.app.core.ui.NuvioScreen
import com.nuvio.app.core.ui.NuvioScreenHeader
import com.nuvio.app.features.addons.AddonRepository
import com.nuvio.app.features.home.HomeCatalogSettingsRepository
import com.nuvio.app.features.watchprogress.ContinueWatchingPreferencesRepository
@Composable
fun HomescreenSettingsScreen(
onBack: () -> Unit,
) {
val addonsUiState by AddonRepository.uiState.collectAsStateWithLifecycle()
val homescreenSettingsUiState by HomeCatalogSettingsRepository.uiState.collectAsStateWithLifecycle()
LaunchedEffect(Unit) {
AddonRepository.initialize()
}
LaunchedEffect(addonsUiState.addons) {
HomeCatalogSettingsRepository.syncCatalogs(addonsUiState.addons)
}
NuvioScreen(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colorScheme.background),
) {
stickyHeader {
NuvioScreenHeader(
title = "Homescreen",
onBack = onBack,
)
}
homescreenSettingsContent(
isTablet = false,
heroEnabled = homescreenSettingsUiState.heroEnabled,
items = homescreenSettingsUiState.items,
)
}
}
@Composable
fun ContinueWatchingSettingsScreen(
onBack: () -> Unit,
) {
val continueWatchingPreferencesUiState by remember {
ContinueWatchingPreferencesRepository.ensureLoaded()
ContinueWatchingPreferencesRepository.uiState
}.collectAsStateWithLifecycle()
NuvioScreen(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colorScheme.background),
) {
stickyHeader {
NuvioScreenHeader(
title = "Continue Watching",
onBack = onBack,
)
}
continueWatchingSettingsContent(
isTablet = false,
isVisible = continueWatchingPreferencesUiState.isVisible,
style = continueWatchingPreferencesUiState.style,
)
}
}
@Composable
fun AddonsSettingsScreen(
onBack: () -> Unit,
) {
LaunchedEffect(Unit) {
AddonRepository.initialize()
}
NuvioScreen(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colorScheme.background),
) {
stickyHeader {
NuvioScreenHeader(
title = "Addons",
onBack = onBack,
)
}
addonsSettingsContent()
}
}
@Composable
fun AccountSettingsScreen(
onBack: () -> Unit,
) {
NuvioScreen(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colorScheme.background),
) {
stickyHeader {
NuvioScreenHeader(
title = "Account",
onBack = onBack,
)
}
accountSettingsContent(
isTablet = false,
)
}
}

View file

@ -19,11 +19,7 @@ internal enum class SettingsPage(
Root("Settings"),
Playback("Playback"),
Appearance("Appearance"),
ContinueWatching("Continue Watching"),
ContentDiscovery("Content & Discovery"),
Addons("Addons"),
Homescreen("Homescreen"),
Account("Account"),
}
internal fun SettingsPage.previousPage(): SettingsPage? =
@ -31,9 +27,5 @@ internal fun SettingsPage.previousPage(): SettingsPage? =
SettingsPage.Root -> null
SettingsPage.Playback -> SettingsPage.Root
SettingsPage.Appearance -> SettingsPage.Root
SettingsPage.ContinueWatching -> SettingsPage.Appearance
SettingsPage.ContentDiscovery -> SettingsPage.Root
SettingsPage.Addons -> SettingsPage.ContentDiscovery
SettingsPage.Homescreen -> SettingsPage.ContentDiscovery
SettingsPage.Account -> SettingsPage.Root
}

View file

@ -22,7 +22,6 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@ -36,41 +35,26 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.nuvio.app.core.ui.PlatformBackHandler
import com.nuvio.app.core.ui.NuvioScreen
import com.nuvio.app.core.ui.NuvioScreenHeader
import com.nuvio.app.features.addons.AddonRepository
import com.nuvio.app.features.home.HomeCatalogSettingsItem
import com.nuvio.app.features.home.HomeCatalogSettingsRepository
import com.nuvio.app.features.player.PlayerSettingsRepository
import com.nuvio.app.features.watchprogress.ContinueWatchingPreferencesRepository
import com.nuvio.app.features.watchprogress.ContinueWatchingSectionStyle
@Composable
fun SettingsScreen(
modifier: Modifier = Modifier,
onSwitchProfile: (() -> Unit)? = null,
onHomescreenClick: () -> Unit = {},
onContinueWatchingClick: () -> Unit = {},
onAddonsClick: () -> Unit = {},
onAccountClick: () -> Unit = {},
) {
BoxWithConstraints(
modifier = modifier
.fillMaxSize()
.background(MaterialTheme.colorScheme.background),
) {
LaunchedEffect(Unit) {
AddonRepository.initialize()
}
val addonsUiState by AddonRepository.uiState.collectAsStateWithLifecycle()
val homescreenSettingsUiState by HomeCatalogSettingsRepository.uiState.collectAsStateWithLifecycle()
val playerSettingsUiState by remember {
PlayerSettingsRepository.ensureLoaded()
PlayerSettingsRepository.uiState
}.collectAsStateWithLifecycle()
val continueWatchingPreferencesUiState by remember {
ContinueWatchingPreferencesRepository.ensureLoaded()
ContinueWatchingPreferencesRepository.uiState
}.collectAsStateWithLifecycle()
LaunchedEffect(addonsUiState.addons) {
HomeCatalogSettingsRepository.syncCatalogs(addonsUiState.addons)
}
var currentPage by rememberSaveable { mutableStateOf(SettingsPage.Root.name) }
val page = remember(currentPage) { SettingsPage.valueOf(currentPage) }
@ -85,23 +69,23 @@ fun SettingsScreen(
TabletSettingsScreen(
page = page,
onPageChange = { currentPage = it.name },
heroEnabled = homescreenSettingsUiState.heroEnabled,
homescreenSettings = homescreenSettingsUiState.items,
showLoadingOverlay = playerSettingsUiState.showLoadingOverlay,
continueWatchingVisible = continueWatchingPreferencesUiState.isVisible,
continueWatchingStyle = continueWatchingPreferencesUiState.style,
onSwitchProfile = onSwitchProfile,
onHomescreenClick = onHomescreenClick,
onContinueWatchingClick = onContinueWatchingClick,
onAddonsClick = onAddonsClick,
onAccountClick = onAccountClick,
)
} else {
MobileSettingsScreen(
page = page,
onPageChange = { currentPage = it.name },
heroEnabled = homescreenSettingsUiState.heroEnabled,
homescreenSettings = homescreenSettingsUiState.items,
showLoadingOverlay = playerSettingsUiState.showLoadingOverlay,
continueWatchingVisible = continueWatchingPreferencesUiState.isVisible,
continueWatchingStyle = continueWatchingPreferencesUiState.style,
onSwitchProfile = onSwitchProfile,
onHomescreenClick = onHomescreenClick,
onContinueWatchingClick = onContinueWatchingClick,
onAddonsClick = onAddonsClick,
onAccountClick = onAccountClick,
)
}
}
@ -111,12 +95,12 @@ fun SettingsScreen(
private fun MobileSettingsScreen(
page: SettingsPage,
onPageChange: (SettingsPage) -> Unit,
heroEnabled: Boolean,
homescreenSettings: List<HomeCatalogSettingsItem>,
showLoadingOverlay: Boolean,
continueWatchingVisible: Boolean,
continueWatchingStyle: ContinueWatchingSectionStyle,
onSwitchProfile: (() -> Unit)? = null,
onHomescreenClick: () -> Unit = {},
onContinueWatchingClick: () -> Unit = {},
onAddonsClick: () -> Unit = {},
onAccountClick: () -> Unit = {},
) {
NuvioScreen {
stickyHeader {
@ -133,7 +117,7 @@ private fun MobileSettingsScreen(
onPlaybackClick = { onPageChange(SettingsPage.Playback) },
onAppearanceClick = { onPageChange(SettingsPage.Appearance) },
onContentDiscoveryClick = { onPageChange(SettingsPage.ContentDiscovery) },
onAccountClick = { onPageChange(SettingsPage.Account) },
onAccountClick = onAccountClick,
onSwitchProfileClick = onSwitchProfile,
)
SettingsPage.Playback -> playbackSettingsContent(
@ -142,26 +126,12 @@ private fun MobileSettingsScreen(
)
SettingsPage.Appearance -> appearanceSettingsContent(
isTablet = false,
onContinueWatchingClick = { onPageChange(SettingsPage.ContinueWatching) },
)
SettingsPage.ContinueWatching -> continueWatchingSettingsContent(
isTablet = false,
isVisible = continueWatchingVisible,
style = continueWatchingStyle,
onContinueWatchingClick = onContinueWatchingClick,
)
SettingsPage.ContentDiscovery -> contentDiscoveryContent(
isTablet = false,
onAddonsClick = { onPageChange(SettingsPage.Addons) },
onHomescreenClick = { onPageChange(SettingsPage.Homescreen) },
)
SettingsPage.Addons -> addonsSettingsContent()
SettingsPage.Homescreen -> homescreenSettingsContent(
isTablet = false,
heroEnabled = heroEnabled,
items = homescreenSettings,
)
SettingsPage.Account -> accountSettingsContent(
isTablet = false,
onAddonsClick = onAddonsClick,
onHomescreenClick = onHomescreenClick,
)
}
}
@ -171,12 +141,12 @@ private fun MobileSettingsScreen(
private fun TabletSettingsScreen(
page: SettingsPage,
onPageChange: (SettingsPage) -> Unit,
heroEnabled: Boolean,
homescreenSettings: List<HomeCatalogSettingsItem>,
showLoadingOverlay: Boolean,
continueWatchingVisible: Boolean,
continueWatchingStyle: ContinueWatchingSectionStyle,
onSwitchProfile: (() -> Unit)? = null,
onHomescreenClick: () -> Unit = {},
onContinueWatchingClick: () -> Unit = {},
onAddonsClick: () -> Unit = {},
onAccountClick: () -> Unit = {},
) {
var selectedCategory by rememberSaveable { mutableStateOf(SettingsCategory.General.name) }
val activeCategory = SettingsCategory.valueOf(selectedCategory)
@ -242,7 +212,7 @@ private fun TabletSettingsScreen(
onPlaybackClick = { onPageChange(SettingsPage.Playback) },
onAppearanceClick = { onPageChange(SettingsPage.Appearance) },
onContentDiscoveryClick = { onPageChange(SettingsPage.ContentDiscovery) },
onAccountClick = { onPageChange(SettingsPage.Account) },
onAccountClick = onAccountClick,
onSwitchProfileClick = onSwitchProfile,
)
SettingsPage.Playback -> playbackSettingsContent(
@ -251,26 +221,12 @@ private fun TabletSettingsScreen(
)
SettingsPage.Appearance -> appearanceSettingsContent(
isTablet = true,
onContinueWatchingClick = { onPageChange(SettingsPage.ContinueWatching) },
)
SettingsPage.ContinueWatching -> continueWatchingSettingsContent(
isTablet = true,
isVisible = continueWatchingVisible,
style = continueWatchingStyle,
onContinueWatchingClick = onContinueWatchingClick,
)
SettingsPage.ContentDiscovery -> contentDiscoveryContent(
isTablet = true,
onAddonsClick = { onPageChange(SettingsPage.Addons) },
onHomescreenClick = { onPageChange(SettingsPage.Homescreen) },
)
SettingsPage.Addons -> addonsSettingsContent()
SettingsPage.Homescreen -> homescreenSettingsContent(
isTablet = true,
heroEnabled = heroEnabled,
items = homescreenSettings,
)
SettingsPage.Account -> accountSettingsContent(
isTablet = true,
onAddonsClick = onAddonsClick,
onHomescreenClick = onHomescreenClick,
)
}
}