diff --git a/app/src/main/kotlin/com/arflix/tv/ui/components/SettingsRows.kt b/app/src/main/kotlin/com/arflix/tv/ui/components/SettingsRows.kt index 2e67b3fc..5b9e7c84 100644 --- a/app/src/main/kotlin/com/arflix/tv/ui/components/SettingsRows.kt +++ b/app/src/main/kotlin/com/arflix/tv/ui/components/SettingsRows.kt @@ -29,7 +29,9 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.sp import androidx.tv.material3.ExperimentalTvMaterial3Api import com.arflix.tv.R +import androidx.compose.ui.draw.clip import com.arflix.tv.ui.theme.ArflixTypography +import com.arflix.tv.ui.theme.BackgroundElevated import com.arflix.tv.ui.theme.Pink import com.arflix.tv.ui.theme.SuccessGreen import com.arflix.tv.ui.theme.TextPrimary @@ -238,3 +240,119 @@ fun SettingsToggleRow( } } } + +@Composable +fun MobileSettingsCategory( + title: String, + content: @Composable androidx.compose.foundation.layout.ColumnScope.() -> Unit +) { + Column( + modifier = Modifier.fillMaxWidth() + ) { + Text( + text = title, + style = ArflixTypography.caption.copy(fontSize = 12.sp, letterSpacing = 1.sp), + color = TextSecondary, + modifier = Modifier.padding(start = 16.dp, bottom = 8.dp) + ) + Column( + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(12.dp)) + .background(BackgroundElevated) + ) { + content() + } + } +} + +@Composable +fun MobileSettingsRow( + icon: ImageVector, + title: String, + subtitle: String = "", + value: String?, + isFocused: Boolean = false, + showDivider: Boolean = true, + onClick: () -> Unit +) { + Column { + Row( + modifier = Modifier + .fillMaxWidth() + .clickable { onClick() } + .padding(horizontal = 16.dp, vertical = 14.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.weight(1f)) { + Icon( + imageVector = icon, + contentDescription = null, + tint = TextSecondary, + modifier = Modifier.size(24.dp) + ) + Spacer(modifier = Modifier.width(16.dp)) + Column { + Text( + text = title, + style = ArflixTypography.cardTitle.copy(fontSize = 16.sp), + color = TextPrimary, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + if (subtitle.isNotEmpty()) { + Text( + text = subtitle, + style = ArflixTypography.caption.copy(fontSize = 13.sp), + color = TextSecondary, + maxLines = 2, + overflow = TextOverflow.Ellipsis + ) + } + } + } + val safeValue = value.orEmpty() + if (safeValue.isNotEmpty()) { + Spacer(modifier = Modifier.width(16.dp)) + if (safeValue == "On" || safeValue == "Off") { + val isChecked = safeValue == "On" + Box( + modifier = Modifier + .width(44.dp) + .height(24.dp) + .background( + color = if (isChecked) SuccessGreen else Color.White.copy(alpha = 0.2f), + shape = RoundedCornerShape(13.dp) + ) + .padding(3.dp), + contentAlignment = if (isChecked) Alignment.CenterEnd else Alignment.CenterStart + ) { + Box( + modifier = Modifier + .size(18.dp) + .background( + color = Color.White, + shape = RoundedCornerShape(10.dp) + ) + ) + } + } else { + Text( + text = localizeSettingValue(safeValue), + style = ArflixTypography.caption.copy( + fontSize = 13.sp, + fontWeight = androidx.compose.ui.text.font.FontWeight.Medium + ), + color = TextSecondary, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + } + } + if (showDivider) { + Box(modifier = Modifier.fillMaxWidth().height(1.dp).padding(horizontal = 16.dp).background(Color.White.copy(alpha = 0.05f))) + } + } +} diff --git a/app/src/main/kotlin/com/arflix/tv/ui/screens/plugin/PluginScreen.kt b/app/src/main/kotlin/com/arflix/tv/ui/screens/plugin/PluginScreen.kt index cb56760c..2e64fad7 100644 --- a/app/src/main/kotlin/com/arflix/tv/ui/screens/plugin/PluginScreen.kt +++ b/app/src/main/kotlin/com/arflix/tv/ui/screens/plugin/PluginScreen.kt @@ -42,6 +42,8 @@ import androidx.tv.material3.ExperimentalTvMaterial3Api import androidx.tv.material3.Text import androidx.compose.material3.Icon import com.arflix.tv.R +import com.arflix.tv.ui.components.MobileSettingsCategory +import com.arflix.tv.ui.components.MobileSettingsRow import com.arflix.tv.ui.components.SettingsRow import com.arflix.tv.ui.components.SettingsToggleRow import com.arflix.tv.ui.skin.resolveAccentColor @@ -52,6 +54,7 @@ import com.arflix.tv.ui.theme.TextSecondary import com.arflix.tv.ui.theme.BackgroundElevated import com.arflix.tv.ui.screens.settings.LocalSettingsFocusTracker import com.arflix.tv.ui.screens.settings.settingsFocusSlot +import com.arflix.tv.util.LocalDeviceType @OptIn(ExperimentalTvMaterial3Api::class) @Composable @@ -71,6 +74,7 @@ fun PluginScreen( var showResetDialog by remember { mutableStateOf(false) } val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl val sectionNavKey = if (isRtl) Key.DirectionRight else Key.DirectionLeft + val isMobile = LocalDeviceType.current.isTouchDevice() val repositories = uiState.repositories val scrapers = uiState.scrapers @@ -79,18 +83,17 @@ fun PluginScreen( // Slot 0: Add button // Slot 1 to repos.size: Repos // Slot repos.size + 1 to repos.size + scrapers.size (or + 1 if empty text) - // Slot repos.size + scrapers.size + 1: Reset button val scrapersCount = if (scrapers.isEmpty()) 1 else scrapers.size - val totalItems = 1 + repositories.size + scrapersCount + 1 + val totalItems = 1 + repositories.size + scrapersCount LaunchedEffect(totalItems) { - onMaxIndexChanged(totalItems - 1) + onMaxIndexChanged((totalItems - 1).coerceAtLeast(0)) if (focusedIndex >= totalItems) { onFocusedIndexChanged((totalItems - 1).coerceAtLeast(0)) } } - val modalOpen = showAddDialog || showResetDialog + val modalOpen = showAddDialog LaunchedEffect(modalOpen) { onModalStateChanged(modalOpen) } @@ -110,165 +113,192 @@ fun PluginScreen( viewModel.onEvent(PluginUiEvent.ToggleScraper(scraper.id, !scraper.enabled)) } } - totalItems - 1 -> { showResetDialog = true } } onEnterTriggerHandled() } } - Column( - modifier = Modifier - .padding(bottom = 80.dp) - .fillMaxWidth() - .onPreviewKeyEvent { event -> - if (event.type == KeyEventType.KeyDown) { - when (event.key) { - sectionNavKey -> { - onNavigateToSection?.invoke() - return@onPreviewKeyEvent onNavigateToSection != null - } - Key.Back, Key.Escape -> { - onBackPressed() - return@onPreviewKeyEvent true - } - else -> {} + if (isMobile) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 24.dp), + verticalArrangement = Arrangement.spacedBy(24.dp) + ) { + uiState.errorMessage?.let { msg -> + Text(msg, color = Color.Red, style = ArflixTypography.body) + } + + MobileSettingsCategory(title = stringResource(R.string.plugin_screen_add_repo)) { + MobileSettingsRow( + icon = Icons.Default.Add, + title = stringResource(R.string.plugin_screen_add_repo), + subtitle = stringResource(R.string.plugin_screen_repo_url), + value = "", + isFocused = false, + showDivider = false, + onClick = { showAddDialog = true } + ) + } + + if (repositories.isNotEmpty()) { + MobileSettingsCategory(title = stringResource(R.string.plugin_screen_installed_repos)) { + repositories.forEachIndexed { idx, repo -> + MobileSettingsRow( + icon = Icons.Default.Extension, + title = repo.name, + subtitle = repo.url, + value = stringResource(R.string.delete), + isFocused = false, + showDivider = idx < repositories.lastIndex, + onClick = { viewModel.onEvent(PluginUiEvent.RemoveRepository(repo.id)) } + ) } } - false } - ) { - uiState.errorMessage?.let { msg -> - Text(msg, color = Color.Red, style = ArflixTypography.body) - Spacer(modifier = Modifier.height(16.dp)) - } - // Full width Add Button styled exactly like Settings Row actions - val accentColor = resolveAccentColor(fallback = Pink) - val isAddRowFocused = (focusedIndex == 0) - Row( + MobileSettingsCategory(title = stringResource(R.string.plugin_screen_installed_scrapers)) { + if (scrapers.isEmpty()) { + MobileSettingsRow( + icon = Icons.Default.Extension, + title = stringResource(R.string.plugin_screen_no_scrapers), + value = "", + isFocused = false, + showDivider = false, + onClick = {} + ) + } else { + scrapers.forEachIndexed { idx, scraper -> + MobileSettingsRow( + icon = Icons.Default.Extension, + title = scraper.name, + subtitle = scraper.id, + value = if (scraper.enabled) "On" else "Off", + isFocused = false, + showDivider = idx < scrapers.lastIndex, + onClick = { viewModel.onEvent(PluginUiEvent.ToggleScraper(scraper.id, !scraper.enabled)) } + ) + } + } + } + } + } else { + Column( modifier = Modifier + .padding(bottom = 80.dp) .fillMaxWidth() - .settingsFocusSlot(0) - .focusProperties { canFocus = false } - .clickable { showAddDialog = true } - .background( - if (isAddRowFocused) Color.White.copy(alpha = 0.12f) else Color.White.copy(alpha = 0.05f), - RoundedCornerShape(12.dp) - ) - .border( - width = if (isAddRowFocused) 2.dp else 0.dp, - color = if (isAddRowFocused) accentColor else Color.Transparent, - shape = RoundedCornerShape(12.dp) - ) - .padding(horizontal = 16.dp, vertical = 14.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Center + .onPreviewKeyEvent { event -> + if (event.type == KeyEventType.KeyDown) { + when (event.key) { + sectionNavKey -> { + onNavigateToSection?.invoke() + return@onPreviewKeyEvent onNavigateToSection != null + } + Key.Back, Key.Escape -> { + onBackPressed() + return@onPreviewKeyEvent true + } + else -> {} + } + } + false + } ) { - Icon( - imageVector = Icons.Default.Add, - contentDescription = null, - tint = accentColor, - modifier = Modifier.size(20.dp) - ) - Spacer(modifier = Modifier.width(12.dp)) - Text( - text = stringResource(R.string.plugin_screen_add_repo), - style = ArflixTypography.button, - color = accentColor - ) - } + uiState.errorMessage?.let { msg -> + Text(msg, color = Color.Red, style = ArflixTypography.body) + Spacer(modifier = Modifier.height(16.dp)) + } - Spacer(modifier = Modifier.height(24.dp)) + val accentColor = resolveAccentColor(fallback = Pink) + val isAddRowFocused = (focusedIndex == 0) + Row( + modifier = Modifier + .fillMaxWidth() + .settingsFocusSlot(0) + .clickable { showAddDialog = true } + .background( + if (isAddRowFocused) Color.White.copy(alpha = 0.12f) else Color.White.copy(alpha = 0.05f), + RoundedCornerShape(12.dp) + ) + .border( + width = if (isAddRowFocused) 2.dp else 0.dp, + color = if (isAddRowFocused) accentColor else Color.Transparent, + shape = RoundedCornerShape(12.dp) + ) + .padding(horizontal = 16.dp, vertical = 14.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center + ) { + Icon( + imageVector = Icons.Default.Add, + contentDescription = null, + tint = accentColor, + modifier = Modifier.size(20.dp) + ) + Spacer(modifier = Modifier.width(12.dp)) + Text( + text = stringResource(R.string.plugin_screen_add_repo), + style = ArflixTypography.button, + color = accentColor + ) + } - if (repositories.isNotEmpty()) { - Text( - text = stringResource(R.string.plugin_screen_installed_repos), - style = ArflixTypography.caption.copy(fontSize = 12.sp, letterSpacing = 1.sp), - color = TextSecondary, - modifier = Modifier.padding(bottom = 10.dp) - ) + Spacer(modifier = Modifier.height(24.dp)) - repositories.forEachIndexed { idx, repo -> - val slotIndex = 1 + idx - FocusableSettingsRow( - index = slotIndex, - focusedIndex = focusedIndex, - icon = Icons.Default.Delete, - title = repo.name, - subtitle = repo.url, - value = stringResource(R.string.delete), - onClick = { viewModel.onEvent(PluginUiEvent.RemoveRepository(repo.id)) } + if (repositories.isNotEmpty()) { + Text( + text = stringResource(R.string.plugin_screen_installed_repos), + style = ArflixTypography.caption.copy(fontSize = 12.sp, letterSpacing = 1.sp), + color = TextSecondary, + modifier = Modifier.padding(bottom = 10.dp) ) + + repositories.forEachIndexed { idx, repo -> + val slotIndex = 1 + idx + FocusableSettingsRow( + index = slotIndex, + focusedIndex = focusedIndex, + icon = Icons.Default.Delete, + title = repo.name, + subtitle = repo.url, + value = stringResource(R.string.delete), + onClick = { viewModel.onEvent(PluginUiEvent.RemoveRepository(repo.id)) } + ) + Spacer(modifier = Modifier.height(12.dp)) + } Spacer(modifier = Modifier.height(12.dp)) } - Spacer(modifier = Modifier.height(12.dp)) - } - - Text( - text = stringResource(R.string.plugin_screen_installed_scrapers), - style = ArflixTypography.caption.copy(fontSize = 12.sp, letterSpacing = 1.sp), - color = TextSecondary, - modifier = Modifier.padding(bottom = 10.dp) - ) - val scraperStartIdx = 1 + repositories.size - if (scrapers.isEmpty()) { - val slotIndex = scraperStartIdx Text( - text = stringResource(R.string.plugin_screen_no_scrapers), - style = ArflixTypography.body, + text = stringResource(R.string.plugin_screen_installed_scrapers), + style = ArflixTypography.caption.copy(fontSize = 12.sp, letterSpacing = 1.sp), color = TextSecondary, - modifier = Modifier.settingsFocusSlot(slotIndex) + modifier = Modifier.padding(bottom = 10.dp) ) - } else { - scrapers.forEachIndexed { idx, scraper -> - val slotIndex = scraperStartIdx + idx - FocusableSettingsToggleRow( - index = slotIndex, - focusedIndex = focusedIndex, - title = scraper.name, - subtitle = scraper.id, - isEnabled = scraper.enabled, - onToggle = { enabled -> viewModel.onEvent(PluginUiEvent.ToggleScraper(scraper.id, enabled)) } - ) - Spacer(modifier = Modifier.height(12.dp)) - } - } - Spacer(modifier = Modifier.height(24.dp)) - val resetIndex = totalItems - 1 - val isResetRowFocused = (focusedIndex == resetIndex) - Row( - modifier = Modifier - .fillMaxWidth() - .settingsFocusSlot(resetIndex) - .focusProperties { canFocus = false } - .clickable { showResetDialog = true } - .background( - if (isResetRowFocused) Color.White.copy(alpha = 0.12f) else Color.White.copy(alpha = 0.05f), - RoundedCornerShape(12.dp) + val scraperStartIdx = 1 + repositories.size + if (scrapers.isEmpty()) { + val slotIndex = scraperStartIdx + Text( + text = stringResource(R.string.plugin_screen_no_scrapers), + style = ArflixTypography.body, + color = TextSecondary, + modifier = Modifier.settingsFocusSlot(slotIndex) ) - .border( - width = if (isResetRowFocused) 2.dp else 0.dp, - color = if (isResetRowFocused) Color.Red else Color.Transparent, - shape = RoundedCornerShape(12.dp) - ) - .padding(horizontal = 16.dp, vertical = 14.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Center - ) { - Icon( - imageVector = Icons.Default.Delete, - contentDescription = null, - tint = Color.Red, - modifier = Modifier.size(20.dp) - ) - Spacer(modifier = Modifier.width(12.dp)) - Text( - text = "Reset Plugins & Extensions", - style = ArflixTypography.button, - color = Color.Red - ) + } else { + scrapers.forEachIndexed { idx, scraper -> + val slotIndex = scraperStartIdx + idx + FocusableSettingsToggleRow( + index = slotIndex, + focusedIndex = focusedIndex, + title = scraper.name, + subtitle = scraper.id, + isEnabled = scraper.enabled, + onToggle = { enabled -> viewModel.onEvent(PluginUiEvent.ToggleScraper(scraper.id, enabled)) } + ) + Spacer(modifier = Modifier.height(12.dp)) + } + } } } @@ -283,23 +313,6 @@ fun PluginScreen( } ) } - - if (showResetDialog) { - WarningDialog( - title = "Warning", - message = "Are you sure you want to delete all plugins, scrapers, and local code data? This action cannot be undone.", - cancelText = stringResource(R.string.cancel), - confirmText = stringResource(R.string.delete), - onConfirm = { - viewModel.onEvent(PluginUiEvent.ResetAllPlugins) - onFocusedIndexChanged(0) - showResetDialog = false - }, - onDismiss = { showResetDialog = false } - ) - } - - } @Composable @@ -339,9 +352,7 @@ fun FocusableSettingsRow( value = value, isFocused = (focusedIndex == index), onClick = onClick, - modifier = modifier - .settingsFocusSlot(index) - .focusProperties { canFocus = false } + modifier = modifier.settingsFocusSlot(index) ) } @@ -361,9 +372,7 @@ fun FocusableSettingsToggleRow( isEnabled = isEnabled, isFocused = (focusedIndex == index), onToggle = onToggle, - modifier = modifier - .settingsFocusSlot(index) - .focusProperties { canFocus = false } + modifier = modifier.settingsFocusSlot(index) ) } @@ -436,47 +445,39 @@ fun AddRepoDialog( Spacer(modifier = Modifier.height(20.dp)) - Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) { - androidx.tv.material3.Surface( - onClick = onDismiss, - modifier = Modifier.weight(1f).focusRequester(cancelFocus), - colors = androidx.tv.material3.ClickableSurfaceDefaults.colors( - containerColor = BackgroundElevated, - focusedContainerColor = BackgroundElevated.copy(alpha = 0.8f) - ), - shape = androidx.tv.material3.ClickableSurfaceDefaults.shape(RoundedCornerShape(8.dp)), - border = androidx.tv.material3.ClickableSurfaceDefaults.border( - border = androidx.tv.material3.Border( - border = androidx.compose.foundation.BorderStroke(1.dp, TextSecondary.copy(alpha = 0.3f)) - ) - ) + Row(horizontalArrangement = Arrangement.spacedBy(12.dp), modifier = Modifier.fillMaxWidth()) { + Box( + modifier = Modifier + .weight(1f) + .focusRequester(cancelFocus) + .clip(RoundedCornerShape(8.dp)) + .background(BackgroundElevated) + .border(1.dp, TextSecondary.copy(alpha = 0.3f), RoundedCornerShape(8.dp)) + .clickable { onDismiss() } + .padding(vertical = 12.dp), + contentAlignment = Alignment.Center ) { Text( text = stringResource(R.string.cancel), - modifier = Modifier.padding(vertical = 12.dp).fillMaxWidth(), textAlign = TextAlign.Center, color = TextSecondary, style = ArflixTypography.button ) } - androidx.tv.material3.Surface( - onClick = { onSave(value) }, - modifier = Modifier.weight(1f).focusRequester(saveFocus), - colors = androidx.tv.material3.ClickableSurfaceDefaults.colors( - containerColor = Pink.copy(alpha = 0.15f), - focusedContainerColor = Pink.copy(alpha = 0.25f) - ), - shape = androidx.tv.material3.ClickableSurfaceDefaults.shape(RoundedCornerShape(8.dp)), - border = androidx.tv.material3.ClickableSurfaceDefaults.border( - border = androidx.tv.material3.Border( - border = androidx.compose.foundation.BorderStroke(1.dp, Pink.copy(alpha = 0.4f)) - ) - ) + Box( + modifier = Modifier + .weight(1f) + .focusRequester(saveFocus) + .clip(RoundedCornerShape(8.dp)) + .background(Pink.copy(alpha = 0.15f)) + .border(1.dp, Pink.copy(alpha = 0.4f), RoundedCornerShape(8.dp)) + .clickable { onSave(value) } + .padding(vertical = 12.dp), + contentAlignment = Alignment.Center ) { Text( text = stringResource(R.string.add), - modifier = Modifier.padding(vertical = 12.dp).fillMaxWidth(), textAlign = TextAlign.Center, color = Pink, style = ArflixTypography.button @@ -489,117 +490,3 @@ fun AddRepoDialog( } } -@Composable -fun WarningDialog( - title: String, - message: String, - cancelText: String, - confirmText: String, - onConfirm: () -> Unit, - onDismiss: () -> Unit -) { - HideDialogSystemBars() - val cancelFocusRequester = remember { FocusRequester() } - val confirmFocus = remember { FocusRequester() } - - LaunchedEffect(Unit) { - kotlinx.coroutines.delay(100) - try { cancelFocusRequester.requestFocus() } catch (_: Exception) {} - } - - Dialog( - onDismissRequest = onDismiss, - properties = DialogProperties(dismissOnClickOutside = true, usePlatformDefaultWidth = false) - ) { - Box( - modifier = Modifier - .fillMaxSize() - .onPreviewKeyEvent { event -> - if (event.type == KeyEventType.KeyDown && - (event.key == Key.Back || event.key == Key.Escape)) { - onDismiss() - return@onPreviewKeyEvent true - } - false - }, - contentAlignment = Alignment.Center - ) { - Box( - modifier = Modifier - .width(420.dp) - .clip(RoundedCornerShape(16.dp)) - .background(BackgroundElevated) - .padding(24.dp) - ) { - Column(horizontalAlignment = Alignment.CenterHorizontally) { - Text( - text = title, - style = ArflixTypography.sectionTitle, - color = Color.Red, - modifier = Modifier.fillMaxWidth(), - textAlign = TextAlign.Start - ) - Spacer(modifier = Modifier.height(12.dp)) - Text( - text = message, - style = ArflixTypography.body, - color = TextSecondary, - modifier = Modifier.fillMaxWidth(), - textAlign = TextAlign.Start - ) - Spacer(modifier = Modifier.height(24.dp)) - Row( - horizontalArrangement = Arrangement.spacedBy(12.dp), - modifier = Modifier.fillMaxWidth() - ) { - androidx.tv.material3.Surface( - onClick = onDismiss, - modifier = Modifier.weight(1f).focusRequester(cancelFocusRequester), - colors = androidx.tv.material3.ClickableSurfaceDefaults.colors( - containerColor = BackgroundElevated, - focusedContainerColor = BackgroundElevated.copy(alpha = 0.8f) - ), - shape = androidx.tv.material3.ClickableSurfaceDefaults.shape(RoundedCornerShape(8.dp)), - border = androidx.tv.material3.ClickableSurfaceDefaults.border( - border = androidx.tv.material3.Border( - border = androidx.compose.foundation.BorderStroke(1.dp, TextSecondary.copy(alpha = 0.3f)) - ) - ) - ) { - Text( - text = cancelText, - modifier = Modifier.padding(vertical = 12.dp).fillMaxWidth(), - textAlign = TextAlign.Center, - color = TextSecondary, - style = ArflixTypography.button - ) - } - - androidx.tv.material3.Surface( - onClick = onConfirm, - modifier = Modifier.weight(1f).focusRequester(confirmFocus), - colors = androidx.tv.material3.ClickableSurfaceDefaults.colors( - containerColor = Pink.copy(alpha = 0.15f), - focusedContainerColor = Pink.copy(alpha = 0.25f) - ), - shape = androidx.tv.material3.ClickableSurfaceDefaults.shape(RoundedCornerShape(8.dp)), - border = androidx.tv.material3.ClickableSurfaceDefaults.border( - border = androidx.tv.material3.Border( - border = androidx.compose.foundation.BorderStroke(1.dp, Pink.copy(alpha = 0.4f)) - ) - ) - ) { - Text( - text = confirmText, - modifier = Modifier.padding(vertical = 12.dp).fillMaxWidth(), - textAlign = TextAlign.Center, - color = Pink, - style = ArflixTypography.button - ) - } - } - } - } - } - } -} diff --git a/app/src/main/kotlin/com/arflix/tv/ui/screens/settings/SettingsScreen.kt b/app/src/main/kotlin/com/arflix/tv/ui/screens/settings/SettingsScreen.kt index 6e619793..e179cc25 100644 --- a/app/src/main/kotlin/com/arflix/tv/ui/screens/settings/SettingsScreen.kt +++ b/app/src/main/kotlin/com/arflix/tv/ui/screens/settings/SettingsScreen.kt @@ -98,6 +98,8 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.activity.compose.BackHandler import androidx.compose.material3.Icon import com.arflix.tv.ui.components.LoadingIndicator +import com.arflix.tv.ui.components.MobileSettingsCategory +import com.arflix.tv.ui.components.MobileSettingsRow import com.arflix.tv.ui.components.QrCodeImage import com.arflix.tv.ui.components.Toast import com.arflix.tv.ui.components.ToastType as ComponentToastType @@ -4210,121 +4212,7 @@ private fun MobileSettingsSubPage( } } -@Composable -private fun MobileSettingsCategory( - title: String, - content: @Composable androidx.compose.foundation.layout.ColumnScope.() -> Unit -) { - Column( - modifier = Modifier.fillMaxWidth() - ) { - Text( - text = title, - style = ArflixTypography.caption.copy(fontSize = 12.sp, letterSpacing = 1.sp), - color = TextSecondary, - modifier = Modifier.padding(start = 16.dp, bottom = 8.dp) - ) - Column( - modifier = Modifier - .fillMaxWidth() - .clip(RoundedCornerShape(12.dp)) - .background(BackgroundElevated) - ) { - content() - } - } -} -@Composable -private fun MobileSettingsRow( - icon: ImageVector, - title: String, - subtitle: String = "", - value: String?, - isFocused: Boolean = false, - showDivider: Boolean = true, - onClick: () -> Unit -) { - Column { - Row( - modifier = Modifier - .fillMaxWidth() - .clickable { onClick() } - .padding(horizontal = 16.dp, vertical = 14.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween - ) { - Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.weight(1f)) { - Icon( - imageVector = icon, - contentDescription = null, - tint = TextSecondary, - modifier = Modifier.size(24.dp) - ) - Spacer(modifier = Modifier.width(16.dp)) - Column { - Text( - text = title, - style = ArflixTypography.cardTitle.copy(fontSize = 16.sp), - color = TextPrimary, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - if (subtitle.isNotEmpty()) { - Text( - text = subtitle, - style = ArflixTypography.caption.copy(fontSize = 13.sp), - color = TextSecondary, - maxLines = 2, - overflow = TextOverflow.Ellipsis - ) - } - } - } - val safeValue = value.orEmpty() - if (safeValue.isNotEmpty()) { - Spacer(modifier = Modifier.width(16.dp)) - if (safeValue == "On" || safeValue == "Off") { - val isChecked = safeValue == "On" - Box( - modifier = Modifier - .width(44.dp) - .height(24.dp) - .background( - color = if (isChecked) SuccessGreen else Color.White.copy(alpha = 0.2f), - shape = RoundedCornerShape(13.dp) - ) - .padding(3.dp), - contentAlignment = if (isChecked) Alignment.CenterEnd else Alignment.CenterStart - ) { - Box( - modifier = Modifier - .size(18.dp) - .background( - color = Color.White, - shape = RoundedCornerShape(10.dp) - ) - ) - } - } else { - Text( - text = localizeSettingValue(safeValue), - style = ArflixTypography.caption.copy( - fontSize = 13.sp, - fontWeight = androidx.compose.ui.text.font.FontWeight.Medium - ), - color = TextSecondary, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - } - } - } - if (showDivider) { - Box(modifier = Modifier.fillMaxWidth().height(1.dp).padding(horizontal = 16.dp).background(Color.White.copy(alpha = 0.05f))) - } - } -} private enum class Zone { SIDEBAR, SECTION, CONTENT