From 48a27ba0227275aacbc7488aa8abf301b727abcc Mon Sep 17 00:00:00 2001
From: Phil Oliver <3497406+poliver@users.noreply.github.com>
Date: Mon, 29 Sep 2025 10:31:55 -0400
Subject: [PATCH] Remove `NodeChip` long-click (#3220)
---
app/detekt-baseline.xml | 10 +-
.../mesh/compose/MessageItemTest.kt | 2 -
.../com/geeksville/mesh/ui/map/MapView.kt | 9 +-
.../map/components/ClusterItemsListDialog.kt | 2 +-
.../ui/map/components/NodeClusterMarkers.kt | 4 +-
.../com/geeksville/mesh/ui/node/NodeMap.kt | 4 +-
.../mesh/navigation/NodesNavigation.kt | 5 +-
.../main/java/com/geeksville/mesh/ui/Main.kt | 32 +---
.../mesh/ui/common/components/MainAppBar.kt | 32 +---
.../mesh/ui/connections/ConnectionsScreen.kt | 16 +-
.../components/CurrentlyConnectedInfo.kt | 55 ++----
.../geeksville/mesh/ui/contact/Contacts.kt | 9 +-
.../com/geeksville/mesh/ui/map/MapScreen.kt | 9 +-
.../com/geeksville/mesh/ui/message/Message.kt | 23 +--
.../geeksville/mesh/ui/message/MessageList.kt | 7 +-
.../mesh/ui/message/MessageScreenEvent.kt | 11 +-
.../mesh/ui/message/MessageViewModel.kt | 69 -------
.../mesh/ui/message/components/MessageItem.kt | 15 +-
.../com/geeksville/mesh/ui/node/NodeDetail.kt | 8 +-
.../mesh/ui/node/NodeDetailViewModel.kt | 2 -
.../com/geeksville/mesh/ui/node/NodeScreen.kt | 169 +++++++++++++-----
.../geeksville/mesh/ui/node/NodesViewModel.kt | 59 ++----
.../mesh/ui/node/components/NodeChip.kt | 85 +++------
.../mesh/ui/node/components/NodeItem.kt | 13 +-
.../mesh/ui/node/components/NodeMenu.kt | 164 +----------------
.../mesh/ui/settings/SettingsScreen.kt | 9 +-
.../settings/radio/CleanNodeDatabaseScreen.kt | 7 +-
.../radio/components/RadioConfigScreenList.kt | 3 +-
core/strings/src/main/res/values/strings.xml | 3 +
29 files changed, 248 insertions(+), 588 deletions(-)
diff --git a/app/detekt-baseline.xml b/app/detekt-baseline.xml
index 8591b9713..e18674796 100644
--- a/app/detekt-baseline.xml
+++ b/app/detekt-baseline.xml
@@ -45,8 +45,6 @@
ComposableParamOrder:NodeFilterTextField.kt$NodeFilterTextField
ComposableParamOrder:NodeItem.kt$NodeItem
ComposableParamOrder:NodeKeyStatusIcon.kt$NodeKeyStatusIcon
- ComposableParamOrder:NodeMenu.kt$NodeMenu
- ComposableParamOrder:NodeScreen.kt$NodeScreen
ComposableParamOrder:PaxMetrics.kt$PaxMetricsChart
ComposableParamOrder:PermissionScreenLayout.kt$PermissionScreenLayout
ComposableParamOrder:PowerMetrics.kt$PowerMetricsChart
@@ -91,11 +89,10 @@
FinalNewline:UsbRepositoryModule.kt$com.geeksville.mesh.repository.usb.UsbRepositoryModule.kt
ForbiddenComment:SafeBluetooth.kt$SafeBluetooth$// TODO: display some kind of UI about restarting BLE
LambdaParameterEventTrailing:Channel.kt$onConfirm
- LambdaParameterEventTrailing:MainAppBar.kt$onAction
+ LambdaParameterEventTrailing:MainAppBar.kt$onClickChip
LambdaParameterEventTrailing:Message.kt$onClick
LambdaParameterEventTrailing:Message.kt$onSendMessage
LambdaParameterEventTrailing:MessageList.kt$onReply
- LambdaParameterEventTrailing:NodeChip.kt$onAction
LambdaParameterEventTrailing:NodeDetail.kt$onClick
LambdaParameterEventTrailing:NodeDetail.kt$onSaveNotes
LambdaParameterInRestartableEffect:Channel.kt$onConfirm
@@ -119,7 +116,6 @@
LongMethod:StoreForwardConfigItemList.kt$@Composable fun StoreForwardConfigScreen(navController: NavController, viewModel: RadioConfigViewModel = hiltViewModel())
LongMethod:TelemetryConfigItemList.kt$@Composable fun TelemetryConfigScreen(navController: NavController, viewModel: RadioConfigViewModel = hiltViewModel())
LongMethod:UserConfigItemList.kt$@Composable fun UserConfigScreen(navController: NavController, viewModel: RadioConfigViewModel = hiltViewModel())
- LongParameterList:MessageList.kt$( nodes: List<Node>, ourNode: Node?, isConnected: Boolean, modifier: Modifier = Modifier, listState: LazyListState = rememberLazyListState(), messages: List<Message>, selectedIds: MutableState<Set<Long>>, onUnreadChanged: (Long) -> Unit, onSendReaction: (String, Int) -> Unit, onNodeMenuAction: (NodeMenuAction) -> Unit, onDeleteMessages: (List<Long>) -> Unit, onSendMessage: (messageText: String, contactKey: String) -> Unit, contactKey: String, onReply: (Message?) -> Unit, )
LongParameterList:MessageViewModel.kt$MessageViewModel$( private val nodeRepository: NodeRepository, radioConfigRepository: RadioConfigRepository, quickChatActionRepository: QuickChatActionRepository, private val serviceRepository: ServiceRepository, private val packetRepository: PacketRepository, private val uiPrefs: UiPrefs, private val meshServiceNotifications: MeshServiceNotifications, )
MagicNumber:BluetoothInterface.kt$BluetoothInterface$1000
MagicNumber:BluetoothInterface.kt$BluetoothInterface$500
@@ -202,7 +198,6 @@
ModifierMissing:MessageActions.kt$ReplyButton
ModifierMissing:NetworkConfigItemList.kt$NetworkConfigScreen
ModifierMissing:NetworkDevices.kt$NetworkDevices
- ModifierMissing:NodeMenu.kt$NodeMenu
ModifierMissing:NodeScreen.kt$NodeScreen
ModifierMissing:NodeStatusIcons.kt$NodeStatusIcons
ModifierMissing:PaxMetrics.kt$PaxMetricsItem
@@ -231,7 +226,6 @@
ModifierNotUsedAtRoot:EditDeviceProfileDialog.kt$modifier = modifier.weight(1f)
ModifierNotUsedAtRoot:EnvironmentCharts.kt$modifier = modifier.width(dp)
ModifierNotUsedAtRoot:EnvironmentCharts.kt$modifier.width(dp)
- ModifierNotUsedAtRoot:NodeChip.kt$modifier = modifier.width(IntrinsicSize.Min).defaultMinSize(minWidth = 72.dp).semantics { contentDescription = node.user.shortName.ifEmpty { "Node" } }
ModifierNotUsedAtRoot:PaxMetrics.kt$modifier.width(dp)
ModifierNotUsedAtRoot:PowerMetrics.kt$modifier = modifier.weight(weight = Y_AXIS_WEIGHT)
ModifierNotUsedAtRoot:PowerMetrics.kt$modifier = modifier.width(dp)
@@ -401,7 +395,7 @@
ViewModelForwarding:Main.kt$VersionChecks(uIViewModel)
ViewModelInjection:DebugSearch.kt$viewModel
WildcardImport:UsbRepository.kt$import kotlinx.coroutines.flow.*
- Wrapping:Message.kt${ event -> when (event) { is MessageScreenEvent.SendMessage -> { viewModel.sendMessage(event.text, contactKey, event.replyingToPacketId) if (event.replyingToPacketId != null) replyingToPacketId = null messageInputState.clearText() } is MessageScreenEvent.SendReaction -> viewModel.sendReaction(event.emoji, event.messageId, contactKey) is MessageScreenEvent.DeleteMessages -> { viewModel.deleteMessages(event.ids) selectedMessageIds.value = emptySet() showDeleteDialog = false } is MessageScreenEvent.ClearUnreadCount -> viewModel.clearUnreadCount(contactKey, event.lastReadMessageId) is MessageScreenEvent.HandleNodeMenuAction -> { when (val action = event.action) { is NodeMenuAction.DirectMessage -> { val hasPKC = ourNode?.hasPKC == true && action.node.hasPKC val targetChannel = if (hasPKC) { DataPacket.PKC_CHANNEL_INDEX } else { action.node.channel } navigateToMessages("$targetChannel${action.node.user.id}") } is NodeMenuAction.MoreDetails -> navigateToNodeDetails(action.node.num) is NodeMenuAction.Share -> sharedContact = action.node else -> viewModel.handleNodeMenuAction(action) } } is MessageScreenEvent.SetTitle -> viewModel.setTitle(event.title) is MessageScreenEvent.NavigateToMessages -> navigateToMessages(event.contactKey) is MessageScreenEvent.NavigateToNodeDetails -> navigateToNodeDetails(event.nodeNum) MessageScreenEvent.NavigateBack -> onNavigateBack() is MessageScreenEvent.CopyToClipboard -> { clipboardManager.nativeClipboard.setPrimaryClip(ClipData.newPlainText(event.text, event.text)) selectedMessageIds.value = emptySet() } } }
+ Wrapping:Message.kt${ event -> when (event) { is MessageScreenEvent.SendMessage -> { viewModel.sendMessage(event.text, contactKey, event.replyingToPacketId) if (event.replyingToPacketId != null) replyingToPacketId = null messageInputState.clearText() } is MessageScreenEvent.SendReaction -> viewModel.sendReaction(event.emoji, event.messageId, contactKey) is MessageScreenEvent.DeleteMessages -> { viewModel.deleteMessages(event.ids) selectedMessageIds.value = emptySet() showDeleteDialog = false } is MessageScreenEvent.ClearUnreadCount -> viewModel.clearUnreadCount(contactKey, event.lastReadMessageId) is MessageScreenEvent.NodeDetails -> navigateToNodeDetails(event.node.num) is MessageScreenEvent.SetTitle -> viewModel.setTitle(event.title) is MessageScreenEvent.NavigateToMessages -> navigateToMessages(event.contactKey) is MessageScreenEvent.NavigateToNodeDetails -> navigateToNodeDetails(event.nodeNum) MessageScreenEvent.NavigateBack -> onNavigateBack() is MessageScreenEvent.CopyToClipboard -> { clipboardManager.nativeClipboard.setPrimaryClip(ClipData.newPlainText(event.text, event.text)) selectedMessageIds.value = emptySet() } } }
Wrapping:SerialConnectionImpl.kt$SerialConnectionImpl$(
Wrapping:SerialConnectionImpl.kt$SerialConnectionImpl$(port, object : SerialInputOutputManager.Listener { override fun onNewData(data: ByteArray) { listener.onDataReceived(data) } override fun onRunError(e: Exception?) { closed.set(true) ignoreException { port.dtr = false port.rts = false port.close() } closedLatch.countDown() listener.onDisconnected(e) } })
Wrapping:SerialInterface.kt$SerialInterface$(
diff --git a/app/src/androidTest/java/com/geeksville/mesh/compose/MessageItemTest.kt b/app/src/androidTest/java/com/geeksville/mesh/compose/MessageItemTest.kt
index b3396b43e..1d1a46336 100644
--- a/app/src/androidTest/java/com/geeksville/mesh/compose/MessageItemTest.kt
+++ b/app/src/androidTest/java/com/geeksville/mesh/compose/MessageItemTest.kt
@@ -65,7 +65,6 @@ class MessageItemTest {
onClick = {},
onLongClick = {},
onStatusClick = {},
- isConnected = true,
ourNode = testNode,
)
}
@@ -105,7 +104,6 @@ class MessageItemTest {
onClick = {},
onLongClick = {},
onStatusClick = {},
- isConnected = true,
ourNode = testNode,
)
}
diff --git a/app/src/google/java/com/geeksville/mesh/ui/map/MapView.kt b/app/src/google/java/com/geeksville/mesh/ui/map/MapView.kt
index dd1aacd27..9adebaa20 100644
--- a/app/src/google/java/com/geeksville/mesh/ui/map/MapView.kt
+++ b/app/src/google/java/com/geeksville/mesh/ui/map/MapView.kt
@@ -406,14 +406,7 @@ fun MapView(
val alpha = (index.toFloat() / (sortedPositions.size.toFloat() - 1))
val color = Color(focusedNode!!.colors.second).copy(alpha = alpha)
if (index == sortedPositions.lastIndex) {
- MarkerComposable(state = markerState, zIndex = 1f) {
- NodeChip(
- node = focusedNode,
- isThisNode = false,
- isConnected = false,
- onAction = {},
- )
- }
+ MarkerComposable(state = markerState, zIndex = 1f) { NodeChip(node = focusedNode) }
} else {
MarkerInfoWindowComposable(
state = markerState,
diff --git a/app/src/google/java/com/geeksville/mesh/ui/map/components/ClusterItemsListDialog.kt b/app/src/google/java/com/geeksville/mesh/ui/map/components/ClusterItemsListDialog.kt
index a37ad918a..93b4092d7 100644
--- a/app/src/google/java/com/geeksville/mesh/ui/map/components/ClusterItemsListDialog.kt
+++ b/app/src/google/java/com/geeksville/mesh/ui/map/components/ClusterItemsListDialog.kt
@@ -59,7 +59,7 @@ fun ClusterItemsListDialog(
@Composable
private fun ClusterDialogListItem(item: NodeClusterItem, onClick: () -> Unit, modifier: Modifier = Modifier) {
ListItem(
- leadingContent = { NodeChip(node = item.node, enabled = false, isThisNode = false, isConnected = false) {} },
+ leadingContent = { NodeChip(node = item.node) },
headlineContent = { Text(item.nodeTitle) },
supportingContent = {
if (item.nodeSnippet.isNotBlank()) {
diff --git a/app/src/google/java/com/geeksville/mesh/ui/map/components/NodeClusterMarkers.kt b/app/src/google/java/com/geeksville/mesh/ui/map/components/NodeClusterMarkers.kt
index e72137721..95ee5bae1 100644
--- a/app/src/google/java/com/geeksville/mesh/ui/map/components/NodeClusterMarkers.kt
+++ b/app/src/google/java/com/geeksville/mesh/ui/map/components/NodeClusterMarkers.kt
@@ -63,8 +63,6 @@ fun NodeClusterMarkers(
navigateToNodeDetails(item.node.num)
false
},
- clusterItemContent = { clusterItem ->
- NodeChip(node = clusterItem.node, enabled = false, isThisNode = false, isConnected = false) {}
- },
+ clusterItemContent = { clusterItem -> NodeChip(node = clusterItem.node) },
)
}
diff --git a/app/src/google/java/com/geeksville/mesh/ui/node/NodeMap.kt b/app/src/google/java/com/geeksville/mesh/ui/node/NodeMap.kt
index 61fd78e48..fbcb47eae 100644
--- a/app/src/google/java/com/geeksville/mesh/ui/node/NodeMap.kt
+++ b/app/src/google/java/com/geeksville/mesh/ui/node/NodeMap.kt
@@ -44,19 +44,17 @@ fun NodeMapScreen(
val positions = state.positionLogs
val destNum = state.node?.num
val ourNodeInfo by uiViewModel.ourNodeInfo.collectAsStateWithLifecycle()
- val isConnected by uiViewModel.isConnectedStateFlow.collectAsStateWithLifecycle()
Scaffold(
topBar = {
MainAppBar(
title = state.node?.user?.longName ?: "",
ourNode = ourNodeInfo,
- isConnected = isConnected,
showNodeChip = false,
canNavigateUp = true,
onNavigateUp = navController::navigateUp,
actions = {},
- onAction = {},
+ onClickChip = {},
)
},
) { paddingValues ->
diff --git a/app/src/main/java/com/geeksville/mesh/navigation/NodesNavigation.kt b/app/src/main/java/com/geeksville/mesh/navigation/NodesNavigation.kt
index 1db6f5224..514845c7c 100644
--- a/app/src/main/java/com/geeksville/mesh/navigation/NodesNavigation.kt
+++ b/app/src/main/java/com/geeksville/mesh/navigation/NodesNavigation.kt
@@ -64,10 +64,7 @@ fun NavGraphBuilder.nodesGraph(navController: NavHostController, uiViewModel: UI
composable(
deepLinks = listOf(navDeepLink(basePath = "$DEEP_LINK_BASE_URI/nodes")),
) {
- NodeScreen(
- navigateToMessages = { navController.navigate(ContactsRoutes.Messages(it)) },
- navigateToNodeDetails = { navController.navigate(NodesRoutes.NodeDetailGraph(it)) },
- )
+ NodeScreen(navigateToNodeDetails = { navController.navigate(NodesRoutes.NodeDetailGraph(it)) })
}
nodeDetailGraph(navController, uiViewModel)
}
diff --git a/app/src/main/java/com/geeksville/mesh/ui/Main.kt b/app/src/main/java/com/geeksville/mesh/ui/Main.kt
index 490b701d3..eb466ea77 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/Main.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/Main.kt
@@ -95,14 +95,11 @@ import com.geeksville.mesh.ui.common.components.ScannedQrCodeDialog
import com.geeksville.mesh.ui.connections.DeviceType
import com.geeksville.mesh.ui.connections.components.TopLevelNavIcon
import com.geeksville.mesh.ui.metrics.annotateTraceroute
-import com.geeksville.mesh.ui.node.components.NodeMenuAction
-import com.geeksville.mesh.ui.sharing.SharedContactDialog
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.isGranted
import com.google.accompanist.permissions.rememberPermissionState
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
-import org.meshtastic.core.database.model.Node
import org.meshtastic.core.model.DeviceVersion
import org.meshtastic.core.navigation.ConnectionsRoutes
import org.meshtastic.core.navigation.ContactsRoutes
@@ -337,11 +334,6 @@ fun MainScreen(uIViewModel: UIViewModel = hiltViewModel(), scanModel: BTScanMode
) {
Scaffold(snackbarHost = { SnackbarHost(uIViewModel.snackBarHostState) }) { _ ->
Column(modifier = Modifier.fillMaxSize()) {
- var sharedContact: Node? by remember { mutableStateOf(null) }
- if (sharedContact != null) {
- SharedContactDialog(contact = sharedContact, onDismiss = { sharedContact = null })
- }
-
fun NavDestination.hasGlobalAppBar(): Boolean =
// List of screens to exclude from having the global app bar
listOf(
@@ -384,22 +376,14 @@ fun MainScreen(uIViewModel: UIViewModel = hiltViewModel(), scanModel: BTScanMode
MainAppBar(
navController = navController,
ourNode = ourNodeInfo,
- isConnected = connectionState.isConnected(),
- onAction = { action ->
- when (action) {
- is NodeMenuAction.MoreDetails -> {
- navController.navigate(
- NodesRoutes.NodeDetailGraph(action.node.num),
- {
- launchSingleTop = true
- restoreState = true
- },
- )
- }
-
- is NodeMenuAction.Share -> sharedContact = action.node
- else -> {}
- }
+ onClickChip = {
+ navController.navigate(
+ NodesRoutes.NodeDetailGraph(it.num),
+ {
+ launchSingleTop = true
+ restoreState = true
+ },
+ )
},
)
}
diff --git a/app/src/main/java/com/geeksville/mesh/ui/common/components/MainAppBar.kt b/app/src/main/java/com/geeksville/mesh/ui/common/components/MainAppBar.kt
index 3e4d408cf..08e98dacd 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/common/components/MainAppBar.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/common/components/MainAppBar.kt
@@ -45,7 +45,6 @@ import androidx.navigation.NavHostController
import androidx.navigation.compose.currentBackStackEntryAsState
import com.geeksville.mesh.ui.debug.DebugMenuActions
import com.geeksville.mesh.ui.node.components.NodeChip
-import com.geeksville.mesh.ui.node.components.NodeMenuAction
import org.meshtastic.core.database.model.Node
import org.meshtastic.core.navigation.ContactsRoutes
import org.meshtastic.core.navigation.SettingsRoutes
@@ -58,8 +57,7 @@ fun MainAppBar(
modifier: Modifier = Modifier,
navController: NavHostController,
ourNode: Node?,
- isConnected: Boolean,
- onAction: (NodeMenuAction) -> Unit,
+ onClickChip: (Node) -> Unit,
) {
val backStackEntry by navController.currentBackStackEntryAsState()
val currentDestination = backStackEntry?.destination
@@ -86,7 +84,6 @@ fun MainAppBar(
subtitle = null,
canNavigateUp = navController.previousBackStackEntry != null,
ourNode = ourNode,
- isConnected = isConnected,
showNodeChip = false,
onNavigateUp = navController::navigateUp,
actions = {
@@ -97,7 +94,7 @@ fun MainAppBar(
}
}
},
- onAction = onAction,
+ onClickChip = onClickChip,
)
}
@@ -108,12 +105,11 @@ fun MainAppBar(
title: String,
subtitle: String? = null,
ourNode: Node?,
- isConnected: Boolean,
showNodeChip: Boolean,
canNavigateUp: Boolean,
onNavigateUp: () -> Unit,
actions: @Composable () -> Unit,
- onAction: (NodeMenuAction) -> Unit,
+ onClickChip: (Node) -> Unit,
) {
TopAppBar(
title = {
@@ -147,13 +143,7 @@ fun MainAppBar(
}
},
actions = {
- TopBarActions(
- ourNode = ourNode,
- isConnected = isConnected,
- showNodeChip = showNodeChip,
- actions = actions,
- onAction = onAction,
- )
+ TopBarActions(ourNode = ourNode, showNodeChip = showNodeChip, actions = actions, onClickChip = onClickChip)
},
)
}
@@ -161,20 +151,13 @@ fun MainAppBar(
@Composable
private fun TopBarActions(
ourNode: Node?,
- isConnected: Boolean,
showNodeChip: Boolean,
actions: @Composable () -> Unit,
- onAction: (NodeMenuAction) -> Unit,
+ onClickChip: (Node) -> Unit,
) {
AnimatedVisibility(visible = showNodeChip, enter = fadeIn(), exit = fadeOut()) {
- ourNode?.let {
- NodeChip(
- modifier = Modifier.padding(horizontal = 16.dp),
- node = it,
- isThisNode = true,
- isConnected = isConnected,
- onAction = onAction,
- )
+ ourNode?.let { node ->
+ NodeChip(modifier = Modifier.padding(horizontal = 16.dp), node = node, onClick = onClickChip)
}
}
@@ -189,7 +172,6 @@ private fun MainAppBarPreview(@PreviewParameter(BooleanProvider::class) canNavig
title = "Title",
subtitle = "Subtitle",
ourNode = previewNode,
- isConnected = false,
showNodeChip = true,
canNavigateUp = canNavigateUp,
onNavigateUp = {},
diff --git a/app/src/main/java/com/geeksville/mesh/ui/connections/ConnectionsScreen.kt b/app/src/main/java/com/geeksville/mesh/ui/connections/ConnectionsScreen.kt
index 0efeffcc5..6f011199a 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/connections/ConnectionsScreen.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/connections/ConnectionsScreen.kt
@@ -71,14 +71,11 @@ import com.geeksville.mesh.ui.connections.components.ConnectionsSegmentedBar
import com.geeksville.mesh.ui.connections.components.CurrentlyConnectedInfo
import com.geeksville.mesh.ui.connections.components.NetworkDevices
import com.geeksville.mesh.ui.connections.components.UsbDevices
-import com.geeksville.mesh.ui.node.components.NodeMenuAction
import com.geeksville.mesh.ui.settings.components.SettingsItem
import com.geeksville.mesh.ui.settings.radio.RadioConfigViewModel
import com.geeksville.mesh.ui.settings.radio.components.PacketResponseStateDialog
-import com.geeksville.mesh.ui.sharing.SharedContactDialog
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import kotlinx.coroutines.delay
-import org.meshtastic.core.database.model.Node
import org.meshtastic.core.navigation.Route
import org.meshtastic.core.navigation.SettingsRoutes
import org.meshtastic.core.strings.R
@@ -177,27 +174,17 @@ fun ConnectionsScreen(
scanModel.setErrorText(context.getString(it, firmwareString))
}
}
- var showSharedContact by remember { mutableStateOf(null) }
- if (showSharedContact != null) {
- SharedContactDialog(contact = showSharedContact, onDismiss = { showSharedContact = null })
- }
Scaffold(
topBar = {
MainAppBar(
title = stringResource(R.string.connections),
ourNode = ourNode,
- isConnected = connectionState.isConnected(),
showNodeChip = ourNode != null && connectionState.isConnected(),
canNavigateUp = false,
onNavigateUp = {},
actions = {},
- onAction = { action ->
- when (action) {
- is NodeMenuAction.MoreDetails -> onClickNodeChip(action.node.num)
- else -> {}
- }
- },
+ onClickChip = { onClickNodeChip(it.num) },
)
},
) { paddingValues ->
@@ -221,7 +208,6 @@ fun ConnectionsScreen(
CurrentlyConnectedInfo(
node = node,
onNavigateToNodeDetails = onNavigateToNodeDetails,
- onSetShowSharedContact = { showSharedContact = it },
onClickDisconnect = { scanModel.disconnect() },
bluetoothRssi = bluetoothRssi,
)
diff --git a/app/src/main/java/com/geeksville/mesh/ui/connections/components/CurrentlyConnectedInfo.kt b/app/src/main/java/com/geeksville/mesh/ui/connections/components/CurrentlyConnectedInfo.kt
index 2cf036560..7efa867e2 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/connections/components/CurrentlyConnectedInfo.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/connections/components/CurrentlyConnectedInfo.kt
@@ -26,7 +26,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@@ -41,7 +40,6 @@ import com.geeksville.mesh.MeshProtos
import com.geeksville.mesh.PaxcountProtos
import com.geeksville.mesh.TelemetryProtos
import com.geeksville.mesh.ui.node.components.NodeChip
-import com.geeksville.mesh.ui.node.components.NodeMenuAction
import org.meshtastic.core.database.model.Node
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.MaterialBatteryInfo
@@ -54,7 +52,6 @@ import org.meshtastic.core.ui.theme.StatusColors.StatusRed
fun CurrentlyConnectedInfo(
node: Node,
onNavigateToNodeDetails: (Int) -> Unit,
- onSetShowSharedContact: (Node) -> Unit,
onClickDisconnect: () -> Unit,
modifier: Modifier = Modifier,
bluetoothRssi: Int? = null,
@@ -75,19 +72,7 @@ fun CurrentlyConnectedInfo(
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
- NodeChip(
- node = node,
- isThisNode = true,
- isConnected = true,
- onAction = { action ->
- when (action) {
- is NodeMenuAction.MoreDetails -> onNavigateToNodeDetails(node.num)
-
- is NodeMenuAction.Share -> onSetShowSharedContact(node)
- else -> {}
- }
- },
- )
+ NodeChip(node = node, onClick = { onNavigateToNodeDetails(it.num) })
}
Column(modifier = Modifier.weight(1f, fill = true)) {
@@ -124,26 +109,22 @@ fun CurrentlyConnectedInfo(
@Composable
private fun CurrentlyConnectedInfoPreview() {
AppTheme {
- Surface {
- CurrentlyConnectedInfo(
- node =
- Node(
- num = 13444,
- user =
- MeshProtos.User.newBuilder().setShortName("\uD83E\uDEE0").setLongName("John Doe").build(),
- isIgnored = false,
- paxcounter = PaxcountProtos.Paxcount.newBuilder().setBle(10).setWifi(5).build(),
- environmentMetrics =
- TelemetryProtos.EnvironmentMetrics.newBuilder()
- .setTemperature(25f)
- .setRelativeHumidity(60f)
- .build(),
- ),
- bluetoothRssi = -75, // Example RSSI for signal preview
- onNavigateToNodeDetails = {},
- onSetShowSharedContact = {},
- onClickDisconnect = {},
- )
- }
+ CurrentlyConnectedInfo(
+ node =
+ Node(
+ num = 13444,
+ user = MeshProtos.User.newBuilder().setShortName("\uD83E\uDEE0").setLongName("John Doe").build(),
+ isIgnored = false,
+ paxcounter = PaxcountProtos.Paxcount.newBuilder().setBle(10).setWifi(5).build(),
+ environmentMetrics =
+ TelemetryProtos.EnvironmentMetrics.newBuilder()
+ .setTemperature(25f)
+ .setRelativeHumidity(60f)
+ .build(),
+ ),
+ bluetoothRssi = -75, // Example RSSI for signal preview
+ onNavigateToNodeDetails = {},
+ onClickDisconnect = {},
+ )
}
}
diff --git a/app/src/main/java/com/geeksville/mesh/ui/contact/Contacts.kt b/app/src/main/java/com/geeksville/mesh/ui/contact/Contacts.kt
index e42148861..d7b126d74 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/contact/Contacts.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/contact/Contacts.kt
@@ -64,7 +64,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.AppOnlyProtos
import com.geeksville.mesh.model.Contact
import com.geeksville.mesh.ui.common.components.MainAppBar
-import com.geeksville.mesh.ui.node.components.NodeMenuAction
import org.meshtastic.core.strings.R
import java.util.concurrent.TimeUnit
@@ -144,17 +143,11 @@ fun ContactsScreen(
MainAppBar(
title = stringResource(R.string.conversations),
ourNode = ourNode,
- isConnected = connectionState.isConnected(),
showNodeChip = ourNode != null && connectionState.isConnected(),
canNavigateUp = false,
onNavigateUp = {},
actions = {},
- onAction = { action ->
- when (action) {
- is NodeMenuAction.MoreDetails -> onClickNodeChip(action.node.num)
- else -> {}
- }
- },
+ onClickChip = { onClickNodeChip(it.num) },
)
},
floatingActionButton = {
diff --git a/app/src/main/java/com/geeksville/mesh/ui/map/MapScreen.kt b/app/src/main/java/com/geeksville/mesh/ui/map/MapScreen.kt
index 1cecaf1aa..0279bd092 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/map/MapScreen.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/map/MapScreen.kt
@@ -27,7 +27,6 @@ import androidx.compose.ui.res.stringResource
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.ui.common.components.MainAppBar
-import com.geeksville.mesh.ui.node.components.NodeMenuAction
import org.meshtastic.core.strings.R
@Composable
@@ -47,17 +46,11 @@ fun MapScreen(
MainAppBar(
title = stringResource(R.string.map),
ourNode = ourNodeInfo,
- isConnected = isConnected,
showNodeChip = ourNodeInfo != null && isConnected,
canNavigateUp = false,
onNavigateUp = {},
actions = {},
- onAction = { action ->
- when (action) {
- is NodeMenuAction.MoreDetails -> onClickNodeChip(action.node.num)
- else -> {}
- }
- },
+ onClickChip = { onClickNodeChip(it.num) },
)
},
) { paddingValues ->
diff --git a/app/src/main/java/com/geeksville/mesh/ui/message/Message.kt b/app/src/main/java/com/geeksville/mesh/ui/message/Message.kt
index 4408e1003..c7cc300d1 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/message/Message.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/message/Message.kt
@@ -97,7 +97,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.AppOnlyProtos
import com.geeksville.mesh.ui.common.components.SecurityIcon
import com.geeksville.mesh.ui.node.components.NodeKeyStatusIcon
-import com.geeksville.mesh.ui.node.components.NodeMenuAction
import com.geeksville.mesh.ui.sharing.SharedContactDialog
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@@ -207,24 +206,7 @@ internal fun MessageScreen(
is MessageScreenEvent.ClearUnreadCount ->
viewModel.clearUnreadCount(contactKey, event.lastReadMessageId)
- is MessageScreenEvent.HandleNodeMenuAction -> {
- when (val action = event.action) {
- is NodeMenuAction.DirectMessage -> {
- val hasPKC = ourNode?.hasPKC == true && action.node.hasPKC
- val targetChannel =
- if (hasPKC) {
- DataPacket.PKC_CHANNEL_INDEX
- } else {
- action.node.channel
- }
- navigateToMessages("$targetChannel${action.node.user.id}")
- }
-
- is NodeMenuAction.MoreDetails -> navigateToNodeDetails(action.node.num)
- is NodeMenuAction.Share -> sharedContact = action.node
- else -> viewModel.handleNodeMenuAction(action)
- }
- }
+ is MessageScreenEvent.NodeDetails -> navigateToNodeDetails(event.node.num)
is MessageScreenEvent.SetTitle -> viewModel.setTitle(event.title)
is MessageScreenEvent.NavigateToMessages -> navigateToMessages(event.contactKey)
@@ -297,7 +279,6 @@ internal fun MessageScreen(
MessageList(
nodes = nodes,
ourNode = ourNode,
- isConnected = connectionState.isConnected(),
modifier = Modifier.fillMaxSize(),
listState = listState,
messages = messages,
@@ -308,7 +289,7 @@ internal fun MessageScreen(
onSendMessage = { text, contactKey -> viewModel.sendMessage(text, contactKey) },
contactKey = contactKey,
onReply = { message -> replyingToPacketId = message?.packetId },
- onNodeMenuAction = { action -> onEvent(MessageScreenEvent.HandleNodeMenuAction(action)) },
+ onClickChip = { onEvent(MessageScreenEvent.NodeDetails(it)) },
)
// Show FAB if we can scroll towards the newest messages (index 0).
if (listState.canScrollBackward) {
diff --git a/app/src/main/java/com/geeksville/mesh/ui/message/MessageList.kt b/app/src/main/java/com/geeksville/mesh/ui/message/MessageList.kt
index 65414a369..93fb72289 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/message/MessageList.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/message/MessageList.kt
@@ -48,7 +48,6 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.geeksville.mesh.ui.message.components.MessageItem
import com.geeksville.mesh.ui.message.components.ReactionDialog
-import com.geeksville.mesh.ui.node.components.NodeMenuAction
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.debounce
@@ -107,14 +106,13 @@ fun DeliveryInfo(
internal fun MessageList(
nodes: List,
ourNode: Node?,
- isConnected: Boolean,
modifier: Modifier = Modifier,
listState: LazyListState = rememberLazyListState(),
messages: List,
selectedIds: MutableState>,
onUnreadChanged: (Long) -> Unit,
onSendReaction: (String, Int) -> Unit,
- onNodeMenuAction: (NodeMenuAction) -> Unit,
+ onClickChip: (Node) -> Unit,
onDeleteMessages: (List) -> Unit,
onSendMessage: (messageText: String, contactKey: String) -> Unit,
contactKey: String,
@@ -173,13 +171,12 @@ internal fun MessageList(
selectedIds.toggle(msg.uuid)
haptics.performHapticFeedback(HapticFeedbackType.LongPress)
},
- onAction = onNodeMenuAction,
+ onClickChip = onClickChip,
onStatusClick = { showStatusDialog = msg },
onReply = { onReply(msg) },
emojis = msg.emojis,
sendReaction = { onSendReaction(it, msg.packetId) },
onShowReactions = { showReactionDialog = msg.emojis },
- isConnected = isConnected,
onNavigateToOriginalMessage = {
coroutineScope.launch {
val targetIndex = messages.indexOfFirst { it.packetId == msg.replyId }
diff --git a/app/src/main/java/com/geeksville/mesh/ui/message/MessageScreenEvent.kt b/app/src/main/java/com/geeksville/mesh/ui/message/MessageScreenEvent.kt
index 3c04c3b17..43552ae1e 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/message/MessageScreenEvent.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/message/MessageScreenEvent.kt
@@ -17,16 +17,15 @@
package com.geeksville.mesh.ui.message
-import com.geeksville.mesh.ui.node.components.NodeMenuAction
+import org.meshtastic.core.database.model.Node
/**
- * Defines the various user interactions that can occur on the [MessageScreen].
- * These events are typically handled by the [com.geeksville.mesh.model.UIViewModel].
+ * Defines the various user interactions that can occur on the [MessageScreen]. These events are typically handled by
+ * the [com.geeksville.mesh.model.UIViewModel].
*/
internal sealed interface MessageScreenEvent {
/** Send a new text message. */
- data class SendMessage(val text: String, val replyingToPacketId: Int? = null) :
- MessageScreenEvent
+ data class SendMessage(val text: String, val replyingToPacketId: Int? = null) : MessageScreenEvent
/** Send an emoji reaction to a specific message. */
data class SendReaction(val emoji: String, val messageId: Int) : MessageScreenEvent
@@ -38,7 +37,7 @@ internal sealed interface MessageScreenEvent {
data class ClearUnreadCount(val lastReadMessageId: Long) : MessageScreenEvent
/** Handle an action from a node's context menu. */
- data class HandleNodeMenuAction(val action: NodeMenuAction) : MessageScreenEvent
+ data class NodeDetails(val node: Node) : MessageScreenEvent
/** Set the title of the screen (typically the contact or channel name). */
data class SetTitle(val title: String) : MessageScreenEvent
diff --git a/app/src/main/java/com/geeksville/mesh/ui/message/MessageViewModel.kt b/app/src/main/java/com/geeksville/mesh/ui/message/MessageViewModel.kt
index ebeb49a39..a039b18e2 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/message/MessageViewModel.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/message/MessageViewModel.kt
@@ -24,7 +24,6 @@ import com.geeksville.mesh.channelSet
import com.geeksville.mesh.service.MeshServiceNotifications
import com.geeksville.mesh.service.ServiceAction
import com.geeksville.mesh.service.ServiceRepository
-import com.geeksville.mesh.ui.node.components.NodeMenuAction
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
@@ -43,7 +42,6 @@ import org.meshtastic.core.data.repository.RadioConfigRepository
import org.meshtastic.core.database.model.Message
import org.meshtastic.core.database.model.Node
import org.meshtastic.core.model.DataPacket
-import org.meshtastic.core.model.Position
import org.meshtastic.core.prefs.ui.UiPrefs
import timber.log.Timber
import javax.inject.Inject
@@ -102,10 +100,6 @@ constructor(
initialValue = emptyList(),
)
- // TODO this should be moved to a repository class
- private val _lastTraceRouteTime = MutableStateFlow(null)
- val lastTraceRouteTime: StateFlow = _lastTraceRouteTime.asStateFlow()
-
fun setTitle(title: String) {
viewModelScope.launch { _title.value = title }
}
@@ -157,22 +151,6 @@ constructor(
if (unreadCount == 0) meshServiceNotifications.cancelMessageNotification(contact)
}
- fun handleNodeMenuAction(action: NodeMenuAction) {
- when (action) {
- is NodeMenuAction.Remove -> removeNode(action.node.num)
- is NodeMenuAction.Ignore -> ignoreNode(action.node)
- is NodeMenuAction.Favorite -> favoriteNode(action.node)
- is NodeMenuAction.RequestUserInfo -> requestUserInfo(action.node.num)
- is NodeMenuAction.RequestPosition -> requestPosition(action.node.num)
- is NodeMenuAction.TraceRoute -> {
- requestTraceroute(action.node.num)
- _lastTraceRouteTime.value = System.currentTimeMillis()
- }
-
- else -> {}
- }
- }
-
private fun favoriteNode(node: Node) = viewModelScope.launch {
try {
serviceRepository.onServiceAction(ServiceAction.Favorite(node))
@@ -188,51 +166,4 @@ constructor(
Timber.e("Send DataPacket error: ${ex.message}")
}
}
-
- private fun removeNode(nodeNum: Int) = viewModelScope.launch(Dispatchers.IO) {
- Timber.i("Removing node '$nodeNum'")
- try {
- val packetId = serviceRepository.meshService?.packetId ?: return@launch
- serviceRepository.meshService?.removeByNodenum(packetId, nodeNum)
- nodeRepository.deleteNode(nodeNum)
- } catch (ex: RemoteException) {
- Timber.e("Remove node error: ${ex.message}")
- }
- }
-
- private fun ignoreNode(node: Node) = viewModelScope.launch {
- try {
- serviceRepository.onServiceAction(ServiceAction.Ignore(node))
- } catch (ex: RemoteException) {
- Timber.e(ex, "Ignore node error:")
- }
- }
-
- private fun requestUserInfo(destNum: Int) {
- Timber.i("Requesting UserInfo for '$destNum'")
- try {
- serviceRepository.meshService?.requestUserInfo(destNum)
- } catch (ex: RemoteException) {
- Timber.e("Request NodeInfo error: ${ex.message}")
- }
- }
-
- fun requestPosition(destNum: Int, position: Position = Position(0.0, 0.0, 0)) {
- Timber.i("Requesting position for '$destNum'")
- try {
- serviceRepository.meshService?.requestPosition(destNum, position)
- } catch (ex: RemoteException) {
- Timber.e("Request position error: ${ex.message}")
- }
- }
-
- fun requestTraceroute(destNum: Int) {
- Timber.i("Requesting traceroute for '$destNum'")
- try {
- val packetId = serviceRepository.meshService?.packetId ?: return
- serviceRepository.meshService?.requestTraceroute(packetId, destNum)
- } catch (ex: RemoteException) {
- Timber.e("Request traceroute error: ${ex.message}")
- }
- }
}
diff --git a/app/src/main/java/com/geeksville/mesh/ui/message/components/MessageItem.kt b/app/src/main/java/com/geeksville/mesh/ui/message/components/MessageItem.kt
index 816691774..9701392c4 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/message/components/MessageItem.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/message/components/MessageItem.kt
@@ -51,7 +51,6 @@ import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.unit.dp
import com.geeksville.mesh.ui.common.preview.NodePreviewParameterProvider
import com.geeksville.mesh.ui.node.components.NodeChip
-import com.geeksville.mesh.ui.node.components.NodeMenuAction
import org.meshtastic.core.database.entity.Reaction
import org.meshtastic.core.database.model.Message
import org.meshtastic.core.database.model.Node
@@ -77,9 +76,8 @@ internal fun MessageItem(
emojis: List = emptyList(),
onClick: () -> Unit = {},
onLongClick: () -> Unit = {},
- onAction: (NodeMenuAction) -> Unit = {},
+ onClickChip: (Node) -> Unit = {},
onStatusClick: () -> Unit = {},
- isConnected: Boolean,
onNavigateToOriginalMessage: (Int) -> Unit = {},
) = Column(
modifier =
@@ -134,12 +132,8 @@ internal fun MessageItem(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp),
) {
- NodeChip(
- node = if (message.fromLocal) ourNode else node,
- onAction = onAction,
- isConnected = isConnected,
- isThisNode = message.fromLocal,
- )
+ val chipNode = if (message.fromLocal) ourNode else node
+ NodeChip(node = chipNode, onClick = onClickChip)
Text(
text = with(if (message.fromLocal) ourNode.user else node.user) { "$longName ($id)" },
overflow = TextOverflow.Ellipsis,
@@ -325,7 +319,6 @@ private fun MessageItemPreview() {
onClick = {},
onLongClick = {},
onStatusClick = {},
- isConnected = true,
ourNode = sent.node,
)
@@ -336,7 +329,6 @@ private fun MessageItemPreview() {
onClick = {},
onLongClick = {},
onStatusClick = {},
- isConnected = true,
ourNode = sent.node,
)
@@ -347,7 +339,6 @@ private fun MessageItemPreview() {
onClick = {},
onLongClick = {},
onStatusClick = {},
- isConnected = true,
ourNode = sent.node,
)
}
diff --git a/app/src/main/java/com/geeksville/mesh/ui/node/NodeDetail.kt b/app/src/main/java/com/geeksville/mesh/ui/node/NodeDetail.kt
index 8640534bb..d0ae1f6e6 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/node/NodeDetail.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/node/NodeDetail.kt
@@ -196,7 +196,6 @@ fun NodeDetailScreen(
val environmentState by viewModel.environmentState.collectAsStateWithLifecycle()
val lastTracerouteTime by nodeDetailViewModel.lastTraceRouteTime.collectAsStateWithLifecycle()
val ourNode by nodeDetailViewModel.ourNodeInfo.collectAsStateWithLifecycle()
- val connectionState by nodeDetailViewModel.connectionState.collectAsStateWithLifecycle()
val availableLogs by
remember(state, environmentState) {
@@ -225,12 +224,11 @@ fun NodeDetailScreen(
MainAppBar(
title = node?.user?.longName ?: "",
ourNode = ourNode,
- isConnected = connectionState.isConnected(),
showNodeChip = false,
canNavigateUp = true,
onNavigateUp = onNavigateUp,
actions = {},
- onAction = {},
+ onClickChip = {},
)
},
) { paddingValues ->
@@ -642,7 +640,9 @@ private fun DeviceActions(
displayIgnoreDialog = false
displayRemoveDialog = false
},
- onAction = { onAction(NodeDetailAction.HandleNodeMenuAction(it)) },
+ onConfirmFavorite = { onAction(NodeDetailAction.HandleNodeMenuAction(NodeMenuAction.Favorite(it))) },
+ onConfirmIgnore = { onAction(NodeDetailAction.HandleNodeMenuAction(NodeMenuAction.Ignore(it))) },
+ onConfirmRemove = { onAction(NodeDetailAction.HandleNodeMenuAction(NodeMenuAction.Remove(it))) },
)
TitledCard(title = stringResource(R.string.actions)) {
SettingsItem(
diff --git a/app/src/main/java/com/geeksville/mesh/ui/node/NodeDetailViewModel.kt b/app/src/main/java/com/geeksville/mesh/ui/node/NodeDetailViewModel.kt
index 66297526b..420530427 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/node/NodeDetailViewModel.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/node/NodeDetailViewModel.kt
@@ -45,8 +45,6 @@ constructor(
val ourNodeInfo: StateFlow = nodeRepository.ourNodeInfo
- val connectionState = serviceRepository.connectionState
-
private val _lastTraceRouteTime = MutableStateFlow(null)
val lastTraceRouteTime: StateFlow = _lastTraceRouteTime.asStateFlow()
diff --git a/app/src/main/java/com/geeksville/mesh/ui/node/NodeScreen.kt b/app/src/main/java/com/geeksville/mesh/ui/node/NodeScreen.kt
index 4c03504a1..a17293996 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/node/NodeScreen.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/node/NodeScreen.kt
@@ -29,9 +29,20 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.DoDisturbOn
+import androidx.compose.material.icons.outlined.DoDisturbOn
+import androidx.compose.material.icons.rounded.DeleteOutline
+import androidx.compose.material.icons.rounded.Star
+import androidx.compose.material.icons.rounded.StarBorder
+import androidx.compose.material3.DropdownMenu
+import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
+import androidx.compose.material3.Icon
+import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
import androidx.compose.material3.animateFloatingActionButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
@@ -41,34 +52,31 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.AdminProtos
import com.geeksville.mesh.service.ConnectionState
import com.geeksville.mesh.ui.common.components.MainAppBar
+import com.geeksville.mesh.ui.node.components.NodeActionDialogs
import com.geeksville.mesh.ui.node.components.NodeFilterTextField
import com.geeksville.mesh.ui.node.components.NodeItem
-import com.geeksville.mesh.ui.node.components.NodeMenuAction
import com.geeksville.mesh.ui.sharing.AddContactFAB
-import com.geeksville.mesh.ui.sharing.SharedContactDialog
import com.geeksville.mesh.ui.sharing.supportsQrCodeSharing
import org.meshtastic.core.database.model.Node
-import org.meshtastic.core.model.DataPacket
import org.meshtastic.core.model.DeviceVersion
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.rememberTimeTickWithLifecycle
+import org.meshtastic.core.ui.theme.StatusColors.StatusRed
@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3ExpressiveApi::class)
@Suppress("LongMethod", "CyclomaticComplexMethod")
@Composable
-fun NodeScreen(
- nodesViewModel: NodesViewModel = hiltViewModel(),
- navigateToMessages: (String) -> Unit,
- navigateToNodeDetails: (Int) -> Unit,
-) {
+fun NodeScreen(nodesViewModel: NodesViewModel = hiltViewModel(), navigateToNodeDetails: (Int) -> Unit) {
val state by nodesViewModel.nodesUiState.collectAsStateWithLifecycle()
val nodes by nodesViewModel.nodeList.collectAsStateWithLifecycle()
@@ -83,11 +91,6 @@ fun NodeScreen(
val currentTimeMillis = rememberTimeTickWithLifecycle()
val connectionState by nodesViewModel.connectionState.collectAsStateWithLifecycle()
- var showSharedContact: Node? by remember { mutableStateOf(null) }
- if (showSharedContact != null) {
- SharedContactDialog(contact = showSharedContact, onDismiss = { showSharedContact = null })
- }
-
val isScrollInProgress by remember { derivedStateOf { listState.isScrollInProgress } }
Scaffold(
topBar = {
@@ -95,12 +98,11 @@ fun NodeScreen(
title = stringResource(R.string.nodes),
subtitle = stringResource(R.string.node_count_template, onlineNodeCount, totalNodeCount),
ourNode = ourNode,
- isConnected = connectionState.isConnected(),
showNodeChip = false,
canNavigateUp = false,
onNavigateUp = {},
actions = {},
- onAction = {},
+ onClickChip = {},
)
},
floatingActionButton = {
@@ -151,37 +153,124 @@ fun NodeScreen(
}
items(nodes, key = { it.num }) { node ->
- NodeItem(
- modifier = Modifier.animateItem(),
- thisNode = ourNode,
- thatNode = node,
- distanceUnits = state.distanceUnits,
- tempInFahrenheit = state.tempInFahrenheit,
- onAction = { menuItem ->
- when (menuItem) {
- is NodeMenuAction.Remove -> nodesViewModel.removeNode(node.num)
- is NodeMenuAction.Ignore -> nodesViewModel.ignoreNode(node)
- is NodeMenuAction.Favorite -> nodesViewModel.favoriteNode(node)
- is NodeMenuAction.DirectMessage -> {
- val hasPKC = nodesViewModel.ourNodeInfo.value?.hasPKC == true && node.hasPKC
- val channel = if (hasPKC) DataPacket.PKC_CHANNEL_INDEX else node.channel
- navigateToMessages("$channel${node.user.id}")
- }
+ var displayFavoriteDialog by remember { mutableStateOf(false) }
+ var displayIgnoreDialog by remember { mutableStateOf(false) }
+ var displayRemoveDialog by remember { mutableStateOf(false) }
- is NodeMenuAction.RequestUserInfo -> nodesViewModel.requestUserInfo(node.num)
- is NodeMenuAction.RequestPosition -> nodesViewModel.requestPosition(node.num)
- is NodeMenuAction.TraceRoute -> nodesViewModel.requestTraceroute(node.num)
- is NodeMenuAction.MoreDetails -> navigateToNodeDetails(node.num)
- is NodeMenuAction.Share -> showSharedContact = node
- }
+ NodeActionDialogs(
+ node = node,
+ displayFavoriteDialog = displayFavoriteDialog,
+ displayIgnoreDialog = displayIgnoreDialog,
+ displayRemoveDialog = displayRemoveDialog,
+ onDismissMenuRequest = {
+ displayFavoriteDialog = false
+ displayIgnoreDialog = false
+ displayRemoveDialog = false
},
- expanded = state.showDetails,
- currentTimeMillis = currentTimeMillis,
- isConnected = connectionState.isConnected(),
+ onConfirmFavorite = nodesViewModel::favoriteNode,
+ onConfirmIgnore = nodesViewModel::ignoreNode,
+ onConfirmRemove = { nodesViewModel.removeNode(it.num) },
)
+
+ Box {
+ var showContextMenu by remember { mutableStateOf(false) }
+
+ NodeItem(
+ modifier = Modifier.animateItem(),
+ thisNode = ourNode,
+ thatNode = node,
+ distanceUnits = state.distanceUnits,
+ tempInFahrenheit = state.tempInFahrenheit,
+ onClickChip = { navigateToNodeDetails(it.num) },
+ onLongClick = { showContextMenu = true },
+ expanded = state.showDetails,
+ currentTimeMillis = currentTimeMillis,
+ isConnected = connectionState.isConnected(),
+ )
+ val isThisNode = remember(node) { ourNode?.num == node.num }
+ ContextMenu(
+ expanded = !isThisNode && showContextMenu,
+ node = node,
+ onClickFavorite = { displayFavoriteDialog = true },
+ onClickIgnore = { displayIgnoreDialog = true },
+ onClickRemove = { displayRemoveDialog = true },
+ onDismiss = { showContextMenu = false },
+ )
+ }
}
item { Spacer(modifier = Modifier.height(88.dp)) }
}
}
}
}
+
+@Composable
+private fun ContextMenu(
+ expanded: Boolean,
+ node: Node,
+ onClickFavorite: (Node) -> Unit,
+ onClickIgnore: (Node) -> Unit,
+ onClickRemove: (Node) -> Unit,
+ onDismiss: () -> Unit,
+) {
+ DropdownMenu(expanded = expanded, onDismissRequest = onDismiss, offset = DpOffset(16.dp, 0.dp)) {
+ val isFavorite = node.isFavorite
+ val isIgnored = node.isIgnored
+
+ DropdownMenuItem(
+ onClick = {
+ onClickFavorite(node)
+ onDismiss()
+ },
+ enabled = !isIgnored,
+ leadingIcon = {
+ Icon(
+ imageVector = if (isFavorite) Icons.Rounded.Star else Icons.Rounded.StarBorder,
+ contentDescription = null,
+ )
+ },
+ text = { Text(stringResource(if (isFavorite) R.string.remove_favorite else R.string.add_favorite)) },
+ )
+
+ DropdownMenuItem(
+ onClick = {
+ onClickIgnore(node)
+ onDismiss()
+ },
+ leadingIcon = {
+ Icon(
+ imageVector = if (isIgnored) Icons.Filled.DoDisturbOn else Icons.Outlined.DoDisturbOn,
+ contentDescription = null,
+ tint = MaterialTheme.colorScheme.StatusRed,
+ )
+ },
+ text = {
+ Text(
+ text = stringResource(if (isIgnored) R.string.remove_ignored else R.string.ignore),
+ color = MaterialTheme.colorScheme.StatusRed,
+ )
+ },
+ )
+
+ DropdownMenuItem(
+ onClick = {
+ onClickRemove(node)
+ onDismiss()
+ },
+ enabled = !isIgnored,
+ leadingIcon = {
+ Icon(
+ imageVector = Icons.Rounded.DeleteOutline,
+ contentDescription = null,
+ tint = if (isIgnored) LocalContentColor.current else MaterialTheme.colorScheme.StatusRed,
+ )
+ },
+ text = {
+ Text(
+ text = stringResource(R.string.remove),
+ color = if (isIgnored) Color.Unspecified else MaterialTheme.colorScheme.StatusRed,
+ )
+ },
+ )
+ }
+}
diff --git a/app/src/main/java/com/geeksville/mesh/ui/node/NodesViewModel.kt b/app/src/main/java/com/geeksville/mesh/ui/node/NodesViewModel.kt
index c80adffb8..c5d5af0fe 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/node/NodesViewModel.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/node/NodesViewModel.kt
@@ -40,7 +40,6 @@ import org.meshtastic.core.data.repository.NodeRepository
import org.meshtastic.core.data.repository.RadioConfigRepository
import org.meshtastic.core.database.model.Node
import org.meshtastic.core.database.model.NodeSortOption
-import org.meshtastic.core.model.Position
import org.meshtastic.core.prefs.ui.UiPrefs
import timber.log.Timber
import javax.inject.Inject
@@ -170,22 +169,14 @@ constructor(
fun addSharedContact(sharedContact: AdminProtos.SharedContact) =
viewModelScope.launch { serviceRepository.onServiceAction(ServiceAction.AddSharedContact(sharedContact)) }
- fun removeNode(nodeNum: Int) = viewModelScope.launch(Dispatchers.IO) {
- Timber.i("Removing node '$nodeNum'")
- try {
- val packetId = serviceRepository.meshService?.packetId ?: return@launch
- serviceRepository.meshService?.removeByNodenum(packetId, nodeNum)
- nodeRepository.deleteNode(nodeNum)
- } catch (ex: RemoteException) {
- Timber.e("Remove node error: ${ex.message}")
- }
+ fun setSharedContactRequested(sharedContact: AdminProtos.SharedContact?) {
+ _sharedContactRequested.value = sharedContact
}
- fun ignoreNode(node: Node) = viewModelScope.launch {
- try {
- serviceRepository.onServiceAction(ServiceAction.Ignore(node))
- } catch (ex: RemoteException) {
- Timber.e(ex, "Ignore node error")
+ private fun toggle(state: MutableStateFlow, onChanged: (newValue: Boolean) -> Unit) {
+ (!state.value).let { toggled ->
+ state.update { toggled }
+ onChanged(toggled)
}
}
@@ -197,42 +188,22 @@ constructor(
}
}
- fun requestUserInfo(destNum: Int) {
- Timber.i("Requesting UserInfo for '$destNum'")
+ fun ignoreNode(node: Node) = viewModelScope.launch {
try {
- serviceRepository.meshService?.requestUserInfo(destNum)
+ serviceRepository.onServiceAction(ServiceAction.Ignore(node))
} catch (ex: RemoteException) {
- Timber.e("Request NodeInfo error: ${ex.message}")
+ Timber.e(ex, "Ignore node error")
}
}
- fun requestPosition(destNum: Int, position: Position = Position(0.0, 0.0, 0)) {
- Timber.i("Requesting position for '$destNum'")
+ fun removeNode(nodeNum: Int) = viewModelScope.launch(Dispatchers.IO) {
+ Timber.i("Removing node '$nodeNum'")
try {
- serviceRepository.meshService?.requestPosition(destNum, position)
+ val packetId = serviceRepository.meshService?.packetId ?: return@launch
+ serviceRepository.meshService?.removeByNodenum(packetId, nodeNum)
+ nodeRepository.deleteNode(nodeNum)
} catch (ex: RemoteException) {
- Timber.e("Request position error: ${ex.message}")
- }
- }
-
- fun requestTraceroute(destNum: Int) {
- Timber.i("Requesting traceroute for '$destNum'")
- try {
- val packetId = serviceRepository.meshService?.packetId ?: return
- serviceRepository.meshService?.requestTraceroute(packetId, destNum)
- } catch (ex: RemoteException) {
- Timber.e("Request traceroute error: ${ex.message}")
- }
- }
-
- fun setSharedContactRequested(sharedContact: AdminProtos.SharedContact?) {
- _sharedContactRequested.value = sharedContact
- }
-
- private fun toggle(state: MutableStateFlow, onChanged: (newValue: Boolean) -> Unit) {
- (!state.value).let { toggled ->
- state.update { toggled }
- onChanged(toggled)
+ Timber.e("Remove node error: ${ex.message}")
}
}
}
diff --git a/app/src/main/java/com/geeksville/mesh/ui/node/components/NodeChip.kt b/app/src/main/java/com/geeksville/mesh/ui/node/components/NodeChip.kt
index 5ba9516d1..3e17388e5 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/node/components/NodeChip.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/node/components/NodeChip.kt
@@ -17,9 +17,7 @@
package com.geeksville.mesh.ui.node.components
-import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.interaction.MutableInteractionSource
-import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxWidth
@@ -29,10 +27,7 @@ import androidx.compose.material3.ElevatedAssistChip
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.semantics.contentDescription
@@ -47,65 +42,33 @@ import com.geeksville.mesh.TelemetryProtos
import org.meshtastic.core.database.model.Node
@Composable
-fun NodeChip(
- modifier: Modifier = Modifier,
- enabled: Boolean = true,
- node: Node,
- isThisNode: Boolean,
- isConnected: Boolean,
- onAction: (NodeMenuAction) -> Unit,
-) {
+fun NodeChip(modifier: Modifier = Modifier, node: Node, onClick: (Node) -> Unit = {}) {
val isIgnored = node.isIgnored
val (textColor, nodeColor) = node.colors
- var menuExpanded by remember { mutableStateOf(false) }
val inputChipInteractionSource = remember { MutableInteractionSource() }
- Box {
- ElevatedAssistChip(
- modifier =
- modifier.width(IntrinsicSize.Min).defaultMinSize(minWidth = 72.dp).semantics {
- contentDescription = node.user.shortName.ifEmpty { "Node" }
- },
- elevation = AssistChipDefaults.elevatedAssistChipElevation(),
- colors =
- AssistChipDefaults.elevatedAssistChipColors(
- containerColor = Color(nodeColor),
- labelColor = Color(textColor),
- ),
- label = {
- Text(
- modifier = Modifier.fillMaxWidth(),
- text = node.user.shortName.ifEmpty { "???" },
- fontSize = MaterialTheme.typography.labelLarge.fontSize,
- textDecoration = TextDecoration.LineThrough.takeIf { isIgnored },
- textAlign = TextAlign.Center,
- maxLines = 1,
- )
- },
- onClick = {},
- interactionSource = inputChipInteractionSource,
- )
- Box(
- modifier =
- Modifier.matchParentSize()
- .combinedClickable(
- enabled = enabled,
- onClick = { onAction(NodeMenuAction.MoreDetails(node)) },
- onLongClick = { menuExpanded = true },
- interactionSource = inputChipInteractionSource,
- indication = null,
- )
- .semantics { contentDescription = node.user.shortName.ifEmpty { "Node" } },
- )
- }
- NodeMenu(
- expanded = menuExpanded,
- node = node,
- showFullMenu = !isThisNode && isConnected,
- onDismissMenuRequest = { menuExpanded = false },
- onAction = {
- menuExpanded = false
- onAction(it)
+ ElevatedAssistChip(
+ modifier =
+ modifier.width(IntrinsicSize.Min).defaultMinSize(minWidth = 72.dp).semantics {
+ contentDescription = node.user.shortName.ifEmpty { "Node" }
},
+ elevation = AssistChipDefaults.elevatedAssistChipElevation(),
+ colors =
+ AssistChipDefaults.elevatedAssistChipColors(
+ containerColor = Color(nodeColor),
+ labelColor = Color(textColor),
+ ),
+ label = {
+ Text(
+ modifier = Modifier.fillMaxWidth(),
+ text = node.user.shortName.ifEmpty { "???" },
+ fontSize = MaterialTheme.typography.labelLarge.fontSize,
+ textDecoration = TextDecoration.LineThrough.takeIf { isIgnored },
+ textAlign = TextAlign.Center,
+ maxLines = 1,
+ )
+ },
+ onClick = { onClick(node) },
+ interactionSource = inputChipInteractionSource,
)
}
@@ -123,5 +86,5 @@ fun NodeChipPreview() {
environmentMetrics =
TelemetryProtos.EnvironmentMetrics.newBuilder().setTemperature(25f).setRelativeHumidity(60f).build(),
)
- NodeChip(node = node, isThisNode = false, isConnected = true, onAction = {})
+ NodeChip(node = node)
}
diff --git a/app/src/main/java/com/geeksville/mesh/ui/node/components/NodeItem.kt b/app/src/main/java/com/geeksville/mesh/ui/node/components/NodeItem.kt
index d7cd1dce9..46d0be8c3 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/node/components/NodeItem.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/node/components/NodeItem.kt
@@ -18,6 +18,7 @@
package com.geeksville.mesh.ui.node.components
import android.content.res.Configuration
+import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -68,7 +69,8 @@ fun NodeItem(
distanceUnits: Int,
tempInFahrenheit: Boolean,
modifier: Modifier = Modifier,
- onAction: (NodeMenuAction) -> Unit = {},
+ onClickChip: (Node) -> Unit = {},
+ onLongClick: () -> Unit = {},
expanded: Boolean = false,
currentTimeMillis: Long,
isConnected: Boolean = false,
@@ -123,13 +125,16 @@ fun NodeItem(
Card(
modifier =
- modifier.fillMaxWidth().padding(horizontal = 8.dp, vertical = 4.dp).defaultMinSize(minHeight = 80.dp),
- onClick = { showDetails(!detailsShown) },
+ modifier
+ .combinedClickable(onClick = { showDetails(!detailsShown) }, onLongClick = onLongClick)
+ .fillMaxWidth()
+ .padding(horizontal = 8.dp, vertical = 4.dp)
+ .defaultMinSize(minHeight = 80.dp),
colors = cardColors,
) {
Column(modifier = Modifier.fillMaxWidth().padding(8.dp)) {
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
- NodeChip(node = thatNode, isThisNode = isThisNode, isConnected = isConnected, onAction = onAction)
+ NodeChip(node = thatNode, onClick = onClickChip)
NodeKeyStatusIcon(
hasPKC = thatNode.hasPKC,
diff --git a/app/src/main/java/com/geeksville/mesh/ui/node/components/NodeMenu.kt b/app/src/main/java/com/geeksville/mesh/ui/node/components/NodeMenu.kt
index e2d397c3b..6f9a98389 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/node/components/NodeMenu.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/node/components/NodeMenu.kt
@@ -17,166 +17,12 @@
package com.geeksville.mesh.ui.node.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
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.unit.dp
import org.meshtastic.core.database.model.Node
-import org.meshtastic.core.database.model.isUnmessageableRole
import org.meshtastic.core.strings.R
import org.meshtastic.core.ui.component.SimpleAlertDialog
-@Suppress("LongMethod")
-@Composable
-fun NodeMenu(
- expanded: Boolean,
- node: Node,
- showFullMenu: Boolean = false,
- onDismissMenuRequest: () -> Unit,
- onAction: (NodeMenuAction) -> Unit,
-) {
- val isUnmessageable =
- if (node.user.hasIsUnmessagable()) {
- node.user.isUnmessagable
- } else {
- // for older firmwares
- node.user.role?.isUnmessageableRole() == true
- }
-
- var displayFavoriteDialog by remember { mutableStateOf(false) }
- var displayIgnoreDialog by remember { mutableStateOf(false) }
- var displayRemoveDialog by remember { mutableStateOf(false) }
- val dialogDismissRequest = {
- displayFavoriteDialog = false
- displayIgnoreDialog = false
- displayRemoveDialog = false
- onDismissMenuRequest()
- }
- val onMenuAction: (NodeMenuAction) -> Unit = {
- dialogDismissRequest()
- onDismissMenuRequest()
- onAction(it)
- }
- NodeActionDialogs(
- node = node,
- displayFavoriteDialog = displayFavoriteDialog,
- displayIgnoreDialog = displayIgnoreDialog,
- displayRemoveDialog = displayRemoveDialog,
- onDismissMenuRequest = dialogDismissRequest,
- onAction = onMenuAction,
- )
- DropdownMenu(
- modifier = Modifier.background(MaterialTheme.colorScheme.background.copy(alpha = 1f)),
- expanded = expanded,
- onDismissRequest = onDismissMenuRequest,
- ) {
- if (showFullMenu) {
- if (!isUnmessageable) {
- DropdownMenuItem(
- onClick = {
- dialogDismissRequest()
- onMenuAction(NodeMenuAction.DirectMessage(node))
- },
- text = { Text(stringResource(R.string.direct_message)) },
- )
- }
- DropdownMenuItem(
- onClick = {
- dialogDismissRequest()
- onMenuAction(NodeMenuAction.RequestUserInfo(node))
- },
- text = { Text(stringResource(R.string.exchange_userinfo)) },
- )
- DropdownMenuItem(
- onClick = {
- dialogDismissRequest()
- onMenuAction(NodeMenuAction.RequestPosition(node))
- },
- text = { Text(stringResource(R.string.exchange_position)) },
- )
- DropdownMenuItem(
- onClick = {
- dialogDismissRequest()
- onMenuAction(NodeMenuAction.TraceRoute(node))
- },
- text = { Text(stringResource(R.string.traceroute)) },
- )
- DropdownMenuItem(
- onClick = {
- dialogDismissRequest()
- displayFavoriteDialog = true
- },
- enabled = !node.isIgnored,
- text = { Text(stringResource(R.string.favorite)) },
- trailingIcon = {
- Icon(
- imageVector = if (node.isFavorite) Icons.Filled.Star else Icons.TwoTone.StarBorder,
- contentDescription = stringResource(R.string.favorite),
- )
- },
- )
- DropdownMenuItem(
- onClick = {
- dialogDismissRequest()
- displayIgnoreDialog = true
- },
- text = { Text(stringResource(R.string.ignore)) },
- trailingIcon = {
- Checkbox(
- checked = node.isIgnored,
- onCheckedChange = {
- dialogDismissRequest()
- displayIgnoreDialog = true
- },
- modifier = Modifier.size(24.dp),
- )
- },
- )
- DropdownMenuItem(
- onClick = {
- dialogDismissRequest()
- displayRemoveDialog = true
- },
- enabled = !node.isIgnored,
- text = { Text(stringResource(R.string.remove)) },
- )
- HorizontalDivider(Modifier.padding(vertical = 8.dp))
- }
- DropdownMenuItem(
- onClick = {
- dialogDismissRequest()
- onMenuAction(NodeMenuAction.Share(node))
- },
- text = { Text(stringResource(R.string.share_contact)) },
- )
-
- DropdownMenuItem(
- onClick = {
- dialogDismissRequest()
- onMenuAction(NodeMenuAction.MoreDetails(node))
- },
- text = { Text(stringResource(R.string.more_details)) },
- )
- }
-}
-
@Composable
fun NodeActionDialogs(
node: Node,
@@ -184,7 +30,9 @@ fun NodeActionDialogs(
displayIgnoreDialog: Boolean,
displayRemoveDialog: Boolean,
onDismissMenuRequest: () -> Unit,
- onAction: (NodeMenuAction) -> Unit,
+ onConfirmFavorite: (Node) -> Unit,
+ onConfirmIgnore: (Node) -> Unit,
+ onConfirmRemove: (Node) -> Unit,
) {
if (displayFavoriteDialog) {
SimpleAlertDialog(
@@ -196,7 +44,7 @@ fun NodeActionDialogs(
),
onConfirm = {
onDismissMenuRequest()
- onAction(NodeMenuAction.Favorite(node))
+ onConfirmFavorite(node)
},
onDismiss = onDismissMenuRequest,
)
@@ -211,7 +59,7 @@ fun NodeActionDialogs(
),
onConfirm = {
onDismissMenuRequest()
- onAction(NodeMenuAction.Ignore(node))
+ onConfirmIgnore(node)
},
onDismiss = onDismissMenuRequest,
)
@@ -222,7 +70,7 @@ fun NodeActionDialogs(
text = R.string.remove_node_text,
onConfirm = {
onDismissMenuRequest()
- onAction(NodeMenuAction.Remove(node))
+ onConfirmRemove(node)
},
onDismiss = onDismissMenuRequest,
)
diff --git a/app/src/main/java/com/geeksville/mesh/ui/settings/SettingsScreen.kt b/app/src/main/java/com/geeksville/mesh/ui/settings/SettingsScreen.kt
index b652c3379..011780fcb 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/settings/SettingsScreen.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/settings/SettingsScreen.kt
@@ -60,7 +60,6 @@ import com.geeksville.mesh.ClientOnlyProtos.DeviceProfile
import com.geeksville.mesh.android.gpsDisabled
import com.geeksville.mesh.navigation.getNavRouteFrom
import com.geeksville.mesh.ui.common.components.MainAppBar
-import com.geeksville.mesh.ui.node.components.NodeMenuAction
import com.geeksville.mesh.ui.settings.components.SettingsItem
import com.geeksville.mesh.ui.settings.components.SettingsItemDetail
import com.geeksville.mesh.ui.settings.components.SettingsItemSwitch
@@ -194,17 +193,11 @@ fun SettingsScreen(
stringResource(R.string.remotely_administrating, remoteName)
},
ourNode = ourNode,
- isConnected = isConnected,
showNodeChip = ourNode != null && isConnected && state.isLocal,
canNavigateUp = false,
onNavigateUp = {},
actions = {},
- onAction = { action ->
- when (action) {
- is NodeMenuAction.MoreDetails -> onClickNodeChip(action.node.num)
- else -> {}
- }
- },
+ onClickChip = { onClickNodeChip(it.num) },
)
},
) { paddingValues ->
diff --git a/app/src/main/java/com/geeksville/mesh/ui/settings/radio/CleanNodeDatabaseScreen.kt b/app/src/main/java/com/geeksville/mesh/ui/settings/radio/CleanNodeDatabaseScreen.kt
index de87030c9..64dc57abc 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/settings/radio/CleanNodeDatabaseScreen.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/settings/radio/CleanNodeDatabaseScreen.kt
@@ -173,12 +173,7 @@ private fun NodesDeletionPreview(nodesToDelete: List) {
verticalArrangement = Arrangement.Center,
) {
nodesToDelete.forEach { node ->
- NodeChip(
- node = node.toModel(),
- modifier = Modifier.padding(end = 8.dp, bottom = 8.dp),
- isThisNode = false,
- isConnected = false,
- ) {}
+ NodeChip(node = node.toModel(), modifier = Modifier.padding(end = 8.dp, bottom = 8.dp))
}
}
}
diff --git a/app/src/main/java/com/geeksville/mesh/ui/settings/radio/components/RadioConfigScreenList.kt b/app/src/main/java/com/geeksville/mesh/ui/settings/radio/components/RadioConfigScreenList.kt
index d959d1e30..9f6e32395 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/settings/radio/components/RadioConfigScreenList.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/settings/radio/components/RadioConfigScreenList.kt
@@ -59,10 +59,9 @@ fun RadioConfigScreenList(
canNavigateUp = true,
onNavigateUp = onBack,
ourNode = null,
- isConnected = false,
showNodeChip = false,
actions = {},
- onAction = {},
+ onClickChip = {},
)
},
) { innerPadding ->
diff --git a/core/strings/src/main/res/values/strings.xml b/core/strings/src/main/res/values/strings.xml
index af9032825..38b0d30d4 100644
--- a/core/strings/src/main/res/values/strings.xml
+++ b/core/strings/src/main/res/values/strings.xml
@@ -297,6 +297,7 @@
Delivery confirmed
Error
Ignore
+ Remove from ignored
Add \'%s\' to ignore list?
Remove \'%s\' from ignore list?
Select download region
@@ -397,6 +398,8 @@
Alert Bell Character!
Critical Alert!
Favorite
+ Add to favorites
+ Remove from favorites
Add \'%s\' as a favorite node?
Remove \'%s\' as a favorite node?
Power Metrics Log