From 3f36b0b461df15387c6c3ca2bfe88725aa473e57 Mon Sep 17 00:00:00 2001 From: Percy Wegmann Date: Thu, 28 Mar 2024 15:01:42 -0500 Subject: [PATCH] WIP styling Signed-off-by: Percy Wegmann --- .../java/com/tailscale/ipn/ui/theme/Color.kt | 12 +-- .../java/com/tailscale/ipn/ui/theme/Theme.kt | 95 +++++++++++++++---- .../java/com/tailscale/ipn/ui/util/Lists.kt | 3 +- .../com/tailscale/ipn/ui/view/MainView.kt | 76 +++++++-------- 4 files changed, 111 insertions(+), 75 deletions(-) diff --git a/android/src/main/java/com/tailscale/ipn/ui/theme/Color.kt b/android/src/main/java/com/tailscale/ipn/ui/theme/Color.kt index 18a2db3..4128971 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/theme/Color.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/theme/Color.kt @@ -5,19 +5,9 @@ package com.tailscale.ipn.ui.theme import androidx.compose.ui.graphics.Color -val ts_color_light_primary = Color(0xFF232222) -val ts_color_light_secondary = Color(0xFF706E6D) -val ts_color_light_background = Color(0xFFFFFFFF) -val ts_color_light_tintedBackground = Color(0xFFF7F5F4) +// TODO: replace references to these with references to material theme 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_light_desctrutive_text = Color(0xFFBB0000) - -val ts_color_dark_primary = Color(0xFFFAF9F8) -val ts_color_dark_secondary = Color(0xFFAFACAB) -val ts_color_dark_background = Color(0xFF232222) -val ts_color_dark_tintedBackground = Color(0xFF2E2D2D) -val ts_color_dark_blue = Color(0xFF4B70CC) -var ts_color_dark_green = Color(0xFF33C27F) diff --git a/android/src/main/java/com/tailscale/ipn/ui/theme/Theme.kt b/android/src/main/java/com/tailscale/ipn/ui/theme/Theme.kt index 23febee..5fe0471 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/theme/Theme.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/theme/Theme.kt @@ -4,30 +4,14 @@ package com.tailscale.ipn.ui.theme import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.ColorScheme import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Typography import androidx.compose.material3.darkColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable - -private val LightColors = - lightColorScheme( - primary = ts_color_light_primary, - onPrimary = ts_color_light_background, - secondary = ts_color_light_secondary, - onSecondary = ts_color_light_background, - secondaryContainer = ts_color_light_tintedBackground, - surface = ts_color_light_background, - ) - -private val DarkColors = - darkColorScheme( - primary = ts_color_dark_primary, - onPrimary = ts_color_dark_background, - secondary = ts_color_dark_secondary, - onSecondary = ts_color_dark_background, - secondaryContainer = ts_color_dark_tintedBackground, - surface = ts_color_dark_background, - ) +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.sp @Composable fun AppTheme(useDarkTheme: Boolean = isSystemInDarkTheme(), content: @Composable() () -> Unit) { @@ -38,5 +22,74 @@ fun AppTheme(useDarkTheme: Boolean = isSystemInDarkTheme(), content: @Composable DarkColors } - MaterialTheme(colorScheme = colors, content = content) + val typography = + Typography( + titleMedium = + MaterialTheme.typography.titleMedium.copy(fontSize = 18.sp, lineHeight = 26.sp), + bodyMedium = + MaterialTheme.typography.bodyMedium.copy(fontSize = 16.sp, lineHeight = 26.sp)) + + MaterialTheme(colorScheme = colors, typography = typography, content = content) } + +private val LightColors = + lightColorScheme( + primary = Color(0xFF4B70CC), // blue-500 + onPrimary = Color(0xFFFFFFFF), // white + primaryContainer = Color(0xFFF0F5FF), // blue-0 + onPrimaryContainer = Color(0xFF3E5DB3), // blue-600 + error = Color(0xFFB22C30), // red-500 + onError = Color(0xFFFFFFFF), // white + errorContainer = Color(0xFFFEF6F3), // red-0 + onErrorContainer = Color(0xFF930921), // red-600 + surfaceDim = Color(0xFFF7F5F4), // gray-100 + surface = Color(0xFFF7F5F4), // gray-100 + background = Color(0xFFF7F5F4), // gray-100 + surfaceBright = Color(0xFFFFFFFF), // white + surfaceContainerLowest = Color(0xFFFFFFFF), // white + surfaceContainerLow = Color(0xFFF7F5F4), // gray-100 + surfaceContainer = Color(0xFFF7F5F4), // gray-100 + surfaceContainerHigh = Color(0xFFF7F5F4), // gray-100 + surfaceContainerHighest = Color(0xFFF7F5F4), // gray-100 + onSurface = Color(0xFF232222), // gray-800 + onSurfaceVariant = Color(0xFF706E6D), // gray-500 + outline = Color(0xFFD9D6D5), // gray-300 + outlineVariant = Color(0xFFEDEBEA), // gray-200 + inverseSurface = Color(0xFF232222), // gray-800 + inverseOnSurface = Color(0xFFFFFFFF), // white + scrim = Color(0xFF000000), // black + ) + +private val DarkColors = + darkColorScheme( + primary = Color(0xFFFAF9F8), + onPrimary = Color(0xFFAFACAB), + secondary = Color(0xFF232222), + onSecondary = Color(0xFF2E2D2D), + secondaryContainer = Color(0xFF4B70CC), + surface = Color(0xFF33C27F), + ) + +val ColorScheme.warning: Color + get() = Color(0xFFD97916) // yellow-300 + +val ColorScheme.onWarning: Color + get() = Color(0xFFFFFFFF) // white + +val ColorScheme.warningContainer: Color + get() = Color(0xFFFFFAEE) // orange-0 + +val ColorScheme.onWarningContainer: Color + get() = Color(0xFF7E1E22) // orange-600 + +val ColorScheme.success: Color + get() = Color(0xFF0A825D) // green-400 + +val ColorScheme.onSuccess: Color + get() = Color(0xFFFFFFFF) // white + +val ColorScheme.successContainer: Color + get() = Color(0xFFEFFEEC) // green-0 + +val ColorScheme.onSuccessContainer: Color + get() = Color(0xFF0E4B3B) // green-600 diff --git a/android/src/main/java/com/tailscale/ipn/ui/util/Lists.kt b/android/src/main/java/com/tailscale/ipn/ui/util/Lists.kt index 6fb4281..bc05c93 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/util/Lists.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/util/Lists.kt @@ -13,6 +13,7 @@ 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.alpha import androidx.compose.ui.unit.dp object Lists { @@ -26,7 +27,7 @@ object Lists { @Composable fun ItemDivider() { - HorizontalDivider(color = MaterialTheme.colorScheme.secondaryContainer) + HorizontalDivider(modifier = Modifier.alpha(0f)) } } diff --git a/android/src/main/java/com/tailscale/ipn/ui/view/MainView.kt b/android/src/main/java/com/tailscale/ipn/ui/view/MainView.kt index 79d7a7f..f918d90 100644 --- a/android/src/main/java/com/tailscale/ipn/ui/view/MainView.kt +++ b/android/src/main/java/com/tailscale/ipn/ui/view/MainView.kt @@ -82,22 +82,36 @@ fun MainView(navigation: MainViewNavigation, viewModel: MainViewModel = viewMode verticalArrangement = Arrangement.Center) { val state = viewModel.ipnState.collectAsState(initial = Ipn.State.NoState) val user = viewModel.loggedInUser.collectAsState(initial = null) + val stateVal = viewModel.stateRes.collectAsState(initial = R.string.placeholder) + val stateStr = stringResource(id = stateVal.value) + val username = viewModel.userName - Row( - modifier = - Modifier.fillMaxWidth() - .background(MaterialTheme.colorScheme.secondaryContainer) - .padding(horizontal = 16.dp) - .padding(top = 10.dp), - verticalAlignment = Alignment.CenterVertically) { + ListItem( + leadingContent = { val isOn = viewModel.vpnToggleState.collectAsState(initial = false) if (state.value != Ipn.State.NoState) { TintedSwitch(onCheckedChange = { viewModel.toggleVpn() }, checked = isOn.value) - Spacer(Modifier.size(3.dp)) } - - StateDisplay(viewModel.stateRes, viewModel.userName) - + }, + headlineContent = { + if (username.isNotEmpty()) { + Text(text = username, style = MaterialTheme.typography.titleMedium) + } + }, + supportingContent = { + if (username.isNotEmpty()) { + Text( + text = stateStr, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.secondary) + } else { + Text( + text = stateStr, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.secondary) + } + }, + trailingContent = { Box( modifier = Modifier.weight(1f).clickable { navigation.onNavigateToSettings() }, @@ -107,7 +121,7 @@ fun MainView(navigation: MainViewNavigation, viewModel: MainViewModel = viewMode else -> Avatar(profile = user.value, size = 36) } } - } + }) when (state.value) { Ipn.State.Running -> { @@ -188,30 +202,6 @@ fun ExitNodeStatus(navAction: () -> Unit, viewModel: MainViewModel) { } } -@Composable -fun StateDisplay(state: StateFlow, tailnet: String) { - val stateVal = state.collectAsState(initial = R.string.placeholder) - val stateStr = stringResource(id = stateVal.value) - - Column(modifier = Modifier.padding(7.dp)) { - when (tailnet.isEmpty()) { - false -> { - Text(text = tailnet, style = MaterialTheme.typography.titleMedium) - Text( - text = stateStr, - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.secondary) - } - true -> { - Text( - text = stateStr, - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.primary) - } - } - } -} - @Composable fun SettingsButton(user: IpnLocal.LoginProfile?, action: () -> Unit) { // (jonathan) TODO: On iOS this is the users avatar or a letter avatar. @@ -365,12 +355,14 @@ fun PeerList( } else { Color.Gray } - Box( - modifier = - Modifier.size(10.dp) - .background( - color = color, shape = RoundedCornerShape(percent = 50))) {} - Spacer(modifier = Modifier.size(6.dp)) + Box(modifier = Modifier.padding(top = 3.dp)) { + Box( + modifier = + Modifier.size(10.dp) + .background( + color = color, shape = RoundedCornerShape(percent = 50))) {} + } + Spacer(modifier = Modifier.size(8.dp)) Text(text = peer.ComputedName, style = MaterialTheme.typography.titleMedium) } },