Remove NodeChip long-click (#3220)

This commit is contained in:
Phil Oliver 2025-09-29 10:31:55 -04:00 committed by GitHub
parent 98ef72d240
commit 48a27ba022
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 248 additions and 588 deletions

View file

@ -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(

View file

@ -45,8 +45,6 @@ constructor(
val ourNodeInfo: StateFlow<Node?> = nodeRepository.ourNodeInfo
val connectionState = serviceRepository.connectionState
private val _lastTraceRouteTime = MutableStateFlow<Long?>(null)
val lastTraceRouteTime: StateFlow<Long?> = _lastTraceRouteTime.asStateFlow()

View file

@ -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,
)
},
)
}
}

View file

@ -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<Boolean>, 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<Boolean>, onChanged: (newValue: Boolean) -> Unit) {
(!state.value).let { toggled ->
state.update { toggled }
onChanged(toggled)
Timber.e("Remove node error: ${ex.message}")
}
}
}

View file

@ -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)
}

View file

@ -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,

View file

@ -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,
)