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