Merge branch 'appstorepolicy' into cmp-rewrite

This commit is contained in:
tapframe 2026-06-24 19:39:10 +05:30
commit e921e28141
14 changed files with 289 additions and 41 deletions

View file

@ -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

View file

@ -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

View file

@ -32,6 +32,11 @@
<string name="addons_badge_unavailable">Unavailable</string>
<string name="addons_configure">Configure addon</string>
<string name="addons_delete">Delete addon</string>
<string name="addons_appstore_add_description">Connect your own media server to browse and play from your personal library.</string>
<string name="addons_appstore_empty_subtitle">Add a server URL above when you want Nuvio to show your private library.</string>
<string name="addons_appstore_empty_title">No personal libraries connected.</string>
<string name="addons_appstore_input_placeholder">Server URL</string>
<string name="addons_appstore_install_button">Add</string>
<string name="addons_empty_subtitle">Add a manifest URL to start loading catalogs, metadata, streams or subtitles into Nuvio.</string>
<string name="addons_empty_title">No addons installed yet.</string>
<string name="addons_error_enter_url">Enter an addon URL.</string>
@ -634,6 +639,7 @@
<string name="settings_content_discovery_section_home">HOME</string>
<string name="settings_content_discovery_section_sources">SOURCES</string>
<string name="settings_content_discovery_addons_description">Install, remove, refresh, and sort your content sources.</string>
<string name="settings_content_discovery_addons_description_appstore">Connect personal media sources and manage access to your own library.</string>
<string name="settings_content_discovery_plugins_description">Install JavaScript scraper repositories and test providers internally.</string>
<string name="settings_content_discovery_homescreen_description">Adjust home layout, content visibility, and poster behavior</string>
<string name="settings_content_discovery_meta_screen_description">Settings for the detail and episode screens.</string>

View file

@ -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<LicensesAttributionsSettingsRoute> { backStackEntry ->
val onBack = rememberGuardedPopBackStack(

View file

@ -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

View file

@ -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<String?>(null) }
var installModalState by remember { mutableStateOf<AddonInstallModalState?>(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,
)

View file

@ -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<String?>(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

View file

@ -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,

View file

@ -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),

View file

@ -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<Unit>(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,
)

View file

@ -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) {

View file

@ -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

View file

@ -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

View file

@ -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