Signed-off-by: Percy Wegmann <percy@tailscale.com>
ox/move_admin_account_info
Percy Wegmann 2 years ago
parent 3d422fafd4
commit cfc3f4bb59
No known key found for this signature in database
GPG Key ID: 29D8CDEB4C13D48B

@ -6,7 +6,6 @@ package com.tailscale.ipn.ui.view
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.ClickableText
import androidx.compose.material3.ListItem import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
@ -15,17 +14,10 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.withStyle
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.tailscale.ipn.BuildConfig import com.tailscale.ipn.BuildConfig
import com.tailscale.ipn.R import com.tailscale.ipn.R
import com.tailscale.ipn.ui.Links
import com.tailscale.ipn.ui.theme.link
import com.tailscale.ipn.ui.theme.listItem import com.tailscale.ipn.ui.theme.listItem
import com.tailscale.ipn.ui.util.Lists import com.tailscale.ipn.ui.util.Lists
import com.tailscale.ipn.ui.viewModel.SettingsNav import com.tailscale.ipn.ui.viewModel.SettingsNav
@ -33,9 +25,7 @@ import com.tailscale.ipn.ui.viewModel.SettingsViewModel
@Composable @Composable
fun SettingsView(settingsNav: SettingsNav, viewModel: SettingsViewModel = viewModel()) { fun SettingsView(settingsNav: SettingsNav, viewModel: SettingsViewModel = viewModel()) {
val handler = LocalUriHandler.current
val user = viewModel.loggedInUser.collectAsState().value val user = viewModel.loggedInUser.collectAsState().value
val isAdmin = viewModel.isAdmin.collectAsState().value
val managedByOrganization = viewModel.managedByOrganization.collectAsState().value val managedByOrganization = viewModel.managedByOrganization.collectAsState().value
Scaffold( Scaffold(
@ -48,10 +38,6 @@ fun SettingsView(settingsNav: SettingsNav, viewModel: SettingsViewModel = viewMo
actionState = UserActionState.NAV, actionState = UserActionState.NAV,
onClick = settingsNav.onNavigateToUserSwitcher) onClick = settingsNav.onNavigateToUserSwitcher)
if (isAdmin) {
AdminTextView { handler.openUri(Links.ADMIN_URL) }
}
Lists.SectionDivider() Lists.SectionDivider()
Setting.Text(R.string.dns_settings, onClick = settingsNav.onNavigateToDNSSettings) Setting.Text(R.string.dns_settings, onClick = settingsNav.onNavigateToDNSSettings)
@ -129,30 +115,3 @@ object Setting {
}) })
} }
} }
@Composable
fun AdminTextView(onNavigateToAdminConsole: () -> Unit) {
val adminStr = buildAnnotatedString {
withStyle(style = SpanStyle(color = MaterialTheme.colorScheme.onSurfaceVariant)) {
append(stringResource(id = R.string.settings_admin_prefix))
}
pushStringAnnotation(tag = "link", annotation = Links.ADMIN_URL)
withStyle(
style =
SpanStyle(
color = MaterialTheme.colorScheme.link,
textDecoration = TextDecoration.Underline)) {
append(stringResource(id = R.string.settings_admin_link))
}
pop()
}
ListItem(
headlineContent = {
ClickableText(
text = adminStr,
style = MaterialTheme.typography.bodyMedium,
onClick = { onNavigateToAdminConsole() })
})
}

@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.text.ClickableText
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenu
@ -18,6 +19,7 @@ import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
@ -31,10 +33,17 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.tailscale.ipn.R import com.tailscale.ipn.R
import com.tailscale.ipn.ui.Links
import com.tailscale.ipn.ui.theme.link
import com.tailscale.ipn.ui.util.Lists import com.tailscale.ipn.ui.util.Lists
import com.tailscale.ipn.ui.util.itemsWithDividers import com.tailscale.ipn.ui.util.itemsWithDividers
import com.tailscale.ipn.ui.util.set import com.tailscale.ipn.ui.util.set
@ -47,10 +56,11 @@ fun UserSwitcherView(
onNavigateHome: () -> Unit, onNavigateHome: () -> Unit,
viewModel: UserSwitcherViewModel = viewModel() viewModel: UserSwitcherViewModel = viewModel()
) { ) {
val handler = LocalUriHandler.current
val users = viewModel.loginProfiles.collectAsState().value val users = viewModel.loginProfiles.collectAsState().value
val currentUser = viewModel.loggedInUser.collectAsState().value val currentUser = viewModel.loggedInUser.collectAsState().value
val showHeaderMenu = viewModel.showHeaderMenu.collectAsState().value val showHeaderMenu = viewModel.showHeaderMenu.collectAsState().value
val isAdmin = viewModel.isAdmin.collectAsState().value
Scaffold( Scaffold(
topBar = { topBar = {
@ -119,7 +129,12 @@ fun UserSwitcherView(
} }
} }
Lists.ItemDivider() // if (isAdmin) {
// Lists.ItemDivider()
// AdminTextView { handler.openUri(Links.ADMIN_URL) }
// }
Lists.SectionDivider()
Setting.Text(R.string.reauthenticate) { viewModel.login {} } Setting.Text(R.string.reauthenticate) { viewModel.login {} }
if (currentUser != null) { if (currentUser != null) {
@ -189,3 +204,30 @@ fun FusMenu(viewModel: UserSwitcherViewModel) {
}) })
} }
} }
@Composable
fun AdminTextView(onNavigateToAdminConsole: () -> Unit) {
val adminStr = buildAnnotatedString {
withStyle(style = SpanStyle(color = MaterialTheme.colorScheme.onSurfaceVariant)) {
append(stringResource(id = R.string.settings_admin_prefix))
}
pushStringAnnotation(tag = "link", annotation = Links.ADMIN_URL)
withStyle(
style =
SpanStyle(
color = MaterialTheme.colorScheme.link,
textDecoration = TextDecoration.Underline)) {
append(stringResource(id = R.string.settings_admin_link))
}
pop()
}
ListItem(
headlineContent = {
ClickableText(
text = adminStr,
style = MaterialTheme.typography.bodyMedium,
onClick = { onNavigateToAdminConsole() })
})
}

@ -3,13 +3,7 @@
package com.tailscale.ipn.ui.viewModel package com.tailscale.ipn.ui.viewModel
import androidx.lifecycle.viewModelScope
import com.tailscale.ipn.mdm.MDMSettings import com.tailscale.ipn.mdm.MDMSettings
import com.tailscale.ipn.ui.notifier.Notifier
import com.tailscale.ipn.ui.util.set
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
data class SettingsNav( data class SettingsNav(
val onNavigateToBugReport: () -> Unit, val onNavigateToBugReport: () -> Unit,
@ -25,12 +19,5 @@ data class SettingsNav(
class SettingsViewModel() : IpnViewModel() { class SettingsViewModel() : IpnViewModel() {
// Display name for the logged in user // Display name for the logged in user
val isAdmin: StateFlow<Boolean> = MutableStateFlow(false)
val managedByOrganization = MDMSettings.managedByOrganizationName.flow val managedByOrganization = MDMSettings.managedByOrganizationName.flow
init {
viewModelScope.launch {
Notifier.netmap.collect { netmap -> isAdmin.set(netmap?.SelfNode?.isAdmin ?: false) }
}
}
} }

@ -11,6 +11,7 @@ import com.tailscale.ipn.ui.util.set
import com.tailscale.ipn.ui.view.ErrorDialogType import com.tailscale.ipn.ui.view.ErrorDialogType
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
class UserSwitcherViewModel : IpnViewModel() { class UserSwitcherViewModel : IpnViewModel() {
@ -20,7 +21,15 @@ class UserSwitcherViewModel : IpnViewModel() {
// True if we should render the kebab menu // True if we should render the kebab menu
val showHeaderMenu: StateFlow<Boolean> = MutableStateFlow(false) val showHeaderMenu: StateFlow<Boolean> = MutableStateFlow(false)
// Sets the custom control URL and immediatly invokes the login flow val isAdmin: StateFlow<Boolean> = MutableStateFlow(false)
init {
viewModelScope.launch {
Notifier.netmap.collect { netmap -> isAdmin.set(netmap?.SelfNode?.isAdmin ?: false) }
}
}
// Sets the custom control URL and immediately invokes the login flow
fun setControlURL(urlStr: String) { fun setControlURL(urlStr: String) {
// Some basic checks that the entered URL is "reasonable". The underlying // Some basic checks that the entered URL is "reasonable". The underlying
// localAPIClient will use the default server if we give it a broken URL, // localAPIClient will use the default server if we give it a broken URL,

@ -98,7 +98,7 @@
<string name="logout_failed">Unable to logout at this time. Please try again.</string> <string name="logout_failed">Unable to logout at this time. Please try again.</string>
<string name="error">Error</string> <string name="error">Error</string>
<string name="accounts">Accounts</string> <string name="accounts">Accounts</string>
<string name="add_account">Add another account</string> <string name="add_account">Add another account</string>
<string name="add_account_short">Add account</string> <string name="add_account_short">Add account</string>
<string name="reauthenticate">Reauthenticate</string> <string name="reauthenticate">Reauthenticate</string>
<string name="switch_user_failed">Unable to switch users. Please try again.</string> <string name="switch_user_failed">Unable to switch users. Please try again.</string>

Loading…
Cancel
Save