diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt index e4a7342a4..f63c22c5e 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt @@ -70,6 +70,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.geohash.GeoHashScreen import com.vitorpamplona.amethyst.ui.screen.loggedIn.hashtag.HashtagScreen import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.HomeScreen import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.NotificationScreen +import com.vitorpamplona.amethyst.ui.screen.loggedIn.privacy.PrivacyOptionsScreen import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.ProfileScreen import com.vitorpamplona.amethyst.ui.screen.loggedIn.redirect.LoadRedirectScreen import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.AllRelayListScreen @@ -111,6 +112,7 @@ fun AppNavigation( composable { SearchScreen(accountViewModel, nav) } composableFromEnd { SecurityFiltersScreen(accountViewModel, nav) } + composableFromEnd { PrivacyOptionsScreen(accountViewModel, nav) } composableFromEnd { BookmarkListScreen(accountViewModel, nav) } composableFromEnd { DraftListScreen(accountViewModel, nav) } composableFromEnd { SettingsScreen(sharedPreferencesViewModel, accountViewModel, nav) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/DrawerContent.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/DrawerContent.kt index ce4d6fa93..835278892 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/DrawerContent.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/DrawerContent.kt @@ -122,7 +122,6 @@ import com.vitorpamplona.amethyst.ui.theme.bannerModifier import com.vitorpamplona.amethyst.ui.theme.drawerSpacing import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.profileContentHeaderModifier -import com.vitorpamplona.amethyst.ui.tor.ConnectTorDialog import com.vitorpamplona.ammolite.relays.RelayPoolStatus import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.tags.addressables.Address @@ -429,8 +428,6 @@ fun ListContent( var backupDialogOpen by remember { mutableStateOf(false) } - var conectOrbotDialogOpen by remember { mutableStateOf(false) } - val context = LocalContext.current Column(modifier) { @@ -484,14 +481,12 @@ fun ListContent( route = Route.SecurityFilters, ) - IconRow( + NavigationRow( title = R.string.privacy_options, icon = R.drawable.ic_tor, tint = MaterialTheme.colorScheme.onBackground, - onClick = { - nav.closeDrawer() - conectOrbotDialogOpen = true - }, + nav = nav, + route = Route.PrivacyOptions, ) accountViewModel.account.settings.keyPair.privKey?.let { @@ -530,24 +525,6 @@ fun ListContent( if (backupDialogOpen) { AccountBackupDialog(accountViewModel, onClose = { backupDialogOpen = false }) } - if (conectOrbotDialogOpen) { - ConnectTorDialog( - torSettings = - accountViewModel.account.settings.torSettings - .toSettings(), - onClose = { conectOrbotDialogOpen = false }, - onPost = { torSettings -> - conectOrbotDialogOpen = false - accountViewModel.setTorSettings(torSettings) - }, - onError = { - accountViewModel.toastManager.toast( - stringRes(context, R.string.could_not_connect_to_tor), - it, - ) - }, - ) - } } @Composable diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/Routes.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/Routes.kt index 740ff56fc..944af3e7f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/Routes.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/Routes.kt @@ -54,6 +54,8 @@ sealed class Route { @Serializable object SecurityFilters : Route() + @Serializable object PrivacyOptions : Route() + @Serializable object Bookmarks : Route() @Serializable object Drafts : Route() @@ -150,6 +152,7 @@ fun getRouteWithArguments(navController: NavHostController): Route? { dest.hasRoute() -> entry.toRoute() dest.hasRoute() -> entry.toRoute() + dest.hasRoute() -> entry.toRoute() dest.hasRoute() -> entry.toRoute() dest.hasRoute() -> entry.toRoute() dest.hasRoute() -> entry.toRoute() diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/privacy/PrivacyOptionsScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/privacy/PrivacyOptionsScreen.kt new file mode 100644 index 000000000..53e457489 --- /dev/null +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/privacy/PrivacyOptionsScreen.kt @@ -0,0 +1,120 @@ +/** + * Copyright (c) 2024 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.amethyst.ui.screen.loggedIn.privacy + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel +import com.vitorpamplona.amethyst.R +import com.vitorpamplona.amethyst.ui.navigation.INav +import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel +import com.vitorpamplona.amethyst.ui.screen.loggedIn.CloseButton +import com.vitorpamplona.amethyst.ui.screen.loggedIn.SaveButton +import com.vitorpamplona.amethyst.ui.stringRes +import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer +import com.vitorpamplona.amethyst.ui.tor.PrivacySettingsBody +import com.vitorpamplona.amethyst.ui.tor.TorDialogViewModel + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun PrivacyOptionsScreen( + accountViewModel: AccountViewModel, + nav: INav, +) { + val dialogViewModel = viewModel() + + LaunchedEffect(dialogViewModel, accountViewModel) { + dialogViewModel.reset( + accountViewModel.account.settings.torSettings + .toSettings(), + ) + } + + Scaffold( + topBar = { + TopAppBar( + title = { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + ) { + Spacer(modifier = StdHorzSpacer) + + Text( + stringRes(id = R.string.privacy_options), + ) + SaveButton( + onPost = { + accountViewModel.setTorSettings(dialogViewModel.save()) + nav.popBack() + }, + isActive = true, + ) + } + }, + navigationIcon = { + Row { + Spacer(modifier = StdHorzSpacer) + CloseButton( + onPress = { + nav.popBack() + }, + ) + } + }, + colors = + TopAppBarDefaults.topAppBarColors( + containerColor = MaterialTheme.colorScheme.surface, + ), + ) + }, + ) { + Column( + Modifier + .padding(it) + .fillMaxSize() + .verticalScroll( + rememberScrollState(), + ).padding(horizontal = 10.dp), + ) { + PrivacySettingsBody(dialogViewModel) + } + } +} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/tor/TorSettingsDialog.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/tor/TorSettingsDialog.kt index 647d8810a..9792ab3e7 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/tor/TorSettingsDialog.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/tor/TorSettingsDialog.kt @@ -151,140 +151,145 @@ fun TorDialogContents( ) } + PrivacySettingsBody(dialogViewModel) + } +} + +@Composable +fun PrivacySettingsBody(dialogViewModel: TorDialogViewModel) { + Column( + modifier = Modifier.padding(horizontal = 5.dp), + verticalArrangement = Arrangement.spacedBy(Size10dp), + ) { + SettingsRow( + R.string.use_internal_tor, + R.string.use_internal_tor_explainer, + persistentListOf( + TitleExplainer(stringRes(TorType.OFF.resourceId)), + TitleExplainer(stringRes(TorType.INTERNAL.resourceId)), + TitleExplainer(stringRes(TorType.EXTERNAL.resourceId)), + ), + dialogViewModel.torType.value.screenCode, + ) { + dialogViewModel.torType.value = parseTorType(it) + } + + AnimatedVisibility( + visible = dialogViewModel.torType.value == TorType.EXTERNAL, + ) { + SettingsRow( + R.string.orbot_socks_port, + R.string.connect_through_your_orbot_setup_short, + ) { + OutlinedTextField( + value = dialogViewModel.socksPortStr.value, + onValueChange = { dialogViewModel.socksPortStr.value = it }, + keyboardOptions = + KeyboardOptions.Default.copy( + capitalization = KeyboardCapitalization.None, + keyboardType = KeyboardType.Number, + ), + placeholder = { + Text( + text = "9050", + color = MaterialTheme.colorScheme.placeholderText, + ) + }, + ) + } + } + } + + AnimatedVisibility( + visible = dialogViewModel.torType.value != TorType.OFF, + ) { Column( modifier = Modifier.padding(horizontal = 5.dp), verticalArrangement = Arrangement.spacedBy(Size10dp), ) { SettingsRow( - R.string.use_internal_tor, - R.string.use_internal_tor_explainer, + R.string.tor_preset, + R.string.tor_preset_explainer, persistentListOf( - TitleExplainer(stringRes(TorType.OFF.resourceId)), - TitleExplainer(stringRes(TorType.INTERNAL.resourceId)), - TitleExplainer(stringRes(TorType.EXTERNAL.resourceId)), + TitleExplainer(stringRes(TorPresetType.ONLY_WHEN_NEEDED.resourceId), stringRes(TorPresetType.ONLY_WHEN_NEEDED.explainerId)), + TitleExplainer(stringRes(TorPresetType.DEFAULT.resourceId), stringRes(TorPresetType.DEFAULT.explainerId)), + TitleExplainer(stringRes(TorPresetType.SMALL_PAYLOADS.resourceId), stringRes(TorPresetType.SMALL_PAYLOADS.explainerId)), + TitleExplainer(stringRes(TorPresetType.FULL_PRIVACY.resourceId), stringRes(TorPresetType.FULL_PRIVACY.explainerId)), + TitleExplainer(stringRes(TorPresetType.CUSTOM.resourceId), stringRes(TorPresetType.CUSTOM.explainerId)), ), - dialogViewModel.torType.value.screenCode, + dialogViewModel.preset.value.screenCode, ) { - dialogViewModel.torType.value = parseTorType(it) + dialogViewModel.setPreset(parseTorPresetType(it)) } - AnimatedVisibility( - visible = dialogViewModel.torType.value == TorType.EXTERNAL, - ) { - SettingsRow( - R.string.orbot_socks_port, - R.string.connect_through_your_orbot_setup_short, - ) { - OutlinedTextField( - value = dialogViewModel.socksPortStr.value, - onValueChange = { dialogViewModel.socksPortStr.value = it }, - keyboardOptions = - KeyboardOptions.Default.copy( - capitalization = KeyboardCapitalization.None, - keyboardType = KeyboardType.Number, - ), - placeholder = { - Text( - text = "9050", - color = MaterialTheme.colorScheme.placeholderText, - ) - }, - ) - } - } - } + SwitchSettingsRow( + R.string.tor_use_onion_address, + R.string.tor_use_onion_address_explainer, + dialogViewModel.onionRelaysViaTor, + ) - AnimatedVisibility( - visible = dialogViewModel.torType.value != TorType.OFF, - ) { - Column( - modifier = Modifier.padding(horizontal = 5.dp), - verticalArrangement = Arrangement.spacedBy(Size10dp), - ) { - SettingsRow( - R.string.tor_preset, - R.string.tor_preset_explainer, - persistentListOf( - TitleExplainer(stringRes(TorPresetType.ONLY_WHEN_NEEDED.resourceId), stringRes(TorPresetType.ONLY_WHEN_NEEDED.explainerId)), - TitleExplainer(stringRes(TorPresetType.DEFAULT.resourceId), stringRes(TorPresetType.DEFAULT.explainerId)), - TitleExplainer(stringRes(TorPresetType.SMALL_PAYLOADS.resourceId), stringRes(TorPresetType.SMALL_PAYLOADS.explainerId)), - TitleExplainer(stringRes(TorPresetType.FULL_PRIVACY.resourceId), stringRes(TorPresetType.FULL_PRIVACY.explainerId)), - TitleExplainer(stringRes(TorPresetType.CUSTOM.resourceId), stringRes(TorPresetType.CUSTOM.explainerId)), - ), - dialogViewModel.preset.value.screenCode, - ) { - dialogViewModel.setPreset(parseTorPresetType(it)) - } + SwitchSettingsRow( + R.string.tor_use_dm_relays, + R.string.tor_use_dm_relays_explainer, + dialogViewModel.dmRelaysViaTor, + ) - SwitchSettingsRow( - R.string.tor_use_onion_address, - R.string.tor_use_onion_address_explainer, - dialogViewModel.onionRelaysViaTor, - ) + SwitchSettingsRow( + R.string.tor_use_new_relays, + R.string.tor_use_new_relays_explainer, + dialogViewModel.newRelaysViaTor, + ) - SwitchSettingsRow( - R.string.tor_use_dm_relays, - R.string.tor_use_dm_relays_explainer, - dialogViewModel.dmRelaysViaTor, - ) + SwitchSettingsRow( + R.string.tor_use_trusted_relays, + R.string.tor_use_trusted_relays_explainer, + dialogViewModel.trustedRelaysViaTor, + ) - SwitchSettingsRow( - R.string.tor_use_new_relays, - R.string.tor_use_new_relays_explainer, - dialogViewModel.newRelaysViaTor, - ) + SwitchSettingsRow( + R.string.tor_use_money_operations, + R.string.tor_use_money_operations_explainer, + dialogViewModel.moneyOperationsViaTor, + ) - SwitchSettingsRow( - R.string.tor_use_trusted_relays, - R.string.tor_use_trusted_relays_explainer, - dialogViewModel.trustedRelaysViaTor, - ) + /** + * Too hard to separate Coil into regular images and profile pics + SwitchSettingsRow( + R.string.tor_use_profile_pictures, + R.string.tor_use_profile_pictures_explainer, + dialogViewModel.profilePicsViaTor, + ) + */ - SwitchSettingsRow( - R.string.tor_use_money_operations, - R.string.tor_use_money_operations_explainer, - dialogViewModel.moneyOperationsViaTor, - ) + SwitchSettingsRow( + R.string.tor_use_nip05_verification, + R.string.tor_use_nip05_verification_explainer, + dialogViewModel.nip05VerificationsViaTor, + ) - /** - * Too hard to separate Coil into regular images and profile pics - SwitchSettingsRow( - R.string.tor_use_profile_pictures, - R.string.tor_use_profile_pictures_explainer, - dialogViewModel.profilePicsViaTor, - ) - */ + SwitchSettingsRow( + R.string.tor_use_url_previews, + R.string.tor_use_url_previews_explainer, + dialogViewModel.urlPreviewsViaTor, + ) - SwitchSettingsRow( - R.string.tor_use_nip05_verification, - R.string.tor_use_nip05_verification_explainer, - dialogViewModel.nip05VerificationsViaTor, - ) + SwitchSettingsRow( + R.string.tor_use_images, + R.string.tor_use_images_explainer, + dialogViewModel.imagesViaTor, + ) - SwitchSettingsRow( - R.string.tor_use_url_previews, - R.string.tor_use_url_previews_explainer, - dialogViewModel.urlPreviewsViaTor, - ) + SwitchSettingsRow( + R.string.tor_use_videos, + R.string.tor_use_videos_explainer, + dialogViewModel.videosViaTor, + ) - SwitchSettingsRow( - R.string.tor_use_images, - R.string.tor_use_images_explainer, - dialogViewModel.imagesViaTor, - ) - - SwitchSettingsRow( - R.string.tor_use_videos, - R.string.tor_use_videos_explainer, - dialogViewModel.videosViaTor, - ) - - SwitchSettingsRow( - R.string.tor_use_nip96_uploads, - R.string.tor_use_nip96_uploads_explainer, - dialogViewModel.nip96UploadsViaTor, - ) - } + SwitchSettingsRow( + R.string.tor_use_nip96_uploads, + R.string.tor_use_nip96_uploads_explainer, + dialogViewModel.nip96UploadsViaTor, + ) } } }