Got rid of ts_color_light_green

Signed-off-by: Percy Wegmann <percy@tailscale.com>
ox/styling_bak
Percy Wegmann 2 years ago
parent 5c77e8377c
commit 4a82eabaff
No known key found for this signature in database
GPG Key ID: 29D8CDEB4C13D48B

@ -3,6 +3,15 @@
package com.tailscale.ipn.ui.model package com.tailscale.ipn.ui.model
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import com.tailscale.ipn.R
import com.tailscale.ipn.ui.theme.success
import com.tailscale.ipn.ui.util.ComposableStringFormatter
import com.tailscale.ipn.ui.util.DisplayAddress
import com.tailscale.ipn.ui.util.TimeUtil
import com.tailscale.ipn.ui.viewModel.PeerSettingInfo
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
class Tailcfg { class Tailcfg {
@ -82,6 +91,33 @@ class Tailcfg {
val isMullvadNode: Boolean val isMullvadNode: Boolean
get() = Name.endsWith(".mullvad.ts.net.") get() = Name.endsWith(".mullvad.ts.net.")
val displayName: String
get() = ComputedName ?: ""
fun connectedOrSelfNode(nm: Netmap.NetworkMap?) =
Online == true || StableID == nm?.SelfNode?.StableID
fun connectedStrRes(nm: Netmap.NetworkMap?) =
if (connectedOrSelfNode(nm)) R.string.connected else R.string.not_connected
@Composable
fun connectedColor(nm: Netmap.NetworkMap?) =
if (connectedOrSelfNode(nm)) MaterialTheme.colorScheme.success else Color.Gray
val displayAddresses: List<DisplayAddress>
get() {
var addresses = mutableListOf<DisplayAddress>()
Addresses?.let { addresses.addAll(it.map { addr -> DisplayAddress(addr) }) }
addresses.add(DisplayAddress(Name))
return addresses
}
val info: List<PeerSettingInfo>
get() =
listOf(
PeerSettingInfo(R.string.os, ComposableStringFormatter(Hostinfo.OS ?: "")),
PeerSettingInfo(R.string.key_expiry, TimeUtil().keyExpiryFromGoTime(KeyExpiry)))
} }
@Serializable @Serializable

@ -7,7 +7,5 @@ import androidx.compose.ui.graphics.Color
// TODO: replace references to these with references to material theme // TODO: replace references to these with references to material theme
val ts_color_light_blue = Color(0xFF4B70CC) val ts_color_light_blue = Color(0xFF4B70CC)
val ts_color_light_green = Color(0xFF1EA672)
var ts_color_dark_desctrutive_text = Color(0xFFFF0000) var ts_color_dark_desctrutive_text = Color(0xFFFF0000)
var ts_color_light_desctrutive_text = Color(0xFFBB0000)

@ -5,6 +5,7 @@ package com.tailscale.ipn.ui.util
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
// FeatureStateRepresentation represents the status of a feature // FeatureStateRepresentation represents the status of a feature
@ -12,7 +13,7 @@ import androidx.compose.ui.graphics.Color
// It is typically implemented as an enumeration. // It is typically implemented as an enumeration.
interface FeatureStateRepresentation { interface FeatureStateRepresentation {
@get:DrawableRes val symbolDrawable: Int @get:DrawableRes val symbolDrawable: Int
val tint: Color @get:Composable val tint: Color
@get:StringRes val title: Int @get:StringRes val title: Int
@get:StringRes val caption: Int @get:StringRes val caption: Int
} }

@ -13,7 +13,6 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
object Lists { object Lists {
@ -27,7 +26,7 @@ object Lists {
@Composable @Composable
fun ItemDivider() { fun ItemDivider() {
HorizontalDivider(modifier = Modifier.alpha(0f)) HorizontalDivider(color = MaterialTheme.colorScheme.outline)
} }
} }

@ -55,15 +55,12 @@ import com.tailscale.ipn.ui.model.Ipn
import com.tailscale.ipn.ui.model.IpnLocal import com.tailscale.ipn.ui.model.IpnLocal
import com.tailscale.ipn.ui.model.Permission import com.tailscale.ipn.ui.model.Permission
import com.tailscale.ipn.ui.model.Permissions import com.tailscale.ipn.ui.model.Permissions
import com.tailscale.ipn.ui.model.StableNodeID
import com.tailscale.ipn.ui.model.Tailcfg import com.tailscale.ipn.ui.model.Tailcfg
import com.tailscale.ipn.ui.theme.ts_color_light_green
import com.tailscale.ipn.ui.util.LoadingIndicator import com.tailscale.ipn.ui.util.LoadingIndicator
import com.tailscale.ipn.ui.util.PeerSet import com.tailscale.ipn.ui.util.PeerSet
import com.tailscale.ipn.ui.util.flag import com.tailscale.ipn.ui.util.flag
import com.tailscale.ipn.ui.util.itemsWithDividers import com.tailscale.ipn.ui.util.itemsWithDividers
import com.tailscale.ipn.ui.viewModel.MainViewModel import com.tailscale.ipn.ui.viewModel.MainViewModel
import kotlinx.coroutines.flow.StateFlow
// Navigation actions for the MainView // Navigation actions for the MainView
data class MainViewNavigation( data class MainViewNavigation(
@ -122,16 +119,12 @@ fun MainView(navigation: MainViewNavigation, viewModel: MainViewModel = viewMode
PromptPermissionsIfNecessary(permissions = Permissions.all) PromptPermissionsIfNecessary(permissions = Permissions.all)
val selfPeerId = viewModel.selfPeerId.collectAsState(initial = "")
Row(modifier = Modifier.padding(top = 10.dp, bottom = 20.dp)) { Row(modifier = Modifier.padding(top = 10.dp, bottom = 20.dp)) {
ExitNodeStatus( ExitNodeStatus(
navAction = navigation.onNavigateToExitNodes, viewModel = viewModel) navAction = navigation.onNavigateToExitNodes, viewModel = viewModel)
} }
PeerList( PeerList(
searchTerm = viewModel.searchTerm, viewModel = viewModel,
state = viewModel.ipnState,
peers = viewModel.peers,
selfPeer = selfPeerId.value,
onNavigateToPeerDetails = navigation.onNavigateToPeerDetails, onNavigateToPeerDetails = navigation.onNavigateToPeerDetails,
onSearch = { viewModel.searchPeers(it) }) onSearch = { viewModel.searchPeers(it) })
} }
@ -279,16 +272,14 @@ fun ConnectView(
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun PeerList( fun PeerList(
searchTerm: StateFlow<String>, viewModel: MainViewModel,
peers: StateFlow<List<PeerSet>>,
state: StateFlow<Ipn.State>,
selfPeer: StableNodeID,
onNavigateToPeerDetails: (Tailcfg.Node) -> Unit, onNavigateToPeerDetails: (Tailcfg.Node) -> Unit,
onSearch: (String) -> Unit onSearch: (String) -> Unit
) { ) {
val peerList = peers.collectAsState(initial = emptyList<PeerSet>()) val peerList = viewModel.peers.collectAsState(initial = emptyList<PeerSet>())
val searchTermStr by searchTerm.collectAsState(initial = "") val searchTermStr by viewModel.searchTerm.collectAsState(initial = "")
val stateVal = state.collectAsState(initial = Ipn.State.NoState) val stateVal = viewModel.ipnState.collectAsState(initial = Ipn.State.NoState)
val netmap = viewModel.netmap.collectAsState()
SearchBar( SearchBar(
query = searchTermStr, query = searchTermStr,
@ -328,21 +319,12 @@ fun PeerList(
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically) {
// By definition, SelfPeer is online since we will not show the peer list // By definition, SelfPeer is online since we will not show the peer list
// unless you're connected. // unless you're connected.
val isSelfAndRunning =
(peer.StableID == selfPeer && stateVal.value == Ipn.State.Running)
val color: Color =
if ((peer.Online == true) || isSelfAndRunning) {
ts_color_light_green
} else {
Color.Gray
}
Box(modifier = Modifier.padding(top = 3.dp)) {
Box( Box(
modifier = modifier =
Modifier.size(10.dp) Modifier.size(10.dp)
.background( .background(
color = color, shape = RoundedCornerShape(percent = 50))) {} color = peer.connectedColor(netmap.value),
} shape = RoundedCornerShape(percent = 50))) {}
Spacer(modifier = Modifier.size(8.dp)) Spacer(modifier = Modifier.size(8.dp))
Text(text = peer.ComputedName, style = MaterialTheme.typography.titleMedium) Text(text = peer.ComputedName, style = MaterialTheme.typography.titleMedium)
} }

@ -19,6 +19,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalClipboardManager
@ -50,16 +51,19 @@ fun PeerDetails(
.padding(horizontal = 16.dp) .padding(horizontal = 16.dp)
.padding(top = 22.dp), .padding(top = 22.dp),
) { ) {
Text(text = model.nodeName, style = MaterialTheme.typography.titleLarge) model.netmap.collectAsState().value?.let { netmap ->
model.node.collectAsState().value?.let { node ->
Text(text = node.displayName, style = MaterialTheme.typography.titleLarge)
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically) {
Box( Box(
modifier = modifier =
Modifier.size(8.dp) Modifier.size(8.dp)
.background( .background(
color = model.connectedColor, shape = RoundedCornerShape(percent = 50))) {} color = node.connectedColor(netmap),
shape = RoundedCornerShape(percent = 50))) {}
Spacer(modifier = Modifier.size(8.dp)) Spacer(modifier = Modifier.size(8.dp))
Text( Text(
text = stringResource(id = model.connectedStrRes), text = stringResource(id = node.connectedStrRes(netmap)),
style = MaterialTheme.typography.bodyMedium) style = MaterialTheme.typography.bodyMedium)
} }
Column(modifier = Modifier.fillMaxHeight()) { Column(modifier = Modifier.fillMaxHeight()) {
@ -68,19 +72,23 @@ fun PeerDetails(
style = MaterialTheme.typography.titleMedium) style = MaterialTheme.typography.titleMedium)
Column(modifier = settingsRowModifier()) { Column(modifier = settingsRowModifier()) {
model.addresses.forEach { AddressRow(address = it.address, type = it.typeString) } node.displayAddresses.forEach {
AddressRow(address = it.address, type = it.typeString)
}
} }
Spacer(modifier = Modifier.size(16.dp)) Spacer(modifier = Modifier.size(16.dp))
Column(modifier = settingsRowModifier()) { Column(modifier = settingsRowModifier()) {
model.info.forEach { node.info.forEach {
ValueRow(title = stringResource(id = it.titleRes), value = it.value.getString()) ValueRow(title = stringResource(id = it.titleRes), value = it.value.getString())
} }
} }
} }
} }
} }
}
}
} }
@Composable @Composable

@ -20,7 +20,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.tailscale.ipn.ui.model.Ipn import com.tailscale.ipn.ui.model.Ipn
import com.tailscale.ipn.ui.model.Tailcfg import com.tailscale.ipn.ui.model.Tailcfg
import com.tailscale.ipn.ui.theme.ts_color_light_green import com.tailscale.ipn.ui.theme.success
@Composable @Composable
fun PeerView( fun PeerView(
@ -43,7 +43,7 @@ fun PeerView(
val isSelfAndRunning = (peer.StableID == selfPeer && stateVal == Ipn.State.Running) val isSelfAndRunning = (peer.StableID == selfPeer && stateVal == Ipn.State.Running)
val color: Color = val color: Color =
if ((peer.Online == true) || isSelfAndRunning) { if ((peer.Online == true) || isSelfAndRunning) {
ts_color_light_green MaterialTheme.colorScheme.success
} else { } else {
Color.Gray Color.Gray
} }

@ -5,6 +5,8 @@ package com.tailscale.ipn.ui.viewModel
import android.util.Log import android.util.Log
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
@ -14,8 +16,7 @@ import com.tailscale.ipn.ui.localapi.Client
import com.tailscale.ipn.ui.model.Ipn import com.tailscale.ipn.ui.model.Ipn
import com.tailscale.ipn.ui.model.Tailcfg import com.tailscale.ipn.ui.model.Tailcfg
import com.tailscale.ipn.ui.notifier.Notifier import com.tailscale.ipn.ui.notifier.Notifier
import com.tailscale.ipn.ui.theme.ts_color_light_desctrutive_text import com.tailscale.ipn.ui.theme.success
import com.tailscale.ipn.ui.theme.ts_color_light_green
import com.tailscale.ipn.ui.util.FeatureStateRepresentation import com.tailscale.ipn.ui.util.FeatureStateRepresentation
import com.tailscale.ipn.ui.util.LoadingIndicator import com.tailscale.ipn.ui.util.LoadingIndicator
import com.tailscale.ipn.ui.util.set import com.tailscale.ipn.ui.util.set
@ -96,7 +97,7 @@ enum class DNSEnablementState : FeatureStateRepresentation {
get() = R.string.tailscale_is_not_running_this_device_is_using_the_system_dns_resolver get() = R.string.tailscale_is_not_running_this_device_is_using_the_system_dns_resolver
override val tint: Color override val tint: Color
get() = Color.Gray @Composable get() = Color.Gray
override val symbolDrawable: Int override val symbolDrawable: Int
get() = R.drawable.xmark_circle get() = R.drawable.xmark_circle
@ -109,7 +110,7 @@ enum class DNSEnablementState : FeatureStateRepresentation {
@StringRes get() = R.string.this_device_is_using_tailscale_to_resolve_dns_names @StringRes get() = R.string.this_device_is_using_tailscale_to_resolve_dns_names
override val tint: Color override val tint: Color
get() = ts_color_light_green @Composable get() = MaterialTheme.colorScheme.success
override val symbolDrawable: Int override val symbolDrawable: Int
get() = R.drawable.check_circle get() = R.drawable.check_circle
@ -122,7 +123,7 @@ enum class DNSEnablementState : FeatureStateRepresentation {
@StringRes get() = R.string.this_device_is_using_the_system_dns_resolver @StringRes get() = R.string.this_device_is_using_the_system_dns_resolver
override val tint: Color override val tint: Color
get() = ts_color_light_desctrutive_text @Composable get() = MaterialTheme.colorScheme.success
override val symbolDrawable: Int override val symbolDrawable: Int
get() = R.drawable.xmark_circle get() = R.drawable.xmark_circle

@ -8,7 +8,6 @@ import com.tailscale.ipn.R
import com.tailscale.ipn.ui.localapi.Client import com.tailscale.ipn.ui.localapi.Client
import com.tailscale.ipn.ui.model.Ipn import com.tailscale.ipn.ui.model.Ipn
import com.tailscale.ipn.ui.model.Ipn.State import com.tailscale.ipn.ui.model.Ipn.State
import com.tailscale.ipn.ui.model.StableNodeID
import com.tailscale.ipn.ui.notifier.Notifier import com.tailscale.ipn.ui.notifier.Notifier
import com.tailscale.ipn.ui.util.LoadingIndicator import com.tailscale.ipn.ui.util.LoadingIndicator
import com.tailscale.ipn.ui.util.PeerCategorizer import com.tailscale.ipn.ui.util.PeerCategorizer
@ -38,9 +37,6 @@ class MainViewModel : IpnViewModel() {
// The active search term for filtering peers // The active search term for filtering peers
val searchTerm: StateFlow<String> = MutableStateFlow("") val searchTerm: StateFlow<String> = MutableStateFlow("")
// The peerID of the local node
val selfPeerId: StateFlow<StableNodeID> = MutableStateFlow("")
private val peerCategorizer = PeerCategorizer(viewModelScope) private val peerCategorizer = PeerCategorizer(viewModelScope)
val userName: String val userName: String
@ -59,7 +55,6 @@ class MainViewModel : IpnViewModel() {
viewModelScope.launch { viewModelScope.launch {
Notifier.netmap.collect { netmap -> Notifier.netmap.collect { netmap ->
peers.set(peerCategorizer.groupedAndFilteredPeers(searchTerm.value)) peers.set(peerCategorizer.groupedAndFilteredPeers(searchTerm.value))
selfPeerId.set(netmap?.SelfNode?.StableID ?: "")
} }
} }
} }

@ -3,16 +3,18 @@
package com.tailscale.ipn.ui.viewModel package com.tailscale.ipn.ui.viewModel
import androidx.compose.ui.graphics.Color
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import com.tailscale.ipn.R import androidx.lifecycle.viewModelScope
import com.tailscale.ipn.ui.model.Netmap
import com.tailscale.ipn.ui.model.StableNodeID import com.tailscale.ipn.ui.model.StableNodeID
import com.tailscale.ipn.ui.model.Tailcfg
import com.tailscale.ipn.ui.notifier.Notifier import com.tailscale.ipn.ui.notifier.Notifier
import com.tailscale.ipn.ui.theme.ts_color_light_green
import com.tailscale.ipn.ui.util.ComposableStringFormatter import com.tailscale.ipn.ui.util.ComposableStringFormatter
import com.tailscale.ipn.ui.util.DisplayAddress import com.tailscale.ipn.ui.util.set
import com.tailscale.ipn.ui.util.TimeUtil import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import java.io.File import java.io.File
data class PeerSettingInfo(val titleRes: Int, val value: ComposableStringFormatter) data class PeerSettingInfo(val titleRes: Int, val value: ComposableStringFormatter)
@ -25,29 +27,15 @@ class PeerDetailsViewModelFactory(private val nodeId: StableNodeID, private val
} }
class PeerDetailsViewModel(val nodeId: StableNodeID, val filesDir: File) : IpnViewModel() { class PeerDetailsViewModel(val nodeId: StableNodeID, val filesDir: File) : IpnViewModel() {
val netmap: StateFlow<Netmap.NetworkMap?> = MutableStateFlow(null)
var addresses: List<DisplayAddress> = emptyList() val node: StateFlow<Tailcfg.Node?> = MutableStateFlow(null)
var info: List<PeerSettingInfo> = emptyList()
val nodeName: String
val connectedStrRes: Int
val connectedColor: Color
init { init {
val peer = Notifier.netmap.value?.getPeer(nodeId) viewModelScope.launch {
peer?.Addresses?.let { addresses = it.map { addr -> DisplayAddress(addr) } } Notifier.netmap.collect { nm ->
netmap.set(nm)
peer?.Name?.let { addresses = listOf(DisplayAddress(it)) + addresses } nm?.getPeer(nodeId)?.let { peer -> node.set(peer) }
}
peer?.let { p ->
info =
listOf(
PeerSettingInfo(R.string.os, ComposableStringFormatter(p.Hostinfo.OS ?: "")),
PeerSettingInfo(R.string.key_expiry, TimeUtil().keyExpiryFromGoTime(p.KeyExpiry)))
} }
nodeName = peer?.ComputedName ?: ""
connectedStrRes = if (peer?.Online == true) R.string.connected else R.string.not_connected
connectedColor = if (peer?.Online == true) ts_color_light_green else Color.Gray
} }
} }

Loading…
Cancel
Save