diff --git a/app/src/main/java/com/geeksville/mesh/ui/NodeItem.kt b/app/src/main/java/com/geeksville/mesh/ui/NodeItem.kt index f1f694bc3..6d43119d7 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/NodeItem.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/NodeItem.kt @@ -17,6 +17,8 @@ package com.geeksville.mesh.ui +import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -29,14 +31,10 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width -import androidx.compose.foundation.layout.wrapContentSize -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.NoCell import androidx.compose.material3.AssistChip import androidx.compose.material3.AssistChipDefaults import androidx.compose.material3.Card import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.Icon import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text @@ -50,7 +48,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontStyle -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.tooling.preview.Preview @@ -66,6 +63,7 @@ import com.geeksville.mesh.ui.components.NodeKeyStatusIcon import com.geeksville.mesh.ui.components.NodeMenu import com.geeksville.mesh.ui.components.NodeMenuAction import com.geeksville.mesh.ui.components.SignalInfo +import com.geeksville.mesh.ui.components.StatusIcons import com.geeksville.mesh.ui.compose.ElevationInfo import com.geeksville.mesh.ui.compose.SatelliteCountInfo import com.geeksville.mesh.ui.preview.NodePreviewParameterProvider @@ -119,6 +117,8 @@ fun NodeItem( } else { thatNode.user.role?.isUnmessageableRole() == true } + var menuExpanded by remember { mutableStateOf(false) } + Card( modifier = modifier .fillMaxWidth() @@ -136,10 +136,8 @@ fun NodeItem( .fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, ) { - var menuExpanded by remember { mutableStateOf(false) } - Box( - modifier = Modifier.wrapContentSize(Alignment.TopStart), - ) { + val inputChipInteractionSource = remember { MutableInteractionSource() } + Box { AssistChip( modifier = Modifier .width(IntrinsicSize.Min) @@ -152,24 +150,32 @@ fun NodeItem( Text( modifier = Modifier.fillMaxWidth(), text = thatNode.user.shortName.ifEmpty { "???" }, - fontWeight = if (isFavorite) FontWeight.Bold else FontWeight.Normal, fontSize = MaterialTheme.typography.labelLarge.fontSize, textDecoration = TextDecoration.LineThrough.takeIf { isIgnored }, textAlign = TextAlign.Center, ) }, - onClick = { - menuExpanded = !menuExpanded - }, + onClick = {}, + interactionSource = inputChipInteractionSource, ) - NodeMenu( - node = thatNode, - showFullMenu = !isThisNode && isConnected, - onAction = onAction, - expanded = menuExpanded, - onDismissRequest = { menuExpanded = false }, + Box( + modifier = Modifier + .matchParentSize() + .combinedClickable( + onClick = { onAction(NodeMenuAction.MoreDetails(thatNode)) }, + onLongClick = { menuExpanded = true }, + interactionSource = inputChipInteractionSource, + indication = null, + ) ) } + NodeMenu( + node = thatNode, + showFullMenu = !isThisNode && isConnected, + onAction = onAction, + expanded = menuExpanded, + onDismissRequest = { menuExpanded = false }, + ) NodeKeyStatusIcon( hasPKC = thatNode.hasPKC, mismatchKey = thatNode.mismatchKey, @@ -179,31 +185,15 @@ fun NodeItem( Text( modifier = Modifier.weight(1f), text = longName, - fontWeight = if (isFavorite) FontWeight.Bold else FontWeight.Normal, style = style, textDecoration = TextDecoration.LineThrough.takeIf { isIgnored }, softWrap = true, ) - LastHeardInfo( lastHeard = thatNode.lastHeard, currentTimeMillis = currentTimeMillis ) - } - if (unmessageable) { - Row( - verticalAlignment = Alignment.CenterVertically, - ) { - Icon( - imageVector = Icons.Outlined.NoCell, - contentDescription = stringResource(R.string.unmessageable), - tint = MaterialTheme.colorScheme.error, - modifier = Modifier.size(32.dp) - ) - Text( - stringResource(R.string.unmonitored_or_infrastructure) - ) - } + StatusIcons(isFavorite = isFavorite, unmessageable = unmessageable) } Row( modifier = Modifier diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/NodeMenu.kt b/app/src/main/java/com/geeksville/mesh/ui/components/NodeMenu.kt index 5006357b9..e8793e124 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/components/NodeMenu.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/components/NodeMenu.kt @@ -20,10 +20,14 @@ package com.geeksville.mesh.ui.components import androidx.compose.foundation.background import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Star +import androidx.compose.material.icons.twotone.StarBorder import androidx.compose.material3.Checkbox import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -148,14 +152,9 @@ fun NodeMenu( Text(stringResource(R.string.favorite)) }, trailingIcon = { - Checkbox( - checked = node.isFavorite, - onCheckedChange = { - onDismissRequest() - displayFavoriteDialog = true - }, - modifier = Modifier.size(24.dp), - enabled = !node.isIgnored, + Icon( + imageVector = if (node.isFavorite) Icons.Filled.Star else Icons.TwoTone.StarBorder, + contentDescription = stringResource(R.string.favorite), ) } ) diff --git a/app/src/main/java/com/geeksville/mesh/ui/components/StatusIcons.kt b/app/src/main/java/com/geeksville/mesh/ui/components/StatusIcons.kt new file mode 100644 index 000000000..565049f65 --- /dev/null +++ b/app/src/main/java/com/geeksville/mesh/ui/components/StatusIcons.kt @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2025 Meshtastic LLC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.geeksville.mesh.ui.components + +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Star +import androidx.compose.material.icons.outlined.NoCell +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.geeksville.mesh.R + +@Composable +fun StatusIcons( + isFavorite: Boolean, + unmessageable: Boolean +) { + Row( + modifier = Modifier.padding(5.dp) + ) { + if (isFavorite) { + Icon( + imageVector = Icons.Filled.Star, + contentDescription = stringResource(R.string.favorite), + modifier = Modifier + .size(25.dp), // Smaller size for badge + tint = Color(color = 0xFFFEC30A) + ) + } + if (unmessageable) { + Icon( + imageVector = Icons.Outlined.NoCell, + contentDescription = stringResource(R.string.unmessageable), + modifier = Modifier + .size(25.dp), // Smaller size for badge + tint = MaterialTheme.colorScheme.error, + ) + } + } +} + +@Preview +@Composable +fun StatusIconsPreview() { + StatusIcons(isFavorite = true, unmessageable = true) +} diff --git a/app/src/main/java/com/geeksville/mesh/ui/preview/NodePreviewParameterProvider.kt b/app/src/main/java/com/geeksville/mesh/ui/preview/NodePreviewParameterProvider.kt index 166fdde62..f986163f6 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/preview/NodePreviewParameterProvider.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/preview/NodePreviewParameterProvider.kt @@ -57,6 +57,7 @@ class NodePreviewParameterProvider : PreviewParameterProvider { voltage = 3.7F uptimeSeconds = 3600 }, + isFavorite = true, hopsAway = 0 ) @@ -114,6 +115,7 @@ class NodePreviewParameterProvider : PreviewParameterProvider { ble = 39 uptime = 420 }, + isFavorite = true, hopsAway = 2 )