android/ui: use compose getValue syntactic sugar consistently (#367)

Updates #cleanup

"by stateFlow" is syntactic sugar for" = stateFlow.value" and is more
idiomatic.  There should be no functional difference here.  Just\
changed where it can be for consistency.

Signed-off-by: Jonathan Nobels <jonathan@tailscale.com>
pull/407/head
Jonathan Nobels 3 weeks ago committed by GitHub
parent 999c6f2357
commit b37492a547
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -12,6 +12,7 @@ import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.produceState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -40,7 +41,7 @@ object LoadingIndicator {
contentAlignment = Alignment.Center,
) {
content()
val isLoading = loading.collectAsState().value
val isLoading by loading.collectAsState()
if (isLoading) {
Box(Modifier.clickable {}.matchParentSize().background(Color.Gray.copy(alpha = 0.0f)))
@ -54,7 +55,8 @@ object LoadingIndicator {
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally) {
TailscaleLogoView(true, usesOnBackgroundColors = false, Modifier.size(72.dp).alpha(0.4f))
TailscaleLogoView(
true, usesOnBackgroundColors = false, Modifier.size(72.dp).alpha(0.4f))
}
}
}

@ -14,6 +14,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.stringResource
@ -36,7 +37,7 @@ import com.tailscale.ipn.ui.viewModel.BugReportViewModel
@Composable
fun BugReportView(backToSettings: BackNavigation, model: BugReportViewModel = viewModel()) {
val handler = LocalUriHandler.current
val bugReportID = model.bugReportID.collectAsState().value
val bugReportID by model.bugReportID.collectAsState()
Scaffold(topBar = { Header(R.string.bug_report_title, onBack = backToSettings) }) { innerPadding
->

@ -53,7 +53,7 @@ fun LoginWithCustomControlURLView(
onBack = backToSettings,
)
}) { innerPadding ->
val error = viewModel.errorDialog.collectAsState().value
val error by viewModel.errorDialog.collectAsState()
val strings =
LoginViewStrings(
title = stringResource(id = R.string.custom_control_menu),
@ -85,7 +85,7 @@ fun LoginWithAuthKeyView(
onBack = backToSettings,
)
}) { innerPadding ->
val error = viewModel.errorDialog.collectAsState().value
val error by viewModel.errorDialog.collectAsState()
val strings =
LoginViewStrings(
title = stringResource(id = R.string.auth_key_title),

@ -14,6 +14,7 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
@ -41,7 +42,7 @@ fun DNSSettingsView(
backToSettings: BackNavigation,
model: DNSSettingsViewModel = viewModel(factory = DNSSettingsViewModelFactory())
) {
val state: DNSEnablementState = model.enablementState.collectAsState().value
val state: DNSEnablementState by model.enablementState.collectAsState()
val resolvers = model.dnsConfig.collectAsState().value?.Resolvers ?: emptyList()
val domains = model.dnsConfig.collectAsState().value?.Domains ?: emptyList()
val routes: List<ViewableRoute> =
@ -49,7 +50,7 @@ fun DNSSettingsView(
entry.value?.let { resolvers -> ViewableRoute(name = entry.key, resolvers) } ?: run { null }
} ?: emptyList()
val useCorpDNS = Notifier.prefs.collectAsState().value?.CorpDNS == true
val dnsSettingsMDMDisposition = MDMSettings.useTailscaleDNSSettings.flow.collectAsState().value
val dnsSettingsMDMDisposition by MDMSettings.useTailscaleDNSSettings.flow.collectAsState()
Scaffold(topBar = { Header(R.string.dns_settings, onBack = backToSettings) }) { innerPadding ->
LoadingIndicator.Wrap {

@ -17,6 +17,7 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.viewmodel.compose.viewModel
@ -42,14 +43,13 @@ fun ExitNodePicker(
LoadingIndicator.Wrap {
Scaffold(topBar = { Header(R.string.choose_exit_node, onBack = nav.onNavigateBackHome) }) {
innerPadding ->
val tailnetExitNodes = model.tailnetExitNodes.collectAsState().value
val mullvadExitNodesByCountryCode = model.mullvadExitNodesByCountryCode.collectAsState().value
val mullvadExitNodeCount = model.mullvadExitNodeCount.collectAsState().value
val anyActive = model.anyActive.collectAsState()
val tailnetExitNodes by model.tailnetExitNodes.collectAsState()
val mullvadExitNodesByCountryCode by model.mullvadExitNodesByCountryCode.collectAsState()
val mullvadExitNodeCount by model.mullvadExitNodeCount.collectAsState()
val anyActive by model.anyActive.collectAsState()
val allowLANAccess = Notifier.prefs.collectAsState().value?.ExitNodeAllowLANAccess == true
val showRunAsExitNode = MDMSettings.runExitNode.flow.collectAsState().value
val allowLanAccessMDMDisposition =
MDMSettings.exitNodeAllowLANAccess.flow.collectAsState().value
val showRunAsExitNode by MDMSettings.runExitNode.flow.collectAsState()
val allowLanAccessMDMDisposition by MDMSettings.exitNodeAllowLANAccess.flow.collectAsState()
LazyColumn(modifier = Modifier.padding(innerPadding)) {
item(key = "header") {
@ -58,7 +58,7 @@ fun ExitNodePicker(
ExitNodePickerViewModel.ExitNode(
label = stringResource(R.string.none),
online = true,
selected = !anyActive.value,
selected = !anyActive,
))
if (showRunAsExitNode == ShowHide.Show) {

@ -20,6 +20,7 @@ import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@ -38,7 +39,7 @@ import com.tailscale.ipn.ui.viewModel.LoginQRViewModel
fun LoginQRView(onDismiss: () -> Unit = {}, model: LoginQRViewModel = viewModel()) {
Surface(color = MaterialTheme.colorScheme.scrim, modifier = Modifier.fillMaxSize()) {
Dialog(onDismissRequest = onDismiss) {
val image = model.qrCode.collectAsState()
val image by model.qrCode.collectAsState()
Column(
modifier =
@ -57,7 +58,7 @@ fun LoginQRView(onDismiss: () -> Unit = {}, model: LoginQRViewModel = viewModel(
.background(MaterialTheme.colorScheme.onSurface)
.fillMaxWidth(),
contentAlignment = Alignment.Center) {
image.value?.let {
image?.let {
Image(
bitmap = it,
contentDescription = "Scan to login",

@ -12,6 +12,7 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
@ -38,7 +39,7 @@ fun MDMSettingsDebugView(backToSettings: BackNavigation, model: IpnViewModel = v
@Composable
fun MDMSettingView(setting: MDMSetting<*>) {
val value = setting.flow.collectAsState().value
val value by setting.flow.collectAsState()
ListItem(
headlineContent = { Text(setting.localizedTitle, maxLines = 3) },
supportingContent = {

@ -40,13 +40,13 @@ import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
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.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
@ -62,7 +62,6 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.tailscale.ipn.R
import com.tailscale.ipn.mdm.MDMSettings
@ -89,7 +88,6 @@ import com.tailscale.ipn.ui.util.PeerSet
import com.tailscale.ipn.ui.util.flag
import com.tailscale.ipn.ui.util.itemsWithDividers
import com.tailscale.ipn.ui.viewModel.MainViewModel
import android.util.Log
// Navigation actions for the MainView
data class MainViewNavigation(
@ -105,14 +103,14 @@ fun MainView(
navigation: MainViewNavigation,
viewModel: MainViewModel
) {
LoadingIndicator.Wrap {
Scaffold(contentWindowInsets = WindowInsets.Companion.statusBars) { paddingInsets ->
Column(
modifier = Modifier.fillMaxWidth().padding(paddingInsets),
verticalArrangement = Arrangement.Center) {
// Assume VPN has been prepared. Whether or not it has been prepared cannot be known until permission has been granted to prepare the VPN.
val isPrepared by viewModel.vpnPrepared.collectAsState(initial=true)
// Assume VPN has been prepared. Whether or not it has been prepared cannot be known
// until permission has been granted to prepare the VPN.
val isPrepared by viewModel.vpnPrepared.collectAsState(initial = true)
val isOn by viewModel.vpnToggleState.collectAsState(initial = false)
val state by viewModel.ipnState.collectAsState(initial = Ipn.State.NoState)
val user by viewModel.loggedInUser.collectAsState(initial = null)
@ -195,8 +193,8 @@ fun MainView(
{ viewModel.toggleVpn() },
{ viewModel.login() },
loginAtUrl,
netmap?.SelfNode,
{viewModel.showVPNPermissionLauncherIfUnauthorized()})
netmap?.SelfNode,
{ viewModel.showVPNPermissionLauncherIfUnauthorized() })
}
}
}
@ -206,17 +204,17 @@ fun MainView(
@Composable
fun ExitNodeStatus(navAction: () -> Unit, viewModel: MainViewModel) {
val maybePrefs = viewModel.prefs.collectAsState()
val netmap = viewModel.netmap.collectAsState()
val maybePrefs by viewModel.prefs.collectAsState()
val netmap by viewModel.netmap.collectAsState()
// There's nothing to render if we haven't loaded the prefs yet
val prefs = maybePrefs.value ?: return
val prefs = maybePrefs ?: return
// The activeExitNode is the source of truth. The selectedExitNode is only relevant if we
// don't have an active node.
val chosenExitNodeId = prefs.activeExitNodeID ?: prefs.selectedExitNodeID
val exitNodePeer = chosenExitNodeId?.let { id -> netmap.value?.Peers?.find { it.StableID == id } }
val exitNodePeer = chosenExitNodeId?.let { id -> netmap?.Peers?.find { it.StableID == id } }
val location = exitNodePeer?.Hostinfo?.Location
val name = exitNodePeer?.ComputedName
@ -319,7 +317,7 @@ fun ConnectView(
if (!isPrepared) {
showVPNPermissionLauncherIfUnauthorized()
}
}
}
Row(horizontalArrangement = Arrangement.Center, modifier = Modifier.fillMaxWidth()) {
Column(horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.fillMaxWidth()) {
Column(
@ -327,7 +325,7 @@ fun ConnectView(
verticalArrangement = Arrangement.spacedBy(8.dp, alignment = Alignment.CenterVertically),
horizontalAlignment = Alignment.CenterHorizontally,
) {
if (!isPrepared) {
if (!isPrepared) {
TailscaleLogoView(modifier = Modifier.size(50.dp))
Spacer(modifier = Modifier.size(1.dp))
Text(
@ -426,10 +424,10 @@ fun PeerList(
onNavigateToPeerDetails: (Tailcfg.Node) -> Unit,
onSearch: (String) -> Unit
) {
val peerList = viewModel.peers.collectAsState(initial = emptyList<PeerSet>())
val peerList by viewModel.peers.collectAsState(initial = emptyList<PeerSet>())
val searchTermStr by viewModel.searchTerm.collectAsState(initial = "")
val showNoResults =
remember { derivedStateOf { searchTermStr.isNotEmpty() && peerList.value.isEmpty() } }.value
remember { derivedStateOf { searchTermStr.isNotEmpty() && peerList.isEmpty() } }.value
val netmap = viewModel.netmap.collectAsState()
@ -490,7 +488,7 @@ fun PeerList(
}
var first = true
peerList.value.forEach { peerSet ->
peerList.forEach { peerSet ->
if (!first) {
item(key = "user_divider_${peerSet.user?.ID ?: 0L}") { Lists.ItemDivider() }
}

@ -10,6 +10,7 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.viewmodel.compose.viewModel
@ -29,10 +30,10 @@ fun MullvadExitNodePicker(
nav: ExitNodePickerNav,
model: ExitNodePickerViewModel = viewModel(factory = ExitNodePickerViewModelFactory(nav))
) {
val mullvadExitNodes = model.mullvadExitNodesByCountryCode.collectAsState()
val bestAvailableByCountry = model.mullvadBestAvailableByCountry.collectAsState()
val mullvadExitNodes by model.mullvadExitNodesByCountryCode.collectAsState()
val bestAvailableByCountry by model.mullvadBestAvailableByCountry.collectAsState()
mullvadExitNodes.value[countryCode]?.toList()?.let { nodes ->
mullvadExitNodes[countryCode]?.toList()?.let { nodes ->
val any = nodes.first()
LoadingIndicator.Wrap {
@ -44,7 +45,7 @@ fun MullvadExitNodePicker(
}) { innerPadding ->
LazyColumn(modifier = Modifier.padding(innerPadding)) {
if (nodes.size > 1) {
val bestAvailableNode = bestAvailableByCountry.value[countryCode]!!
val bestAvailableNode = bestAvailableByCountry[countryCode]!!
item {
ExitNodeItem(
model,

@ -17,6 +17,7 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.viewmodel.compose.viewModel
@ -40,11 +41,11 @@ fun MullvadExitNodePickerList(
topBar = {
Header(R.string.choose_mullvad_exit_node, onBack = nav.onNavigateBackToExitNodes)
}) { innerPadding ->
val mullvadExitNodes = model.mullvadExitNodesByCountryCode.collectAsState()
val mullvadExitNodes by model.mullvadExitNodesByCountryCode.collectAsState()
LazyColumn(modifier = Modifier.padding(innerPadding)) {
val sortedCountries =
mullvadExitNodes.value.entries.toList().sortedBy {
mullvadExitNodes.entries.toList().sortedBy {
it.value.first().country.lowercase()
}
itemsWithDividers(sortedCountries) { (countryCode, nodes) ->

@ -21,6 +21,7 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
@ -39,7 +40,7 @@ fun RunExitNodeView(
nav: ExitNodePickerNav,
model: RunExitNodeViewModel = viewModel(factory = RunExitNodeViewModelFactory())
) {
val isRunningExitNode = model.isRunningExitNode.collectAsState().value
val isRunningExitNode by model.isRunningExitNode.collectAsState()
Scaffold(
topBar = { Header(R.string.run_as_exit_node, onBack = nav.onNavigateBackToExitNodes) }) {
@ -49,7 +50,11 @@ fun RunExitNodeView(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement =
Arrangement.spacedBy(24.dp, alignment = Alignment.CenterVertically),
modifier = Modifier.padding(innerPadding).padding(24.dp).fillMaxHeight().verticalScroll(rememberScrollState())) {
modifier =
Modifier.padding(innerPadding)
.padding(24.dp)
.fillMaxHeight()
.verticalScroll(rememberScrollState())) {
RunExitNodeGraphic()
if (isRunningExitNode) {

@ -14,6 +14,7 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalUriHandler
@ -39,14 +40,14 @@ import com.tailscale.ipn.ui.viewModel.SettingsViewModel
@Composable
fun SettingsView(settingsNav: SettingsNav, viewModel: SettingsViewModel = viewModel()) {
val handler = LocalUriHandler.current
val user = viewModel.loggedInUser.collectAsState().value
val isAdmin = viewModel.isAdmin.collectAsState().value
val managedByOrganization = viewModel.managedByOrganization.collectAsState().value
val tailnetLockEnabled = viewModel.tailNetLockEnabled.collectAsState().value
val corpDNSEnabled = viewModel.corpDNSEnabled.collectAsState().value
val isVPNPrepared = viewModel.vpnPrepared.collectAsState().value
val showTailnetLock = MDMSettings.manageTailnetLock.flow.collectAsState().value
val user by viewModel.loggedInUser.collectAsState()
val isAdmin by viewModel.isAdmin.collectAsState()
val managedByOrganization by viewModel.managedByOrganization.collectAsState()
val tailnetLockEnabled by viewModel.tailNetLockEnabled.collectAsState()
val corpDNSEnabled by viewModel.corpDNSEnabled.collectAsState()
val isVPNPrepared by viewModel.vpnPrepared.collectAsState()
val showTailnetLock by MDMSettings.manageTailnetLock.flow.collectAsState()
Scaffold(
topBar = {

@ -19,6 +19,7 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
@ -57,7 +58,7 @@ fun TaildropView(
when (viewModel.state.collectAsState().value) {
Ipn.State.Running -> {
val peers = viewModel.myPeers.collectAsState().value
val peers by viewModel.myPeers.collectAsState()
val context = LocalContext.current
FileSharePeerList(
peers = peers,

@ -14,6 +14,7 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.painterResource
@ -41,9 +42,9 @@ fun TailnetLockSetupView(
backToSettings: BackNavigation,
model: TailnetLockSetupViewModel = viewModel(factory = TailnetLockSetupViewModelFactory())
) {
val statusItems = model.statusItems.collectAsState().value
val nodeKey = model.nodeKey.collectAsState().value
val tailnetLockKey = model.tailnetLockKey.collectAsState().value
val statusItems by model.statusItems.collectAsState()
val nodeKey by model.nodeKey.collectAsState()
val tailnetLockKey by model.tailnetLockKey.collectAsState()
val tailnetLockTlPubKey = tailnetLockKey.replace("nlpub", "tlpub")
Scaffold(topBar = { Header(R.string.tailnet_lock, onBack = backToSettings) }) { innerPadding ->

@ -22,6 +22,7 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
@ -46,9 +47,9 @@ data class UserSwitcherNav(
@Composable
fun UserSwitcherView(nav: UserSwitcherNav, viewModel: UserSwitcherViewModel = viewModel()) {
val users = viewModel.loginProfiles.collectAsState().value
val currentUser = viewModel.loggedInUser.collectAsState().value
val showHeaderMenu = viewModel.showHeaderMenu.collectAsState().value
val users by viewModel.loginProfiles.collectAsState()
val currentUser by viewModel.loggedInUser.collectAsState()
val showHeaderMenu by viewModel.showHeaderMenu.collectAsState()
Scaffold(
topBar = {
@ -70,7 +71,7 @@ fun UserSwitcherView(nav: UserSwitcherNav, viewModel: UserSwitcherViewModel = vi
Column(
modifier = Modifier.padding(innerPadding).fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(8.dp)) {
val showErrorDialog = viewModel.errorDialog.collectAsState().value
val showErrorDialog by viewModel.errorDialog.collectAsState()
// Show the error overlay if need be
showErrorDialog?.let {
@ -149,7 +150,7 @@ fun FusMenu(
onAuthKeyClick: () -> Unit,
viewModel: UserSwitcherViewModel
) {
val expanded = viewModel.showHeaderMenu.collectAsState().value
val expanded by viewModel.showHeaderMenu.collectAsState()
DropdownMenu(
expanded = expanded,
@ -173,7 +174,9 @@ fun FusMenu(
@Composable
fun MenuItem(text: String, onClick: () -> Unit) {
DropdownMenuItem(
modifier = Modifier.padding(horizontal = 8.dp, vertical = 0.dp), onClick = onClick, text = { Text(text = text) })
modifier = Modifier.padding(horizontal = 8.dp, vertical = 0.dp),
onClick = onClick,
text = { Text(text = text) })
}
@Composable

@ -153,7 +153,7 @@ class TaildropViewModel(
fun TrailingContentForPeer(peerId: String) {
// Check our outgoing files for the peer and determine the state of the transfer.
val transfers = this.transfers.collectAsState().value.filter { it.PeerID == peerId }
var status: TransferState = transferState(transfers) ?: return
val status: TransferState = transferState(transfers) ?: return
// Still no status? Nothing to render for this peer

Loading…
Cancel
Save