Moves privacy option from dialog to route

This commit is contained in:
Vitor Pamplona
2025-04-25 12:14:44 -04:00
parent 390e0615d1
commit aef7d91a6b
5 changed files with 247 additions and 140 deletions
@@ -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<Route.Search> { SearchScreen(accountViewModel, nav) }
composableFromEnd<Route.SecurityFilters> { SecurityFiltersScreen(accountViewModel, nav) }
composableFromEnd<Route.PrivacyOptions> { PrivacyOptionsScreen(accountViewModel, nav) }
composableFromEnd<Route.Bookmarks> { BookmarkListScreen(accountViewModel, nav) }
composableFromEnd<Route.Drafts> { DraftListScreen(accountViewModel, nav) }
composableFromEnd<Route.Settings> { SettingsScreen(sharedPreferencesViewModel, accountViewModel, nav) }
@@ -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
@@ -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<Route.Search>() -> entry.toRoute<Route.Search>()
dest.hasRoute<Route.SecurityFilters>() -> entry.toRoute<Route.SecurityFilters>()
dest.hasRoute<Route.PrivacyOptions>() -> entry.toRoute<Route.PrivacyOptions>()
dest.hasRoute<Route.Bookmarks>() -> entry.toRoute<Route.Bookmarks>()
dest.hasRoute<Route.ContentDiscovery>() -> entry.toRoute<Route.ContentDiscovery>()
dest.hasRoute<Route.Drafts>() -> entry.toRoute<Route.Drafts>()
@@ -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<TorDialogViewModel>()
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)
}
}
}
@@ -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,
)
}
}
}