mirror of
https://github.com/wgtunnel/android.git
synced 2026-06-02 00:29:08 +02:00
refactor: adjust action ordering, make config view selectable
Other minor UI improvements. closes #1242
This commit is contained in:
+3
-1
@@ -48,7 +48,9 @@ fun <T> DropdownSelector(
|
||||
DropdownMenu(
|
||||
modifier = modifier.heightIn(max = 250.dp),
|
||||
scrollState = rememberScrollState(),
|
||||
containerColor = MaterialTheme.colorScheme.surface,
|
||||
containerColor = MaterialTheme.colorScheme.surface.copy(alpha = 0.8f),
|
||||
tonalElevation = 4.dp,
|
||||
shadowElevation = 4.dp,
|
||||
expanded = isExpanded,
|
||||
onDismissRequest = onDismiss,
|
||||
) {
|
||||
|
||||
+121
-63
@@ -4,8 +4,12 @@ import android.os.Build
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.rounded.Sort
|
||||
import androidx.compose.material.icons.filled.QrCode
|
||||
import androidx.compose.material.icons.outlined.ContentCopy
|
||||
import androidx.compose.material.icons.outlined.ContentPasteGo
|
||||
import androidx.compose.material.icons.outlined.CopyAll
|
||||
import androidx.compose.material.icons.outlined.Edit
|
||||
import androidx.compose.material.icons.outlined.MoreVert
|
||||
import androidx.compose.material.icons.outlined.RemoveRedEye
|
||||
import androidx.compose.material.icons.rounded.Add
|
||||
import androidx.compose.material.icons.rounded.Delete
|
||||
@@ -13,19 +17,26 @@ import androidx.compose.material.icons.rounded.Download
|
||||
import androidx.compose.material.icons.rounded.Edit
|
||||
import androidx.compose.material.icons.rounded.Menu
|
||||
import androidx.compose.material.icons.rounded.NetworkCheck
|
||||
import androidx.compose.material.icons.rounded.QrCode2
|
||||
import androidx.compose.material.icons.rounded.Save
|
||||
import androidx.compose.material.icons.rounded.SelectAll
|
||||
import androidx.compose.material.icons.rounded.SortByAlpha
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.ui.navigation.NavController
|
||||
import com.zaneschepke.wireguardautotunnel.ui.navigation.Route
|
||||
@@ -79,13 +90,14 @@ fun currentRouteAsNavbarState(
|
||||
return remember(route, globalState) {
|
||||
derivedStateOf {
|
||||
when (route) {
|
||||
Appearance ->
|
||||
Appearance -> {
|
||||
NavbarState(
|
||||
topLeading = { TvBackButton { navController.pop() } },
|
||||
showBottomItems = true,
|
||||
topTitle = context.getString(R.string.appearance),
|
||||
)
|
||||
AutoTunnel ->
|
||||
}
|
||||
AutoTunnel -> {
|
||||
NavbarState(
|
||||
showBottomItems = true,
|
||||
topTitle =
|
||||
@@ -94,13 +106,15 @@ fun currentRouteAsNavbarState(
|
||||
context.getString(R.string.auto_tunnel)
|
||||
},
|
||||
)
|
||||
Display ->
|
||||
}
|
||||
Display -> {
|
||||
NavbarState(
|
||||
topLeading = { TvBackButton { navController.pop() } },
|
||||
showBottomItems = true,
|
||||
topTitle = context.getString(R.string.display_theme),
|
||||
)
|
||||
Dns ->
|
||||
}
|
||||
Dns -> {
|
||||
NavbarState(
|
||||
topLeading = { TvBackButton { navController.pop() } },
|
||||
showBottomItems = true,
|
||||
@@ -116,13 +130,15 @@ fun currentRouteAsNavbarState(
|
||||
}
|
||||
},
|
||||
)
|
||||
Language ->
|
||||
}
|
||||
Language -> {
|
||||
NavbarState(
|
||||
topLeading = { TvBackButton { navController.pop() } },
|
||||
showBottomItems = true,
|
||||
topTitle = context.getString(R.string.language),
|
||||
)
|
||||
LockdownSettings ->
|
||||
}
|
||||
LockdownSettings -> {
|
||||
NavbarState(
|
||||
topLeading = { TvBackButton { navController.pop() } },
|
||||
showBottomItems = true,
|
||||
@@ -138,15 +154,21 @@ fun currentRouteAsNavbarState(
|
||||
}
|
||||
},
|
||||
)
|
||||
License ->
|
||||
}
|
||||
License -> {
|
||||
NavbarState(
|
||||
topLeading = { TvBackButton { navController.pop() } },
|
||||
showBottomItems = true,
|
||||
topTitle = context.getString(R.string.licenses),
|
||||
)
|
||||
LocationDisclosure -> NavbarState(showBottomItems = true)
|
||||
Lock -> NavbarState(showBottomItems = false)
|
||||
Logs ->
|
||||
}
|
||||
LocationDisclosure -> {
|
||||
NavbarState(showBottomItems = true)
|
||||
}
|
||||
Lock -> {
|
||||
NavbarState(showBottomItems = false)
|
||||
}
|
||||
Logs -> {
|
||||
NavbarState(
|
||||
topLeading = { TvBackButton { navController.pop() } },
|
||||
showBottomItems = false,
|
||||
@@ -163,7 +185,8 @@ fun currentRouteAsNavbarState(
|
||||
}
|
||||
},
|
||||
)
|
||||
ProxySettings ->
|
||||
}
|
||||
ProxySettings -> {
|
||||
NavbarState(
|
||||
topLeading = { TvBackButton { navController.pop() } },
|
||||
showBottomItems = true,
|
||||
@@ -179,18 +202,27 @@ fun currentRouteAsNavbarState(
|
||||
}
|
||||
},
|
||||
)
|
||||
Settings ->
|
||||
}
|
||||
Settings -> {
|
||||
NavbarState(
|
||||
showBottomItems = true,
|
||||
topTitle = context.getString(R.string.settings),
|
||||
)
|
||||
Sort ->
|
||||
}
|
||||
Sort -> {
|
||||
NavbarState(
|
||||
topLeading = { TvBackButton { navController.pop() } },
|
||||
showBottomItems = true,
|
||||
topTitle = context.getString(R.string.sort),
|
||||
topTrailing = {
|
||||
Row {
|
||||
IconButton(
|
||||
onClick = {
|
||||
sharedViewModel.postSideEffect(LocalSideEffect.SaveChanges)
|
||||
}
|
||||
) {
|
||||
Icon(Icons.Rounded.Save, stringResource(R.string.save))
|
||||
}
|
||||
IconButton(
|
||||
onClick = {
|
||||
sharedViewModel.postSideEffect(
|
||||
@@ -210,16 +242,10 @@ fun currentRouteAsNavbarState(
|
||||
) {
|
||||
Icon(Icons.Rounded.SortByAlpha, stringResource(R.string.sort))
|
||||
}
|
||||
IconButton(
|
||||
onClick = {
|
||||
sharedViewModel.postSideEffect(LocalSideEffect.SaveChanges)
|
||||
}
|
||||
) {
|
||||
Icon(Icons.Rounded.Save, stringResource(R.string.save))
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
is ConfigEdit,
|
||||
is ConfigGlobal -> {
|
||||
val global = route !is ConfigEdit
|
||||
@@ -231,6 +257,14 @@ fun currentRouteAsNavbarState(
|
||||
showBottomItems = true,
|
||||
topTitle = tunnelName ?: context.getString(R.string.new_tunnel),
|
||||
topTrailing = {
|
||||
IconButton(
|
||||
onClick = {
|
||||
keyboardController?.hide()
|
||||
sharedViewModel.postSideEffect(LocalSideEffect.SaveChanges)
|
||||
}
|
||||
) {
|
||||
Icon(Icons.Rounded.Save, stringResource(R.string.save))
|
||||
}
|
||||
if (!global)
|
||||
IconButton(
|
||||
onClick = {
|
||||
@@ -257,14 +291,6 @@ fun currentRouteAsNavbarState(
|
||||
stringResource(R.string.copy_from),
|
||||
)
|
||||
}
|
||||
IconButton(
|
||||
onClick = {
|
||||
keyboardController?.hide()
|
||||
sharedViewModel.postSideEffect(LocalSideEffect.SaveChanges)
|
||||
}
|
||||
) {
|
||||
Icon(Icons.Rounded.Save, stringResource(R.string.save))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -278,6 +304,13 @@ fun currentRouteAsNavbarState(
|
||||
topTitle = tunnelName ?: "",
|
||||
topTrailing = {
|
||||
Row {
|
||||
IconButton(
|
||||
onClick = {
|
||||
sharedViewModel.postSideEffect(LocalSideEffect.SaveChanges)
|
||||
}
|
||||
) {
|
||||
Icon(Icons.Rounded.Save, stringResource(R.string.save))
|
||||
}
|
||||
IconButton(
|
||||
onClick = {
|
||||
sharedViewModel.postSideEffect(
|
||||
@@ -290,29 +323,24 @@ fun currentRouteAsNavbarState(
|
||||
stringResource(R.string.copy_from),
|
||||
)
|
||||
}
|
||||
IconButton(
|
||||
onClick = {
|
||||
sharedViewModel.postSideEffect(LocalSideEffect.SaveChanges)
|
||||
}
|
||||
) {
|
||||
Icon(Icons.Rounded.Save, stringResource(R.string.save))
|
||||
}
|
||||
}
|
||||
},
|
||||
showBottomItems = true,
|
||||
)
|
||||
}
|
||||
Support ->
|
||||
Support -> {
|
||||
NavbarState(
|
||||
topTitle = context.getString(R.string.support),
|
||||
showBottomItems = true,
|
||||
)
|
||||
AndroidIntegrations ->
|
||||
}
|
||||
AndroidIntegrations -> {
|
||||
NavbarState(
|
||||
topLeading = { TvBackButton { navController.pop() } },
|
||||
topTitle = context.getString(R.string.android_integrations),
|
||||
showBottomItems = true,
|
||||
)
|
||||
}
|
||||
is TunnelSettings -> {
|
||||
val tunnelName = globalState.tunnelNames[route.id]
|
||||
NavbarState(
|
||||
@@ -328,12 +356,6 @@ fun currentRouteAsNavbarState(
|
||||
when (globalState.selectedTunnelCount) {
|
||||
0 ->
|
||||
Row {
|
||||
IconButton(onClick = { navController.push(Sort) }) {
|
||||
Icon(
|
||||
Icons.AutoMirrored.Rounded.Sort,
|
||||
stringResource(R.string.sort),
|
||||
)
|
||||
}
|
||||
IconButton(
|
||||
onClick = {
|
||||
sharedViewModel.postSideEffect(
|
||||
@@ -346,6 +368,12 @@ fun currentRouteAsNavbarState(
|
||||
stringResource(R.string.add_tunnel),
|
||||
)
|
||||
}
|
||||
IconButton(onClick = { navController.push(Sort) }) {
|
||||
Icon(
|
||||
Icons.AutoMirrored.Rounded.Sort,
|
||||
stringResource(R.string.sort),
|
||||
)
|
||||
}
|
||||
}
|
||||
else ->
|
||||
Row {
|
||||
@@ -411,12 +439,13 @@ fun currentRouteAsNavbarState(
|
||||
showBottomItems = true,
|
||||
)
|
||||
}
|
||||
WifiDetectionMethod ->
|
||||
WifiDetectionMethod -> {
|
||||
NavbarState(
|
||||
topLeading = { TvBackButton { navController.pop() } },
|
||||
topTitle = context.getString(R.string.wifi_detection_method),
|
||||
showBottomItems = true,
|
||||
)
|
||||
}
|
||||
Donate -> {
|
||||
NavbarState(
|
||||
topLeading = { TvBackButton { navController.pop() } },
|
||||
@@ -456,12 +485,18 @@ fun currentRouteAsNavbarState(
|
||||
NavbarState(
|
||||
topLeading = { TvBackButton { navController.pop() } },
|
||||
topTrailing = {
|
||||
Row {
|
||||
var showOverflowMenu by remember { mutableStateOf(false) }
|
||||
if (!route.live) {
|
||||
IconButton(onClick = { navController.push(ConfigEdit(route.id)) }) {
|
||||
Icon(
|
||||
Icons.Outlined.Edit,
|
||||
contentDescription = stringResource(R.string.edit_tunnel),
|
||||
)
|
||||
}
|
||||
}
|
||||
IconButton(
|
||||
onClick = {
|
||||
sharedViewModel.postSideEffect(
|
||||
LocalSideEffect.ShowSensitive
|
||||
)
|
||||
sharedViewModel.postSideEffect(LocalSideEffect.ShowSensitive)
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
@@ -469,25 +504,46 @@ fun currentRouteAsNavbarState(
|
||||
stringResource(R.string.toggle_sensitive_data_visibility),
|
||||
)
|
||||
}
|
||||
|
||||
if (!route.live) {
|
||||
IconButton(
|
||||
IconButton(onClick = { showOverflowMenu = true }) {
|
||||
Icon(
|
||||
Icons.Outlined.MoreVert,
|
||||
contentDescription = stringResource(R.string.more_options),
|
||||
)
|
||||
}
|
||||
|
||||
DropdownMenu(
|
||||
expanded = showOverflowMenu,
|
||||
onDismissRequest = { showOverflowMenu = false },
|
||||
containerColor =
|
||||
MaterialTheme.colorScheme.surface.copy(alpha = 0.8f),
|
||||
tonalElevation = 4.dp,
|
||||
shadowElevation = 4.dp,
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(R.string.show_qr)) },
|
||||
leadingIcon = { Icon(Icons.Default.QrCode, null) },
|
||||
onClick = {
|
||||
showOverflowMenu = false
|
||||
sharedViewModel.postSideEffect(LocalSideEffect.Modal.QR)
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
Icons.Rounded.QrCode2,
|
||||
stringResource(R.string.show_qr),
|
||||
},
|
||||
)
|
||||
}
|
||||
IconButton(
|
||||
onClick = { navController.push(ConfigEdit(route.id)) }
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(R.string.copy)) },
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
Icons.Rounded.Edit,
|
||||
stringResource(R.string.edit_tunnel),
|
||||
Icons.Outlined.ContentCopy,
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
showOverflowMenu = false
|
||||
sharedViewModel.postSideEffect(
|
||||
LocalSideEffect.CopyToClipboard
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -523,7 +579,9 @@ fun currentRouteAsNavbarState(
|
||||
showBottomItems = true,
|
||||
)
|
||||
}
|
||||
null -> NavbarState()
|
||||
null -> {
|
||||
NavbarState()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
-2
@@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
@@ -21,10 +22,12 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.functions.rememberClipboardHelper
|
||||
import com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.settings.config.components.QrCodeDialog
|
||||
import com.zaneschepke.wireguardautotunnel.ui.sideeffect.LocalSideEffect
|
||||
import com.zaneschepke.wireguardautotunnel.ui.theme.ConfigHeaderColor
|
||||
@@ -45,6 +48,7 @@ fun ConfigScreen(
|
||||
) {
|
||||
|
||||
val context = LocalContext.current
|
||||
val clipboard = rememberClipboardHelper()
|
||||
val uiState by viewModel.collectAsState()
|
||||
var showKeys by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
@@ -74,6 +78,7 @@ fun ConfigScreen(
|
||||
}
|
||||
}
|
||||
is LocalSideEffect.ShowSensitive -> showKeys = !showKeys
|
||||
is LocalSideEffect.CopyToClipboard -> clipboard.copy(rawConfig)
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
@@ -89,18 +94,19 @@ fun ConfigScreen(
|
||||
) {
|
||||
val displayText by
|
||||
remember(rawConfig, showKeys) { derivedStateOf { maskSensitive(rawConfig, showKeys) } }
|
||||
|
||||
val annotated by
|
||||
remember(displayText) { derivedStateOf { buildConfigAnnotatedString(displayText) } }
|
||||
|
||||
SelectionContainer {
|
||||
Text(
|
||||
text = annotated,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
style = MaterialTheme.typography.bodySmall.copy(fontFamily = FontFamily.Monospace),
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun buildConfigAnnotatedString(text: String): AnnotatedString {
|
||||
val headerRegex = "\\[(Interface|Peer)]".toRegex()
|
||||
|
||||
+3
-4
@@ -1,6 +1,5 @@
|
||||
package com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.settings.config.edit.components
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.MoreVert
|
||||
@@ -11,8 +10,6 @@ import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
@@ -38,7 +35,9 @@ fun InterfaceDropdown(
|
||||
DropdownMenu(
|
||||
expanded = uiState.ui.isInterfaceDropdownExpanded,
|
||||
onDismissRequest = { onToggleDropdown(false) },
|
||||
modifier = Modifier.shadow(12.dp).background(MaterialTheme.colorScheme.surface),
|
||||
containerColor = MaterialTheme.colorScheme.surface.copy(alpha = 0.8f),
|
||||
tonalElevation = 4.dp,
|
||||
shadowElevation = 4.dp,
|
||||
) {
|
||||
if (!uiState.isGlobalConfig)
|
||||
DropdownMenuItem(
|
||||
|
||||
+3
-4
@@ -1,6 +1,5 @@
|
||||
package com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.settings.config.edit.components
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
@@ -18,7 +17,6 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
@@ -66,8 +64,9 @@ fun PeersSection(
|
||||
onDismissRequest = {
|
||||
onPeerDropdownExpanded(!uiState.ui.isPeerDropdownExpanded)
|
||||
},
|
||||
modifier =
|
||||
Modifier.shadow(12.dp).background(MaterialTheme.colorScheme.surface),
|
||||
containerColor = MaterialTheme.colorScheme.surface.copy(alpha = 0.8f),
|
||||
tonalElevation = 4.dp,
|
||||
shadowElevation = 4.dp,
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
text = {
|
||||
|
||||
+6
@@ -2,6 +2,9 @@ package com.zaneschepke.wireguardautotunnel.ui.screens.tunnels.splittunnel.compo
|
||||
|
||||
import androidx.compose.foundation.gestures.ScrollableDefaults
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
@@ -15,6 +18,7 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.button.SurfaceRow
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.dialog.InfoDialog
|
||||
@@ -34,6 +38,7 @@ fun SelectTunnelModal(
|
||||
InfoDialog(
|
||||
title = stringResource(R.string.copy_from),
|
||||
body = {
|
||||
Box(modifier = Modifier.fillMaxWidth().heightIn(max = 480.dp)) {
|
||||
LazyColumn(
|
||||
horizontalAlignment = Alignment.Start,
|
||||
verticalArrangement = Arrangement.Top,
|
||||
@@ -59,6 +64,7 @@ fun SelectTunnelModal(
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onAttest = { onAttest(selectedTunnelId) },
|
||||
onDismiss = onDismiss,
|
||||
|
||||
@@ -16,6 +16,8 @@ sealed class LocalSideEffect {
|
||||
|
||||
data object ShowSensitive : LocalSideEffect()
|
||||
|
||||
data object CopyToClipboard : LocalSideEffect()
|
||||
|
||||
sealed class Sheet : LocalSideEffect() {
|
||||
|
||||
data object ImportTunnels : Sheet()
|
||||
|
||||
@@ -566,4 +566,5 @@
|
||||
<string name="balance_saver">Battery Saver (10s)</string>
|
||||
<string name="tunnel_scripting">Pre/Post script support</string>
|
||||
<string name="name_error_empty">Tunnel name cannot be empty</string>
|
||||
<string name="more_options">More options</string>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user