From df8b05f1c20dfcfad9601c9642c6a54491d1dcf5 Mon Sep 17 00:00:00 2001
From: tapframe <85391825+tapframe@users.noreply.github.com>
Date: Wed, 24 Jun 2026 13:22:53 +0530
Subject: [PATCH] Adjust App Store settings policy and copy
---
.../core/build/AppFeaturePolicy.android.kt | 3 +
.../core/build/AppFeaturePolicy.android.kt | 3 +
.../composeResources/values/strings.xml | 6 +
.../commonMain/kotlin/com/nuvio/app/App.kt | 16 ++-
.../nuvio/app/core/build/AppFeaturePolicy.kt | 3 +
.../nuvio/app/features/addons/AddonsScreen.kt | 50 ++++++++-
.../features/settings/AccountSettingsPage.kt | 104 ++++++++++++++++++
.../settings/ContentDiscoverySettingsPage.kt | 10 +-
.../app/features/settings/SettingsRootPage.kt | 19 ++--
.../app/features/settings/SettingsScreen.kt | 64 +++++++++--
.../app/features/settings/SettingsSearch.kt | 43 ++++++--
.../core/build/AppFeaturePolicy.desktop.kt | 3 +
.../app/core/build/AppFeaturePolicy.ios.kt | 3 +
.../app/core/build/AppFeaturePolicy.ios.kt | 3 +
14 files changed, 289 insertions(+), 41 deletions(-)
diff --git a/composeApp/src/androidFull/kotlin/com/nuvio/app/core/build/AppFeaturePolicy.android.kt b/composeApp/src/androidFull/kotlin/com/nuvio/app/core/build/AppFeaturePolicy.android.kt
index 62e4aa33..c09e2c29 100644
--- a/composeApp/src/androidFull/kotlin/com/nuvio/app/core/build/AppFeaturePolicy.android.kt
+++ b/composeApp/src/androidFull/kotlin/com/nuvio/app/core/build/AppFeaturePolicy.android.kt
@@ -2,6 +2,9 @@ package com.nuvio.app.core.build
actual object AppFeaturePolicy {
actual val pluginsEnabled: Boolean = true
+ actual val supportersContributorsPageEnabled: Boolean = true
+ actual val accountDeletionEnabled: Boolean = false
+ actual val personalMediaAddonCopyEnabled: Boolean = false
actual val p2pEnabled: Boolean = true
actual val trailerPlaybackMode: TrailerPlaybackMode = TrailerPlaybackMode.IN_APP
actual val heroTrailerPlaybackSupported: Boolean = true
diff --git a/composeApp/src/androidPlaystore/kotlin/com/nuvio/app/core/build/AppFeaturePolicy.android.kt b/composeApp/src/androidPlaystore/kotlin/com/nuvio/app/core/build/AppFeaturePolicy.android.kt
index 1a639c5c..3be7ac2f 100644
--- a/composeApp/src/androidPlaystore/kotlin/com/nuvio/app/core/build/AppFeaturePolicy.android.kt
+++ b/composeApp/src/androidPlaystore/kotlin/com/nuvio/app/core/build/AppFeaturePolicy.android.kt
@@ -2,6 +2,9 @@ package com.nuvio.app.core.build
actual object AppFeaturePolicy {
actual val pluginsEnabled: Boolean = false
+ actual val supportersContributorsPageEnabled: Boolean = true
+ actual val accountDeletionEnabled: Boolean = false
+ actual val personalMediaAddonCopyEnabled: Boolean = false
actual val p2pEnabled: Boolean = true
actual val trailerPlaybackMode: TrailerPlaybackMode = TrailerPlaybackMode.EXTERNAL
actual val heroTrailerPlaybackSupported: Boolean = false
diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml
index 008521a5..ff7e6c75 100644
--- a/composeApp/src/commonMain/composeResources/values/strings.xml
+++ b/composeApp/src/commonMain/composeResources/values/strings.xml
@@ -32,6 +32,11 @@
Unavailable
Configure addon
Delete addon
+ Connect your own media server to browse and play from your personal library.
+ Add a server URL above when you want Nuvio to show your private library.
+ No personal libraries connected.
+ Server URL
+ Add
Add a manifest URL to start loading catalogs, metadata, streams or subtitles into Nuvio.
No addons installed yet.
Enter an addon URL.
@@ -634,6 +639,7 @@
HOME
SOURCES
Install, remove, refresh, and sort your content sources.
+ Connect personal media sources and manage access to your own library.
Install JavaScript scraper repositories and test providers internally.
Adjust home layout, content visibility, and poster behavior
Settings for the detail and episode screens.
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/App.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/App.kt
index 8ac3a7b5..4f040a0b 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/App.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/App.kt
@@ -1582,7 +1582,9 @@ private fun MainAppContent(
},
onAccountSettingsClick = { navController.navigate(AccountSettingsRoute) },
onSupportersContributorsSettingsClick = {
- navController.navigate(SupportersContributorsSettingsRoute)
+ if (AppFeaturePolicy.supportersContributorsPageEnabled) {
+ navController.navigate(SupportersContributorsSettingsRoute)
+ }
},
onLicensesAttributionsSettingsClick = {
navController.navigate(LicensesAttributionsSettingsRoute)
@@ -2681,9 +2683,15 @@ private fun MainAppContent(
navController = navController,
backStackEntry = backStackEntry,
)
- SupportersContributorsSettingsScreen(
- onBack = onBack,
- )
+ if (AppFeaturePolicy.supportersContributorsPageEnabled) {
+ SupportersContributorsSettingsScreen(
+ onBack = onBack,
+ )
+ } else {
+ LaunchedEffect(Unit) {
+ onBack()
+ }
+ }
}
composable { backStackEntry ->
val onBack = rememberGuardedPopBackStack(
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/core/build/AppFeaturePolicy.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/core/build/AppFeaturePolicy.kt
index 55f7c1e1..3c3f7470 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/core/build/AppFeaturePolicy.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/core/build/AppFeaturePolicy.kt
@@ -7,6 +7,9 @@ enum class TrailerPlaybackMode {
expect object AppFeaturePolicy {
val pluginsEnabled: Boolean
+ val supportersContributorsPageEnabled: Boolean
+ val accountDeletionEnabled: Boolean
+ val personalMediaAddonCopyEnabled: Boolean
val p2pEnabled: Boolean
val trailerPlaybackMode: TrailerPlaybackMode
val heroTrailerPlaybackSupported: Boolean
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/addons/AddonsScreen.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/addons/AddonsScreen.kt
index 7e12666d..2d7db4a8 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/addons/AddonsScreen.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/addons/AddonsScreen.kt
@@ -44,6 +44,7 @@ import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.nuvio.app.core.build.AppFeaturePolicy
import coil3.compose.AsyncImage
import com.nuvio.app.core.ui.NuvioIconActionButton
import com.nuvio.app.core.ui.NuvioInfoBadge
@@ -93,6 +94,7 @@ internal fun AddonsSettingsPageContent(
var formMessage by rememberSaveable { mutableStateOf(null) }
var installModalState by remember { mutableStateOf(null) }
val enterAddonUrlMessage = stringResource(Res.string.addons_error_enter_url)
+ val usePersonalMediaCopy = AppFeaturePolicy.personalMediaAddonCopyEnabled
val overview = remember(uiState.addons) { uiState.addons.toOverview() }
@@ -107,6 +109,7 @@ internal fun AddonsSettingsPageContent(
AddAddonCard(
addonUrl = addonUrl,
formMessage = formMessage,
+ usePersonalMediaCopy = usePersonalMediaCopy,
onAddonUrlChange = {
addonUrl = it
formMessage = null
@@ -138,7 +141,7 @@ internal fun AddonsSettingsPageContent(
SectionHeader(stringResource(Res.string.addons_section_installed))
if (uiState.addons.isEmpty()) {
- EmptyStateCard()
+ EmptyStateCard(usePersonalMediaCopy = usePersonalMediaCopy)
} else {
val lastIndex = uiState.addons.lastIndex
uiState.addons.forEachIndexed { index, addon ->
@@ -283,6 +286,7 @@ private fun VerticalSeparator() {
private fun AddAddonCard(
addonUrl: String,
formMessage: String?,
+ usePersonalMediaCopy: Boolean,
onAddonUrlChange: (String) -> Unit,
onAddClick: () -> Unit,
) {
@@ -290,14 +294,34 @@ private fun AddAddonCard(
NuvioInputField(
value = addonUrl,
onValueChange = onAddonUrlChange,
- placeholder = stringResource(Res.string.addons_input_placeholder),
+ placeholder = stringResource(
+ if (usePersonalMediaCopy) {
+ Res.string.addons_appstore_input_placeholder
+ } else {
+ Res.string.addons_input_placeholder
+ },
+ ),
)
Spacer(modifier = Modifier.height(18.dp))
NuvioPrimaryButton(
- text = stringResource(Res.string.addons_install_button),
+ text = stringResource(
+ if (usePersonalMediaCopy) {
+ Res.string.addons_appstore_install_button
+ } else {
+ Res.string.addons_install_button
+ },
+ ),
enabled = addonUrl.isNotBlank(),
onClick = onAddClick,
)
+ if (usePersonalMediaCopy) {
+ Spacer(modifier = Modifier.height(14.dp))
+ Text(
+ text = stringResource(Res.string.addons_appstore_add_description),
+ style = MaterialTheme.typography.bodyLarge,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ )
+ }
formMessage?.let { message ->
Spacer(modifier = Modifier.height(14.dp))
Text(
@@ -330,16 +354,30 @@ private sealed interface AddonInstallModalState {
}
@Composable
-private fun EmptyStateCard() {
+private fun EmptyStateCard(
+ usePersonalMediaCopy: Boolean,
+) {
NuvioSurfaceCard {
Text(
- text = stringResource(Res.string.addons_empty_title),
+ text = stringResource(
+ if (usePersonalMediaCopy) {
+ Res.string.addons_appstore_empty_title
+ } else {
+ Res.string.addons_empty_title
+ },
+ ),
style = MaterialTheme.typography.titleLarge,
color = MaterialTheme.colorScheme.onSurface,
)
Spacer(modifier = Modifier.height(8.dp))
Text(
- text = stringResource(Res.string.addons_empty_subtitle),
+ text = stringResource(
+ if (usePersonalMediaCopy) {
+ Res.string.addons_appstore_empty_subtitle
+ } else {
+ Res.string.addons_empty_subtitle
+ },
+ ),
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/AccountSettingsPage.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/AccountSettingsPage.kt
index 9c875a37..7cc47203 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/AccountSettingsPage.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/AccountSettingsPage.kt
@@ -7,6 +7,8 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.lazy.LazyListScope
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -25,16 +27,24 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.nuvio.app.core.auth.AuthRepository
import com.nuvio.app.core.auth.AuthState
+import com.nuvio.app.core.build.AppFeaturePolicy
import com.nuvio.app.core.network.SyncBackendRepository
import com.nuvio.app.core.ui.NuvioPrimaryButton
import com.nuvio.app.core.ui.NuvioStatusModal
import com.nuvio.app.core.ui.NuvioSurfaceCard
+import com.nuvio.app.core.ui.NuvioTokens
+import com.nuvio.app.core.ui.nuvio
import com.nuvio.app.features.dev.DebugSyncBackendSwitch
import com.nuvio.app.features.dev.shouldShowDebugSyncBackendSwitch
import kotlinx.coroutines.launch
import nuvio.composeapp.generated.resources.Res
import nuvio.composeapp.generated.resources.action_cancel
import nuvio.composeapp.generated.resources.compose_settings_page_account
+import nuvio.composeapp.generated.resources.auth_account_deletion_failed
+import nuvio.composeapp.generated.resources.settings_account_delete_account
+import nuvio.composeapp.generated.resources.settings_account_delete_account_description
+import nuvio.composeapp.generated.resources.settings_account_delete_confirm_message
+import nuvio.composeapp.generated.resources.settings_account_delete_confirm_title
import nuvio.composeapp.generated.resources.settings_account_email
import nuvio.composeapp.generated.resources.settings_account_not_signed_in
import nuvio.composeapp.generated.resources.settings_account_sign_out
@@ -62,7 +72,12 @@ private fun AccountSettingsBody(
val syncBackendState by SyncBackendRepository.state.collectAsStateWithLifecycle()
val scope = rememberCoroutineScope()
var showSignOutConfirm by remember { mutableStateOf(false) }
+ var showDeleteConfirm by remember { mutableStateOf(false) }
+ var isDeletingAccount by remember { mutableStateOf(false) }
+ var deleteErrorMessage by remember { mutableStateOf(null) }
val syncBackendLabel = syncBackendState.selectedBackend.displayName
+ val deleteAccountFallbackMessage = stringResource(Res.string.auth_account_deletion_failed)
+ val canDeleteAccount = AppFeaturePolicy.accountDeletionEnabled && authState is AuthState.Authenticated
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
NuvioSurfaceCard {
@@ -121,6 +136,16 @@ private fun AccountSettingsBody(
text = stringResource(Res.string.settings_account_sign_out),
onClick = { showSignOutConfirm = true },
)
+
+ if (canDeleteAccount) {
+ DeleteAccountCard(
+ errorMessage = deleteErrorMessage,
+ onDeleteClick = {
+ deleteErrorMessage = null
+ showDeleteConfirm = true
+ },
+ )
+ }
}
NuvioStatusModal(
@@ -135,6 +160,85 @@ private fun AccountSettingsBody(
},
onDismiss = { showSignOutConfirm = false },
)
+
+ NuvioStatusModal(
+ title = stringResource(Res.string.settings_account_delete_confirm_title),
+ message = stringResource(Res.string.settings_account_delete_confirm_message),
+ isVisible = showDeleteConfirm,
+ isBusy = isDeletingAccount,
+ confirmText = stringResource(Res.string.settings_account_delete_account),
+ dismissText = stringResource(Res.string.action_cancel),
+ onConfirm = {
+ if (isDeletingAccount) return@NuvioStatusModal
+ isDeletingAccount = true
+ scope.launch {
+ val result = AuthRepository.deleteAccount()
+ isDeletingAccount = false
+ showDeleteConfirm = false
+ deleteErrorMessage = if (result.isSuccess) {
+ null
+ } else {
+ AuthRepository.error.value
+ ?: result.exceptionOrNull()?.message
+ ?: deleteAccountFallbackMessage
+ }
+ }
+ },
+ onDismiss = {
+ if (!isDeletingAccount) {
+ showDeleteConfirm = false
+ }
+ },
+ )
+}
+
+@Composable
+private fun DeleteAccountCard(
+ errorMessage: String?,
+ onDeleteClick: () -> Unit,
+) {
+ val tokens = MaterialTheme.nuvio
+
+ NuvioSurfaceCard {
+ Text(
+ text = stringResource(Res.string.settings_account_delete_account),
+ style = MaterialTheme.typography.titleMedium,
+ color = tokens.colors.textPrimary,
+ fontWeight = FontWeight.SemiBold,
+ )
+ Spacer(modifier = Modifier.height(8.dp))
+ Text(
+ text = stringResource(Res.string.settings_account_delete_account_description),
+ style = MaterialTheme.typography.bodyMedium,
+ color = tokens.colors.textMuted,
+ )
+ errorMessage?.let { message ->
+ Spacer(modifier = Modifier.height(8.dp))
+ Text(
+ text = message,
+ style = MaterialTheme.typography.bodySmall,
+ color = tokens.colors.danger,
+ )
+ }
+ Spacer(modifier = Modifier.height(14.dp))
+ Button(
+ onClick = onDeleteClick,
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(NuvioTokens.Space.s48 + NuvioTokens.Space.s4),
+ shape = tokens.shapes.button,
+ colors = ButtonDefaults.buttonColors(
+ containerColor = tokens.colors.danger,
+ contentColor = tokens.colors.textInverse,
+ ),
+ ) {
+ Text(
+ text = stringResource(Res.string.settings_account_delete_account),
+ style = MaterialTheme.typography.titleMedium,
+ textAlign = TextAlign.Center,
+ )
+ }
+ }
}
@Composable
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/ContentDiscoverySettingsPage.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/ContentDiscoverySettingsPage.kt
index 438fd3c3..bd2652ca 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/ContentDiscoverySettingsPage.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/ContentDiscoverySettingsPage.kt
@@ -7,6 +7,7 @@ import androidx.compose.material.icons.rounded.Extension
import androidx.compose.material.icons.rounded.Home
import androidx.compose.material.icons.rounded.Hub
import androidx.compose.material.icons.rounded.Tune
+import com.nuvio.app.core.build.AppFeaturePolicy
import nuvio.composeapp.generated.resources.Res
import nuvio.composeapp.generated.resources.compose_settings_page_addons
import nuvio.composeapp.generated.resources.compose_settings_page_homescreen
@@ -14,6 +15,7 @@ import nuvio.composeapp.generated.resources.compose_settings_page_meta_screen
import nuvio.composeapp.generated.resources.compose_settings_page_plugins
import nuvio.composeapp.generated.resources.collections_header
import nuvio.composeapp.generated.resources.settings_content_discovery_addons_description
+import nuvio.composeapp.generated.resources.settings_content_discovery_addons_description_appstore
import nuvio.composeapp.generated.resources.settings_content_discovery_collections_description
import nuvio.composeapp.generated.resources.settings_content_discovery_homescreen_description
import nuvio.composeapp.generated.resources.settings_content_discovery_meta_screen_description
@@ -39,7 +41,13 @@ internal fun LazyListScope.contentDiscoveryContent(
SettingsGroup(isTablet = isTablet) {
SettingsNavigationRow(
title = stringResource(Res.string.compose_settings_page_addons),
- description = stringResource(Res.string.settings_content_discovery_addons_description),
+ description = stringResource(
+ if (AppFeaturePolicy.personalMediaAddonCopyEnabled) {
+ Res.string.settings_content_discovery_addons_description_appstore
+ } else {
+ Res.string.settings_content_discovery_addons_description
+ },
+ ),
icon = Icons.Rounded.Extension,
isTablet = isTablet,
onClick = onAddonsClick,
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsRootPage.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsRootPage.kt
index f557f762..36f04b12 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsRootPage.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsRootPage.kt
@@ -79,6 +79,7 @@ internal fun LazyListScope.settingsRootContent(
showGeneralSection: Boolean = true,
showAboutSection: Boolean = true,
showAdvancedSection: Boolean = true,
+ showSupportersContributorsPage: Boolean = true,
) {
if (showAccountSection) {
item {
@@ -189,14 +190,16 @@ internal fun LazyListScope.settingsRootContent(
isTablet = isTablet,
) {
SettingsGroup(isTablet = isTablet) {
- SettingsNavigationRow(
- title = stringResource(Res.string.compose_settings_page_supporters_contributors),
- description = stringResource(Res.string.about_supporters_contributors_subtitle),
- icon = Icons.Rounded.Favorite,
- isTablet = isTablet,
- onClick = onSupportersContributorsClick,
- )
- SettingsGroupDivider(isTablet = isTablet)
+ if (showSupportersContributorsPage) {
+ SettingsNavigationRow(
+ title = stringResource(Res.string.compose_settings_page_supporters_contributors),
+ description = stringResource(Res.string.about_supporters_contributors_subtitle),
+ icon = Icons.Rounded.Favorite,
+ isTablet = isTablet,
+ onClick = onSupportersContributorsClick,
+ )
+ SettingsGroupDivider(isTablet = isTablet)
+ }
SettingsNavigationRow(
title = stringResource(Res.string.compose_settings_page_licenses_attributions),
description = stringResource(Res.string.about_licenses_attributions_subtitle),
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsScreen.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsScreen.kt
index ea95d9ff..fab7e7ed 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsScreen.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsScreen.kt
@@ -94,6 +94,12 @@ private val SettingsSearchRevealThreshold = 28.dp
private const val SettingsSearchRevealAnimationMillis = 240L
private const val SettingsSearchRevealHapticDelayMillis = 90L
+private fun SettingsPage.isEnabledByPolicy(): Boolean =
+ when (this) {
+ SettingsPage.SupportersContributors -> AppFeaturePolicy.supportersContributorsPageEnabled
+ else -> true
+ }
+
@Composable
fun SettingsScreen(
modifier: Modifier = Modifier,
@@ -216,9 +222,20 @@ fun SettingsScreen(
var currentPage by rememberSaveable { mutableStateOf(SettingsPage.Root.name) }
val scrollToTopRequests = remember { MutableSharedFlow(extraBufferCapacity = 1) }
- val page = remember(currentPage) { SettingsPage.valueOf(currentPage) }
+ val page = remember(currentPage) {
+ runCatching { SettingsPage.valueOf(currentPage) }
+ .getOrDefault(SettingsPage.Root)
+ .takeIf { it.isEnabledByPolicy() }
+ ?: SettingsPage.Root
+ }
val previousPage = page.previousPage()
+ LaunchedEffect(page, currentPage) {
+ if (page.name != currentPage) {
+ currentPage = page.name
+ }
+ }
+
LaunchedEffect(rootActionRequests, rootActionsEnabled, page) {
rootActionRequests.collect {
if (!rootActionsEnabled) return@collect
@@ -232,9 +249,12 @@ fun SettingsScreen(
}
LaunchedEffect(requestedPageName, rootActionsEnabled) {
- val targetPage = requestedPageName
- ?.let { runCatching { SettingsPage.valueOf(it) }.getOrNull() }
- ?: return@LaunchedEffect
+ val requestedPage = requestedPageName ?: return@LaunchedEffect
+ val targetPage = runCatching { SettingsPage.valueOf(requestedPage) }.getOrNull()
+ if (targetPage == null || !targetPage.isEnabledByPolicy()) {
+ onRequestedPageConsumed()
+ return@LaunchedEffect
+ }
if (!rootActionsEnabled) return@LaunchedEffect
currentPage = targetPage.name
onRequestedPageConsumed()
@@ -449,6 +469,9 @@ private fun MobileSettingsScreen(
}
val searchEntries = settingsSearchEntries(
pluginsEnabled = AppFeaturePolicy.pluginsEnabled,
+ supportersContributorsPageEnabled = AppFeaturePolicy.supportersContributorsPageEnabled,
+ accountDeletionEnabled = AppFeaturePolicy.accountDeletionEnabled,
+ personalMediaAddonCopyEnabled = AppFeaturePolicy.personalMediaAddonCopyEnabled,
liquidGlassNativeTabBarSupported = liquidGlassNativeTabBarSupported,
switchProfileAvailable = onSwitchProfile != null,
checkForUpdatesAvailable = onCheckForUpdatesClick != null,
@@ -458,7 +481,11 @@ private fun MobileSettingsScreen(
when (target) {
is SettingsSearchTarget.Page -> when (target.page) {
SettingsPage.Account -> onAccountClick()
- SettingsPage.SupportersContributors -> onSupportersContributorsClick()
+ SettingsPage.SupportersContributors -> {
+ if (AppFeaturePolicy.supportersContributorsPageEnabled) {
+ onSupportersContributorsClick()
+ }
+ }
SettingsPage.LicensesAttributions -> onLicensesAttributionsClick()
SettingsPage.ContinueWatching -> onContinueWatchingClick()
SettingsPage.Addons -> onAddonsClick()
@@ -531,15 +558,18 @@ private fun MobileSettingsScreen(
onDownloadsClick = onDownloadsClick,
onAccountClick = onAccountClick,
onSwitchProfileClick = onSwitchProfile,
+ showSupportersContributorsPage = AppFeaturePolicy.supportersContributorsPageEnabled,
)
}
}
SettingsPage.Account -> accountSettingsContent(
isTablet = false,
)
- SettingsPage.SupportersContributors -> supportersContributorsContent(
- isTablet = false,
- )
+ SettingsPage.SupportersContributors -> {
+ if (AppFeaturePolicy.supportersContributorsPageEnabled) {
+ supportersContributorsContent(isTablet = false)
+ }
+ }
SettingsPage.LicensesAttributions -> licensesAttributionsContent(
isTablet = false,
)
@@ -822,6 +852,9 @@ private fun TabletSettingsScreen(
val hapticScope = rememberCoroutineScope()
val searchEntries = settingsSearchEntries(
pluginsEnabled = AppFeaturePolicy.pluginsEnabled,
+ supportersContributorsPageEnabled = AppFeaturePolicy.supportersContributorsPageEnabled,
+ accountDeletionEnabled = AppFeaturePolicy.accountDeletionEnabled,
+ personalMediaAddonCopyEnabled = AppFeaturePolicy.personalMediaAddonCopyEnabled,
liquidGlassNativeTabBarSupported = liquidGlassNativeTabBarSupported,
switchProfileAvailable = onSwitchProfile != null,
checkForUpdatesAvailable = onCheckForUpdatesClick != null,
@@ -829,7 +862,11 @@ private fun TabletSettingsScreen(
fun openSearchTarget(target: SettingsSearchTarget) {
when (target) {
- is SettingsSearchTarget.Page -> openInlinePage(target.page)
+ is SettingsSearchTarget.Page -> {
+ if (target.page.isEnabledByPolicy()) {
+ openInlinePage(target.page)
+ }
+ }
SettingsSearchTarget.Downloads -> onDownloadsClick()
SettingsSearchTarget.Collections -> onCollectionsClick()
SettingsSearchTarget.SwitchProfile -> onSwitchProfile?.invoke()
@@ -924,15 +961,18 @@ private fun TabletSettingsScreen(
showGeneralSection = activeCategory == SettingsCategory.General,
showAboutSection = activeCategory == SettingsCategory.About,
showAdvancedSection = activeCategory == SettingsCategory.Advanced,
+ showSupportersContributorsPage = AppFeaturePolicy.supportersContributorsPageEnabled,
)
}
}
SettingsPage.Account -> accountSettingsContent(
isTablet = true,
)
- SettingsPage.SupportersContributors -> supportersContributorsContent(
- isTablet = true,
- )
+ SettingsPage.SupportersContributors -> {
+ if (AppFeaturePolicy.supportersContributorsPageEnabled) {
+ supportersContributorsContent(isTablet = true)
+ }
+ }
SettingsPage.LicensesAttributions -> licensesAttributionsContent(
isTablet = true,
)
diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsSearch.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsSearch.kt
index 248d6397..69ab6a9a 100644
--- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsSearch.kt
+++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/SettingsSearch.kt
@@ -79,6 +79,9 @@ internal data class SettingsSearchEntry(
@Composable
internal fun settingsSearchEntries(
pluginsEnabled: Boolean,
+ supportersContributorsPageEnabled: Boolean,
+ accountDeletionEnabled: Boolean,
+ personalMediaAddonCopyEnabled: Boolean,
liquidGlassNativeTabBarSupported: Boolean,
switchProfileAvailable: Boolean,
checkForUpdatesAvailable: Boolean,
@@ -261,14 +264,16 @@ internal fun settingsSearchEntries(
description = stringResource(Res.string.compose_settings_root_notifications_description),
icon = Icons.Rounded.Notifications,
)
- addPage(
- page = SettingsPage.SupportersContributors,
- key = "supporters",
- title = supportersPage,
- description = stringResource(Res.string.about_supporters_contributors_subtitle),
- category = aboutCategory,
- icon = Icons.Rounded.Favorite,
- )
+ if (supportersContributorsPageEnabled) {
+ addPage(
+ page = SettingsPage.SupportersContributors,
+ key = "supporters",
+ title = supportersPage,
+ description = stringResource(Res.string.about_supporters_contributors_subtitle),
+ category = aboutCategory,
+ icon = Icons.Rounded.Favorite,
+ )
+ }
addPage(
page = SettingsPage.LicensesAttributions,
key = "licenses-attributions",
@@ -316,7 +321,7 @@ internal fun settingsSearchEntries(
key = "check-updates",
title = stringResource(Res.string.compose_settings_root_check_updates_title),
description = stringResource(Res.string.compose_settings_root_check_updates_description),
- page = supportersPage,
+ page = if (supportersContributorsPageEnabled) supportersPage else licensesPage,
section = stringResource(Res.string.compose_settings_root_about_section),
category = aboutCategory,
icon = Icons.Rounded.CloudDownload,
@@ -342,6 +347,18 @@ internal fun settingsSearchEntries(
category = accountCategory,
icon = Icons.Rounded.AccountCircle,
)
+ if (accountDeletionEnabled) {
+ addRow(
+ page = SettingsPage.Account,
+ key = "account-delete",
+ title = stringResource(Res.string.settings_account_delete_account),
+ description = stringResource(Res.string.settings_account_delete_account_description),
+ pageLabel = accountPage,
+ section = accountPage,
+ category = accountCategory,
+ icon = Icons.Rounded.AccountCircle,
+ )
+ }
addRow(
page = SettingsPage.Appearance,
@@ -418,7 +435,13 @@ internal fun settingsSearchEntries(
page = SettingsPage.Addons,
key = "addons",
title = addonsPage,
- description = stringResource(Res.string.settings_content_discovery_addons_description),
+ description = stringResource(
+ if (personalMediaAddonCopyEnabled) {
+ Res.string.settings_content_discovery_addons_description_appstore
+ } else {
+ Res.string.settings_content_discovery_addons_description
+ },
+ ),
icon = Icons.Rounded.Extension,
)
if (pluginsEnabled) {
diff --git a/composeApp/src/desktopMain/kotlin/com/nuvio/app/core/build/AppFeaturePolicy.desktop.kt b/composeApp/src/desktopMain/kotlin/com/nuvio/app/core/build/AppFeaturePolicy.desktop.kt
index ebffde7e..9d23979f 100644
--- a/composeApp/src/desktopMain/kotlin/com/nuvio/app/core/build/AppFeaturePolicy.desktop.kt
+++ b/composeApp/src/desktopMain/kotlin/com/nuvio/app/core/build/AppFeaturePolicy.desktop.kt
@@ -2,6 +2,9 @@ package com.nuvio.app.core.build
actual object AppFeaturePolicy {
actual val pluginsEnabled: Boolean = false
+ actual val supportersContributorsPageEnabled: Boolean = true
+ actual val accountDeletionEnabled: Boolean = false
+ actual val personalMediaAddonCopyEnabled: Boolean = false
actual val p2pEnabled: Boolean = false
actual val trailerPlaybackMode: TrailerPlaybackMode = TrailerPlaybackMode.EXTERNAL
actual val heroTrailerPlaybackSupported: Boolean = false
diff --git a/composeApp/src/iosAppStore/kotlin/com/nuvio/app/core/build/AppFeaturePolicy.ios.kt b/composeApp/src/iosAppStore/kotlin/com/nuvio/app/core/build/AppFeaturePolicy.ios.kt
index 37660c9c..20a1f552 100644
--- a/composeApp/src/iosAppStore/kotlin/com/nuvio/app/core/build/AppFeaturePolicy.ios.kt
+++ b/composeApp/src/iosAppStore/kotlin/com/nuvio/app/core/build/AppFeaturePolicy.ios.kt
@@ -2,6 +2,9 @@ package com.nuvio.app.core.build
actual object AppFeaturePolicy {
actual val pluginsEnabled: Boolean = false
+ actual val supportersContributorsPageEnabled: Boolean = false
+ actual val accountDeletionEnabled: Boolean = true
+ actual val personalMediaAddonCopyEnabled: Boolean = true
actual val p2pEnabled: Boolean = false
actual val trailerPlaybackMode: TrailerPlaybackMode = TrailerPlaybackMode.EXTERNAL
actual val heroTrailerPlaybackSupported: Boolean = false
diff --git a/composeApp/src/iosFull/kotlin/com/nuvio/app/core/build/AppFeaturePolicy.ios.kt b/composeApp/src/iosFull/kotlin/com/nuvio/app/core/build/AppFeaturePolicy.ios.kt
index 2a17ddfd..dc3e1839 100644
--- a/composeApp/src/iosFull/kotlin/com/nuvio/app/core/build/AppFeaturePolicy.ios.kt
+++ b/composeApp/src/iosFull/kotlin/com/nuvio/app/core/build/AppFeaturePolicy.ios.kt
@@ -2,6 +2,9 @@ package com.nuvio.app.core.build
actual object AppFeaturePolicy {
actual val pluginsEnabled: Boolean = true
+ actual val supportersContributorsPageEnabled: Boolean = true
+ actual val accountDeletionEnabled: Boolean = false
+ actual val personalMediaAddonCopyEnabled: Boolean = false
actual val p2pEnabled: Boolean = false
actual val trailerPlaybackMode: TrailerPlaybackMode = TrailerPlaybackMode.IN_APP
actual val heroTrailerPlaybackSupported: Boolean = false