refactor: Add remote node indicator and node chip to app bar (#2195)

This commit is contained in:
James Rich 2025-06-21 12:14:33 +00:00 committed by GitHub
parent d24a501f06
commit 86a35603d5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 75 additions and 35 deletions

View file

@ -266,7 +266,10 @@ class UIViewModel @Inject constructor(
private val _title = MutableStateFlow("")
val title: StateFlow<String> = _title.asStateFlow()
fun setTitle(title: String) {
_title.value = title
viewModelScope.launch {
_title.value = title
}
}
val receivingLocationUpdates: StateFlow<Boolean> get() = locationRepository.receivingLocationUpdates

View file

@ -18,6 +18,7 @@
package com.geeksville.mesh.ui
import androidx.annotation.StringRes
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
@ -62,6 +63,7 @@ import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavDestination
@ -74,6 +76,7 @@ import androidx.navigation.compose.rememberNavController
import com.geeksville.mesh.BuildConfig
import com.geeksville.mesh.R
import com.geeksville.mesh.model.DeviceVersion
import com.geeksville.mesh.model.Node
import com.geeksville.mesh.model.UIViewModel
import com.geeksville.mesh.navigation.ChannelsRoutes
import com.geeksville.mesh.navigation.ConnectionsRoutes
@ -90,7 +93,10 @@ import com.geeksville.mesh.ui.common.components.MultipleChoiceAlertDialog
import com.geeksville.mesh.ui.common.components.ScannedQrCodeDialog
import com.geeksville.mesh.ui.common.components.SimpleAlertDialog
import com.geeksville.mesh.ui.debug.DebugMenuActions
import com.geeksville.mesh.ui.node.components.NodeChip
import com.geeksville.mesh.ui.node.components.NodeMenuAction
import com.geeksville.mesh.ui.radioconfig.RadioConfigMenuActions
import com.geeksville.mesh.ui.sharing.SharedContactDialog
enum class TopLevelDestination(@StringRes val label: Int, val icon: ImageVector, val route: Route) {
Contacts(R.string.contacts, Icons.AutoMirrored.TwoTone.Chat, ContactsRoutes.Contacts),
@ -243,16 +249,36 @@ fun MainScreen(
modifier = Modifier
.fillMaxSize()
) {
var sharedContact: Node? by remember { mutableStateOf(null) }
if (sharedContact != null) {
SharedContactDialog(
contact = sharedContact,
onDismiss = { sharedContact = null }
)
}
MainAppBar(
viewModel = viewModel,
isManaged = localConfig.security.isManaged,
navController = navController,
onAction = { action ->
when (action) {
MainMenuAction.DEBUG -> navController.navigate(Route.DebugPanel)
MainMenuAction.RADIO_CONFIG -> navController.navigate(RadioConfigRoutes.RadioConfig())
MainMenuAction.QUICK_CHAT -> navController.navigate(ContactsRoutes.QuickChat)
else -> onAction(action)
if (action is MainMenuAction) {
when (action) {
MainMenuAction.DEBUG -> navController.navigate(Route.DebugPanel)
MainMenuAction.RADIO_CONFIG -> navController.navigate(RadioConfigRoutes.RadioConfig())
MainMenuAction.QUICK_CHAT -> navController.navigate(ContactsRoutes.QuickChat)
else -> onAction(action)
}
} else if (action is NodeMenuAction) {
when (action) {
is NodeMenuAction.MoreDetails -> navController.navigate(
NodesRoutes.NodeDetail(
action.node.num
)
)
is NodeMenuAction.Share -> sharedContact = action.node
else -> {}
}
}
},
)
@ -339,7 +365,7 @@ private fun MainAppBar(
isManaged: Boolean,
navController: NavHostController,
modifier: Modifier = Modifier,
onAction: (MainMenuAction) -> Unit
onAction: (Any?) -> Unit
) {
val backStackEntry by navController.currentBackStackEntryAsState()
val currentDestination = backStackEntry?.destination
@ -352,34 +378,25 @@ private fun MainAppBar(
val title by viewModel.title.collectAsStateWithLifecycle("")
TopAppBar(
title = {
when {
currentDestination == null || isTopLevelRoute -> {
Text(
text = stringResource(id = R.string.app_name),
)
}
val title = when {
currentDestination == null || isTopLevelRoute -> stringResource(id = R.string.app_name)
currentDestination.hasRoute<Route.DebugPanel>() ->
Text(
stringResource(id = R.string.debug_panel),
)
currentDestination.hasRoute<Route.DebugPanel>() -> stringResource(id = R.string.debug_panel)
currentDestination.hasRoute<ContactsRoutes.QuickChat>() ->
Text(
stringResource(id = R.string.quick_chat),
)
currentDestination.hasRoute<ContactsRoutes.QuickChat>() -> stringResource(id = R.string.quick_chat)
currentDestination.hasRoute<ContactsRoutes.Share>() ->
Text(
stringResource(id = R.string.share_to),
)
currentDestination.hasRoute<ContactsRoutes.Share>() -> stringResource(id = R.string.share_to)
currentDestination.showLongNameTitle() -> {
Text(
title,
)
}
currentDestination.showLongNameTitle() -> title
else -> stringResource(id = R.string.app_name)
}
Text(
text = title,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.titleLarge,
)
},
modifier = modifier,
navigationIcon = if (canNavigateBack && !isTopLevelRoute) {
@ -422,8 +439,18 @@ private fun TopBarActions(
currentDestination: NavDestination?,
isManaged: Boolean,
isTopLevelRoute: Boolean,
onAction: (MainMenuAction) -> Unit
onAction: (Any?) -> Unit
) {
val ourNode by viewModel.ourNodeInfo.collectAsStateWithLifecycle()
val isConnected by viewModel.isConnected.collectAsStateWithLifecycle(false)
AnimatedVisibility(ourNode != null) {
NodeChip(
node = ourNode!!,
isThisNode = true,
isConnected = isConnected,
onAction = onAction
)
}
when {
currentDestination == null || isTopLevelRoute ->
MainMenuActions(isManaged, onAction)

View file

@ -176,6 +176,7 @@ internal fun MessageItem(
.fillMaxWidth()
.padding(horizontal = 4.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
NodeChip(
node = if (fromLocal) ourNode else node,
@ -183,14 +184,13 @@ internal fun MessageItem(
isConnected = isConnected,
isThisNode = fromLocal,
)
Spacer(Modifier.width(4.dp))
Text(
text = with(if (fromLocal) ourNode.user else node.user) { "$longName ($id)" },
overflow = TextOverflow.Ellipsis,
maxLines = 1,
style = MaterialTheme.typography.labelLarge
style = MaterialTheme.typography.labelMedium,
modifier = Modifier.weight(1f, fill = false)
)
Spacer(Modifier.weight(1f))
MessageActions(
onSendReaction = sendReaction,
onSendReply = onReply,

View file

@ -90,7 +90,16 @@ fun RadioConfigScreen(
onNavigate: (Route) -> Unit = {}
) {
val node by viewModel.destNode.collectAsStateWithLifecycle()
val nodeName: String? = node?.user?.longName
val ourNode by uiViewModel.ourNodeInfo.collectAsStateWithLifecycle()
val isLocal = node?.num == ourNode?.num
val nodeName: String? = node?.user?.longName?.let {
if (!isLocal) {
"$it (" + stringResource(R.string.remote) + ")"
} else {
it
}
}
nodeName?.let {
uiViewModel.setTitle(it)
}