mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Show who relayed messages (#1486)
* Add identification for node that relayed text messages and add count for ammount of relayers of your message * Ack Relays
This commit is contained in:
parent
3f27e3b925
commit
7668a7a7ae
5 changed files with 100 additions and 14 deletions
|
|
@ -28480,6 +28480,7 @@
|
|||
}
|
||||
},
|
||||
"Received Ack" : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
|
|
@ -28542,8 +28543,12 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Received Ack: %@" : {
|
||||
|
||||
},
|
||||
"Recipient Ack" : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
|
|
@ -28606,6 +28611,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Recipient Ack: %@" : {
|
||||
|
||||
},
|
||||
"Recording route" : {
|
||||
"localizations" : {
|
||||
|
|
@ -28789,6 +28797,16 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"Relayed by %d %@" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "new",
|
||||
"value" : "Relayed by %1$d %2$@"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Release Notes" : {
|
||||
"localizations" : {
|
||||
"it" : {
|
||||
|
|
|
|||
|
|
@ -38,4 +38,39 @@ extension MessageEntity {
|
|||
}
|
||||
return false // First message will have no timestamp
|
||||
}
|
||||
|
||||
func relayDisplay() -> String? {
|
||||
|
||||
guard self.relayNode != 0 else { return nil }
|
||||
let context = PersistenceController.shared.container.viewContext
|
||||
|
||||
let relaySuffix = Int64(self.relayNode & 0xFF)
|
||||
let request: NSFetchRequest<UserEntity> = UserEntity.fetchRequest()
|
||||
request.predicate = NSPredicate(format: "(num & 0xFF) == %lld", relaySuffix)
|
||||
|
||||
do {
|
||||
let users = try context.fetch(request)
|
||||
|
||||
// If exactly one match is found, return its name
|
||||
if users.count == 1, let name = users.first?.longName, !name.isEmpty {
|
||||
return "\(name)"
|
||||
}
|
||||
|
||||
// If no exact match, find the node with the smallest hopsAway
|
||||
if let closestNode = users.min(by: { lhs, rhs in
|
||||
guard let lhsHops = lhs.userNode?.hopsAway, let rhsHops = rhs.userNode?.hopsAway else {
|
||||
return false
|
||||
}
|
||||
return lhsHops < rhsHops
|
||||
}), let name = closestNode.longName, !name.isEmpty {
|
||||
return "\(name)"
|
||||
}
|
||||
|
||||
// Fallback to hex node number if no matches
|
||||
return String(format: "Node 0x%02X", UInt32(self.relayNode & 0xFF))
|
||||
|
||||
} catch {
|
||||
return String(format: "Node 0x%02X", UInt32(self.relayNode & 0xFF))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -624,6 +624,7 @@ func adminResponseAck (packet: MeshPacket, context: NSManagedObjectContext) {
|
|||
fetchedMessage[0].ackError = Int32(RoutingError.none.rawValue)
|
||||
fetchedMessage[0].receivedACK = true
|
||||
fetchedMessage[0].realACK = true
|
||||
fetchedMessage[0].relayNode = Int64(packet.relayNode)
|
||||
fetchedMessage[0].ackSNR = packet.rxSnr
|
||||
if fetchedMessage[0].fromUser != nil {
|
||||
fetchedMessage[0].fromUser?.objectWillChange.send()
|
||||
|
|
@ -699,9 +700,11 @@ func routingPacket (packet: MeshPacket, connectedNodeNum: Int64, context: NSMana
|
|||
fetchedMessage[0].realACK = true
|
||||
}
|
||||
}
|
||||
fetchedMessage[0].relayNode = Int64(packet.relayNode)
|
||||
fetchedMessage[0].ackError = Int32(routingMessage.errorReason.rawValue)
|
||||
if routingMessage.errorReason == Routing.Error.none {
|
||||
fetchedMessage[0].receivedACK = true
|
||||
fetchedMessage[0].relays += 1
|
||||
}
|
||||
|
||||
fetchedMessage[0].ackSNR = packet.rxSnr
|
||||
|
|
@ -944,6 +947,9 @@ func textMessageAppPacket(
|
|||
} else {
|
||||
newMessage.messageTimestamp = Int32(Date().timeIntervalSince1970)
|
||||
}
|
||||
if packet.relayNode != 0 {
|
||||
newMessage.relayNode = Int64(packet.relayNode)
|
||||
}
|
||||
newMessage.receivedACK = false
|
||||
newMessage.snr = packet.rxSnr
|
||||
newMessage.rssi = packet.rxRssi
|
||||
|
|
@ -983,6 +989,7 @@ func textMessageAppPacket(
|
|||
newMessage.pkiEncrypted = true
|
||||
newMessage.publicKey = packet.publicKey
|
||||
}
|
||||
|
||||
/// Check for key mismatch
|
||||
if let nodeKey = newMessage.fromUser?.publicKey {
|
||||
if newMessage.toUser != nil && packet.pkiEncrypted && !packet.publicKey.isEmpty {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="24299" systemVersion="25A354" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="24299" systemVersion="25A362" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<entity name="AmbientLightingConfigEntity" representedClassName="AmbientLightingConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="blue" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="current" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
|
|
@ -164,6 +164,8 @@
|
|||
<attribute name="read" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="realACK" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="receivedACK" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="relayNode" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="relays" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="replyID" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="rssi" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ struct MessageContextMenuItems: View {
|
|||
let isCurrentUser: Bool
|
||||
@Binding var isShowingDeleteConfirmation: Bool
|
||||
let onReply: () -> Void
|
||||
@State var relayDisplay: String? = nil
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
|
|
@ -19,6 +20,14 @@ struct MessageContextMenuItems: View {
|
|||
}
|
||||
Text("Channel") + Text(": \(message.channel)")
|
||||
}
|
||||
.onAppear {
|
||||
DispatchQueue.global(qos: .userInitiated).async {
|
||||
let result = message.relayDisplay()
|
||||
DispatchQueue.main.async {
|
||||
relayDisplay = result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Menu("Tapback") {
|
||||
ForEach(Tapbacks.allCases) { tb in
|
||||
|
|
@ -59,12 +68,27 @@ struct MessageContextMenuItems: View {
|
|||
}
|
||||
|
||||
Menu("Message Details") {
|
||||
// Precompute values to avoid executing non-View code inside the ViewBuilder
|
||||
let messageDate = Date(timeIntervalSince1970: TimeInterval(message.messageTimestamp))
|
||||
let ackDate = Date(timeIntervalSince1970: TimeInterval(message.ackTimestamp))
|
||||
let sixMonthsAgo = Calendar.current.date(byAdding: .month, value: -6, to: Date())
|
||||
|
||||
// Compute a relay display string if relayNode is present
|
||||
|
||||
|
||||
VStack {
|
||||
let messageDate = Date(timeIntervalSince1970: TimeInterval(message.messageTimestamp))
|
||||
Text("\(messageDate.formattedDate(format: MessageText.dateFormatString))").foregroundColor(.gray)
|
||||
Text("\(messageDate.formattedDate(format: MessageText.dateFormatString))")
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
|
||||
if !isCurrentUser && !(message.fromUser?.userNode?.viaMqtt ?? false) && message.fromUser?.userNode?.hopsAway ?? -1 == 0 {
|
||||
if let relayDisplay {
|
||||
let prefix = message.realACK ? "Ack Relay: " : "Relay: "
|
||||
Text(prefix + relayDisplay)
|
||||
.foregroundColor(relayDisplay.contains("Node ") ? .gray : .primary)
|
||||
.font(relayDisplay.contains("Node ") ? .caption : .body)
|
||||
}
|
||||
|
||||
if !isCurrentUser && !(message.fromUser?.userNode?.viaMqtt ?? false) && message.fromUser?.userNode?.hopsAway ?? -1 == 0 {
|
||||
VStack {
|
||||
Text("SNR \(String(format: "%.2f", message.snr)) dB")
|
||||
Text("RSSI \(String(format: "%.2f", message.rssi)) dBm")
|
||||
|
|
@ -74,29 +98,29 @@ struct MessageContextMenuItems: View {
|
|||
Text("Hops Away \(message.fromUser?.userNode?.hopsAway ?? 0)")
|
||||
}
|
||||
}
|
||||
if message.relays != 0 && message.realACK == false {
|
||||
Text("Relayed by \(message.relays) \(message.relays == 1 ? "node" : "nodes")")
|
||||
}
|
||||
if isCurrentUser && message.receivedACK {
|
||||
VStack {
|
||||
Text("Received Ack") + Text(": \(message.receivedACK ? "✔️" : "")")
|
||||
Text("Recipient Ack") + Text(": \(message.realACK ? "✔️" : "")")
|
||||
Text("Received Ack: \(message.receivedACK ? "✔️" : "")")
|
||||
Text("Recipient Ack: \(message.realACK ? "✔️" : "")")
|
||||
}
|
||||
} else if isCurrentUser && message.ackError == 0 {
|
||||
// Empty Error
|
||||
Text("Waiting")
|
||||
} else if isCurrentUser && message.ackError > 0 {
|
||||
let ackErrorVal = RoutingError(rawValue: Int(message.ackError))
|
||||
Text("\(ackErrorVal?.display ?? "Empty Ack Error")")
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
|
||||
if isCurrentUser {
|
||||
VStack {
|
||||
let ackDate = Date(timeIntervalSince1970: TimeInterval(message.ackTimestamp))
|
||||
let sixMonthsAgo = Calendar.current.date(byAdding: .month, value: -6, to: Date())
|
||||
if ackDate >= sixMonthsAgo! {
|
||||
Text("Ack Time: \(ackDate.formattedDate(format: MessageText.timeFormatString))")
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
if let sixMonthsAgo, ackDate >= sixMonthsAgo {
|
||||
Text("Ack Time: \(ackDate.formattedDate(format: MessageText.timeFormatString))")
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
}
|
||||
|
||||
if message.ackSNR != 0 {
|
||||
VStack {
|
||||
Text("Ack SNR: \(String(format: "%.2f", message.ackSNR)) dB")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue