Merge branch 'main' into safresume

Signed-off-by: kari-ts <135075563+kari-ts@users.noreply.github.com>
pull/675/head
kari-ts 4 months ago committed by GitHub
commit 7953292aeb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -15,7 +15,8 @@ class Netmap {
var Domain: String,
var UserProfiles: Map<String, Tailcfg.UserProfile>,
var TKAEnabled: Boolean,
var DNS: Tailcfg.DNSConfig? = null
var DNS: Tailcfg.DNSConfig? = null,
var AllCaps: List<String> = emptyList()
) {
// Keys are tailcfg.UserIDs thet get stringified
// Helpers
@ -51,5 +52,9 @@ class Netmap {
UserProfiles == other.UserProfiles &&
TKAEnabled == other.TKAEnabled
}
fun hasCap(capability: String): Boolean {
return AllCaps.contains(capability)
}
}
}

@ -12,6 +12,7 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.ClickableText
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material3.AlertDialog
@ -33,6 +34,9 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
@ -52,10 +56,14 @@ data class UserSwitcherNav(
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun UserSwitcherView(nav: UserSwitcherNav, viewModel: UserSwitcherViewModel = viewModel()) {
val users by viewModel.loginProfiles.collectAsState()
val currentUser by viewModel.loggedInUser.collectAsState()
val showHeaderMenu by viewModel.showHeaderMenu.collectAsState()
var showDeleteDialog by remember { mutableStateOf(false) }
val context = LocalContext.current
val netmapState by viewModel.netmap.collectAsState()
val capabilityIsOwner = "https://tailscale.com/cap/is-owner"
val isOwner = netmapState?.hasCap(capabilityIsOwner) == true
Scaffold(
topBar = {
@ -145,10 +153,46 @@ fun UserSwitcherView(nav: UserSwitcherNav, viewModel: UserSwitcherViewModel = vi
})
}
Lists.SectionDivider()
Setting.Text(R.string.delete_tailnet, destructive = true) {
showDeleteDialog = true
}
}
}
}
}
if (showDeleteDialog) {
AlertDialog(
onDismissRequest = { showDeleteDialog = false },
title = { Text(text = stringResource(R.string.delete_tailnet)) },
text = {
if (isOwner) {
OwnerDeleteDialogText {
val uri = Uri.parse("https://login.tailscale.com/admin/settings/general")
context.startActivity(Intent(Intent.ACTION_VIEW, uri))
}
} else {
Text(stringResource(R.string.request_deletion_nonowner))
}
},
confirmButton = {
TextButton(
onClick = {
val intent =
Intent(Intent.ACTION_VIEW, Uri.parse("https://tailscale.com/contact/support"))
context.startActivity(intent)
showDeleteDialog = false
}) {
Text(text = stringResource(R.string.contact_support))
}
},
dismissButton = {
TextButton(onClick = { showDeleteDialog = false }) {
Text(text = stringResource(R.string.cancel))
}
})
}
}
@Composable
@ -178,6 +222,41 @@ fun FusMenu(
}
}
@Composable
fun OwnerDeleteDialogText(onSettingsClick: () -> Unit) {
val part1 = stringResource(R.string.request_deletion_owner_part1)
val part2a = stringResource(R.string.request_deletion_owner_part2a)
val part2b = stringResource(R.string.request_deletion_owner_part2b)
val annotatedText = buildAnnotatedString {
append(part1 + " ")
pushStringAnnotation(
tag = "settings", annotation = "https://login.tailscale.com/admin/settings/general")
withStyle(style = SpanStyle(color = MaterialTheme.colorScheme.primary)) {
append("Settings > General")
}
pop()
append(" $part2a\n\n") // newline after "Delete tailnet."
append(part2b)
}
val context = LocalContext.current
ClickableText(
text = annotatedText,
style = MaterialTheme.typography.bodyMedium.copy(color = MaterialTheme.colorScheme.onSurface),
onClick = { offset ->
annotatedText
.getStringAnnotations(tag = "settings", start = offset, end = offset)
.firstOrNull()
?.let { annotation ->
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(annotation.item))
context.startActivity(intent)
}
})
}
@Composable
fun MenuItem(text: String, onClick: () -> Unit) {
DropdownMenuItem(

@ -23,6 +23,7 @@ import com.tailscale.ipn.ui.model.Ipn
import com.tailscale.ipn.ui.model.Ipn.State
import com.tailscale.ipn.ui.model.Tailcfg
import com.tailscale.ipn.ui.notifier.Notifier
import com.tailscale.ipn.ui.util.AndroidTVUtil
import com.tailscale.ipn.ui.util.PeerCategorizer
import com.tailscale.ipn.ui.util.PeerSet
import com.tailscale.ipn.ui.util.TimeUtil
@ -226,7 +227,7 @@ class MainViewModel(private val vpnViewModel: VpnViewModel) : IpnViewModel() {
}
fun checkIfTaildropDirectorySelected() {
if (skipPromptsForAuthKeyLogin()) {
if (skipPromptsForAuthKeyLogin() || AndroidTVUtil.isAndroidTV()) {
return
}

@ -13,7 +13,7 @@
<string name="not_connected">Not connected</string>
<string name="empty" translatable="false"> </string>
<string name="template" translatable="false">%s</string>
<string name="selected">Selected</string>
<string name="selected">Selected</string>
<string name="offline">Offline</string>
<string name="ok">OK</string>
<string name="_continue">Continue</string>
@ -136,7 +136,20 @@
<string name="invalidAuthKeyTitle">Invalid key</string>
<string name="custom_control_url_title">Custom control server URL</string>
<string name="auth_key_input_title">Auth key</string>
<string name="delete_tailnet">Delete tailnet</string>
<string name="contact_support">Contact support</string>
<string name="request_deletion_nonowner">All requests related to the removal or deletion of data are handled by our Support team. To open a request, tap the Contact Support button below to be taken to our contact form in the browser. Complete the form, and a Customer Support Engineer will work with you directly to assist.</string>
<string name="request_deletion_owner_part1">
As the owner of this tailnet, to remove yourself from the tailnet you can either reassign ownership and contact our Support team, or delete the whole tailnet through the admin console. To do the latter, go to
</string>
<string name="request_deletion_owner_part2a">
and look for “Delete tailnet”.
</string>
<string name="request_deletion_owner_part2b">
All requests related to the removal or deletion of data are handled by our Support team. To open a request, tap the Contact Support button below to be taken to our contact form in the browser. Complete the form, and a Customer Support Engineer will work with you directly to assist.
</string>
<!-- Strings for ExitNode picker -->
<string name="choose_exit_node">Choose exit node</string>
<string name="choose_mullvad_exit_node">Mullvad exit nodes</string>

@ -236,4 +236,4 @@ howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k=
software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=
tailscale.com v1.87.0-pre.0.20250801224156-0f15e4419683 h1:meEUX1Nsr5SaXiaeivOGG4c7gsQm/P3Jr3dzbtE0j6k=
tailscale.com v1.87.0-pre.0.20250801224156-0f15e4419683/go.mod h1:Lm8dnzU2i/Emw15r6sl3FRNp/liSQ/nYw6ZSQvIdZ1M=
tailscale.com v1.87.0-pre.0.20250801224156-0f15e4419683/go.mod h1:Lm8dnzU2i/Emw15r6sl3FRNp/liSQ/nYw6ZSQvIdZ1M=
Loading…
Cancel
Save