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