feat: Display relay node information for messages (#3574)

Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
James Rich 2025-10-30 17:25:34 -05:00 committed by GitHub
parent 7d1c5cba4c
commit 453dd398d4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 840 additions and 16 deletions

View file

@ -18,6 +18,7 @@
package org.meshtastic.feature.messaging
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
@ -40,6 +41,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalHapticFeedback
@ -50,6 +52,7 @@ import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.launch
import org.meshtastic.core.database.entity.Packet
import org.meshtastic.core.database.entity.Reaction
import org.meshtastic.core.database.model.Message
import org.meshtastic.core.database.model.Node
@ -62,6 +65,7 @@ import org.meshtastic.feature.messaging.component.ReactionDialog
fun DeliveryInfo(
@StringRes title: Int,
@StringRes text: Int? = null,
relayNodeName: String? = null,
onConfirm: (() -> Unit) = {},
onDismiss: () -> Unit = {},
resendOption: Boolean,
@ -88,13 +92,22 @@ fun DeliveryInfo(
)
},
text = {
text?.let {
Text(
text = stringResource(id = it),
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.bodyMedium,
)
Column(modifier = Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
text?.let {
Text(
text = stringResource(id = it),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.bodyMedium,
)
}
relayNodeName?.let {
Text(
text = stringResource(R.string.relayed_by, it),
modifier = Modifier.padding(top = 8.dp),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.bodyMedium,
)
}
}
},
shape = RoundedCornerShape(16.dp),
@ -127,9 +140,16 @@ internal fun MessageList(
if (showStatusDialog != null) {
val msg = showStatusDialog ?: return
val (title, text) = msg.getStatusStringRes()
val relayNodeName by
remember(msg.relayNode, nodes) {
derivedStateOf {
msg.relayNode?.let { relayNodeId -> Packet.getRelayNode(relayNodeId, nodes)?.user?.longName }
}
}
DeliveryInfo(
title = title,
text = text,
relayNodeName = relayNodeName,
onConfirm = {
val deleteList: List<Long> = listOf(msg.uuid)
onDeleteMessages(deleteList)

View file

@ -35,6 +35,7 @@ import kotlinx.coroutines.launch
import org.meshtastic.core.data.repository.MeshLogRepository
import org.meshtastic.core.data.repository.NodeRepository
import org.meshtastic.core.database.entity.MeshLog
import org.meshtastic.core.database.entity.Packet
import org.meshtastic.core.model.getTracerouteResponse
import org.meshtastic.core.ui.viewmodel.stateInWhileSubscribed
import org.meshtastic.proto.AdminProtos
@ -181,6 +182,7 @@ class LogFilterManager {
log.formattedReceivedDate.contains(filterText, ignoreCase = true) ||
(log.decodedPayload?.contains(filterText, ignoreCase = true) == true)
}
FilterMode.AND ->
filterTexts.all { filterText ->
log.logMessage.contains(filterText, ignoreCase = true) ||
@ -281,14 +283,40 @@ constructor(
val decoded = if (hasDecoded) builder.decoded else null
if (hasDecoded) builder.clearDecoded()
val baseText = builder.build().toString().trimEnd()
val result =
var result =
if (hasDecoded && decoded != null) {
val decodedText = decoded.toString().trimEnd().prependIndent(" ")
"$baseText\ndecoded {\n$decodedText\n}"
} else {
baseText
}
return annotateRawMessage(result, packet.from, packet.to)
val relayNode = packet.relayNode
var relayNodeAnnotation: String? = null
val placeholder = "___RELAY_NODE___"
if (relayNode != 0) {
Packet.getRelayNode(relayNode, nodeRepository.nodeDBbyNum.value.values.toList())?.let { node ->
val relayId = node.user.id
val relayName = node.user.longName
val regex = Regex("""\brelay_node: ${relayNode.toUInt()}\b""")
if (regex.containsMatchIn(result)) {
relayNodeAnnotation = "relay_node: $relayName ($relayId)"
result = regex.replace(result, placeholder)
}
}
}
result = annotateRawMessage(result, packet.from, packet.to)
if (relayNodeAnnotation != null) {
result = result.replace(placeholder, relayNodeAnnotation)
} else {
// Not annotated with name, so use hex.
result = annotateRawMessage(result, relayNode)
}
return result
}
/** Annotate the raw message string with the node IDs provided, in hex, if they are present. */