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
)