mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Merge branch 'main' into jpirihi-dev
This commit is contained in:
commit
64dcd4b1ad
10 changed files with 307 additions and 123 deletions
|
|
@ -9,6 +9,7 @@
|
|||
/* Begin PBXBuildFile section */
|
||||
C9483F6D2773017500998F6B /* MapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9483F6C2773017500998F6B /* MapView.swift */; };
|
||||
C9A7BC1027759A9600760B50 /* PositionAnnotationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9A7BC0F27759A9600760B50 /* PositionAnnotationView.swift */; };
|
||||
DD1BF2F92776FE2E008C8D2F /* UserMessageList.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1BF2F82776FE2E008C8D2F /* UserMessageList.swift */; };
|
||||
DD23A50F26FD1B4400D9B90C /* PeripheralModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD23A50E26FD1B4400D9B90C /* PeripheralModel.swift */; };
|
||||
DD2E65262767A01F00E45FC5 /* NodeDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2E65252767A01F00E45FC5 /* NodeDetail.swift */; };
|
||||
DD47E3CE26F103C600029299 /* NodeList.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD47E3CD26F103C600029299 /* NodeList.swift */; };
|
||||
|
|
@ -25,6 +26,7 @@
|
|||
DD8169FB271F1F3A00F4AB02 /* MeshLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8169FA271F1F3A00F4AB02 /* MeshLog.swift */; };
|
||||
DD8169FF272476C700F4AB02 /* LogDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8169FE272476C700F4AB02 /* LogDocument.swift */; };
|
||||
DD836AE726F6B38600ABCC23 /* Connect.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD836AE626F6B38600ABCC23 /* Connect.swift */; };
|
||||
DD882F5D2772E4640005BF05 /* Contacts.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD882F5C2772E4640005BF05 /* Contacts.swift */; };
|
||||
DD90860C26F684AF00DC5189 /* BatteryIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD90860B26F684AF00DC5189 /* BatteryIcon.swift */; };
|
||||
DD90860E26F69BAE00DC5189 /* NodeMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD90860D26F69BAE00DC5189 /* NodeMap.swift */; };
|
||||
DD913639270DFF4C00D7ACF3 /* LocalNotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD913638270DFF4C00D7ACF3 /* LocalNotificationManager.swift */; };
|
||||
|
|
@ -71,6 +73,7 @@
|
|||
/* Begin PBXFileReference section */
|
||||
C9483F6C2773017500998F6B /* MapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapView.swift; sourceTree = "<group>"; };
|
||||
C9A7BC0F27759A9600760B50 /* PositionAnnotationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionAnnotationView.swift; sourceTree = "<group>"; };
|
||||
DD1BF2F82776FE2E008C8D2F /* UserMessageList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserMessageList.swift; sourceTree = "<group>"; };
|
||||
DD23A50E26FD1B4400D9B90C /* PeripheralModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeripheralModel.swift; sourceTree = "<group>"; };
|
||||
DD2E65252767A01F00E45FC5 /* NodeDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeDetail.swift; sourceTree = "<group>"; };
|
||||
DD47E3CD26F103C600029299 /* NodeList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeList.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -86,6 +89,7 @@
|
|||
DD8169FA271F1F3A00F4AB02 /* MeshLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshLog.swift; sourceTree = "<group>"; };
|
||||
DD8169FE272476C700F4AB02 /* LogDocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogDocument.swift; sourceTree = "<group>"; };
|
||||
DD836AE626F6B38600ABCC23 /* Connect.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Connect.swift; sourceTree = "<group>"; };
|
||||
DD882F5C2772E4640005BF05 /* Contacts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Contacts.swift; sourceTree = "<group>"; };
|
||||
DD90860A26F645B700DC5189 /* MeshtasticClient.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MeshtasticClient.entitlements; sourceTree = "<group>"; };
|
||||
DD90860B26F684AF00DC5189 /* BatteryIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryIcon.swift; sourceTree = "<group>"; };
|
||||
DD90860D26F69BAE00DC5189 /* NodeMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeMap.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -315,6 +319,8 @@
|
|||
children = (
|
||||
DD47E3DA26F3901A00029299 /* Channels.swift */,
|
||||
DD47E3DC26F390A000029299 /* Messages.swift */,
|
||||
DD882F5C2772E4640005BF05 /* Contacts.swift */,
|
||||
DD1BF2F82776FE2E008C8D2F /* UserMessageList.swift */,
|
||||
);
|
||||
path = Messages;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -527,8 +533,10 @@
|
|||
DDF924CA26FBB953009FE055 /* ConnectedDevice.swift in Sources */,
|
||||
DDAF8C5D26ED09490058C060 /* portnums.pb.swift in Sources */,
|
||||
DD9D8F2F2764403B00080993 /* Meshtastic.xcdatamodeld in Sources */,
|
||||
DD1BF2F92776FE2E008C8D2F /* UserMessageList.swift in Sources */,
|
||||
DD23A50F26FD1B4400D9B90C /* PeripheralModel.swift in Sources */,
|
||||
C9A7BC1027759A9600760B50 /* PositionAnnotationView.swift in Sources */,
|
||||
DD882F5D2772E4640005BF05 /* Contacts.swift in Sources */,
|
||||
DD47E3CE26F103C600029299 /* NodeList.swift in Sources */,
|
||||
DD47E3D626F17ED900029299 /* CircleText.swift in Sources */,
|
||||
DDC2E18F26CE25FE0042C5E4 /* ContentView.swift in Sources */,
|
||||
|
|
@ -715,7 +723,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.32;
|
||||
MARKETING_VERSION = 1.36;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
|
|
@ -742,7 +750,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.32;
|
||||
MARKETING_VERSION = 1.36;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
|
|
|
|||
|
|
@ -83,15 +83,15 @@
|
|||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "BD16DE86-365A-47B2-8DBB-340D5A835D76"
|
||||
uuid = "04619ED9-EF12-49A5-9AEF-C115B206581C"
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "MeshtasticClient/Helpers/BLEManager.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "492"
|
||||
endingLineNumber = "492"
|
||||
startingLineNumber = "469"
|
||||
endingLineNumber = "469"
|
||||
landmarkName = "peripheral(_:didUpdateValueFor:error:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
|
|
@ -99,15 +99,31 @@
|
|||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "77F5CE1E-8BA3-4831-8A0D-6CEE16E9E8FD"
|
||||
uuid = "F25765DD-CBAA-4892-8EE8-A632C79D42F0"
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "MeshtasticClient/Helpers/BLEManager.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "809"
|
||||
endingLineNumber = "809"
|
||||
startingLineNumber = "466"
|
||||
endingLineNumber = "466"
|
||||
landmarkName = "peripheral(_:didUpdateValueFor:error:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "679F6F7F-E126-4725-A682-BC8129C0CCB9"
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "MeshtasticClient/Helpers/BLEManager.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "806"
|
||||
endingLineNumber = "806"
|
||||
landmarkName = "sendMessage(message:toUserNum:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
|
|
@ -115,16 +131,16 @@
|
|||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "B9045E7F-D63F-4540-B984-60F820809762"
|
||||
uuid = "1756397A-4451-4B94-8EB1-0B19AD444C9E"
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "MeshtasticClient/Helpers/BLEManager.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "893"
|
||||
endingLineNumber = "893"
|
||||
landmarkName = "sendMessage(message:toUserNum:)"
|
||||
startingLineNumber = "608"
|
||||
endingLineNumber = "608"
|
||||
landmarkName = "peripheral(_:didUpdateValueFor:error:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
@Published var peripherals = [Peripheral]()
|
||||
|
||||
@Published var connectedPeripheral: Peripheral!
|
||||
//@Published var connectedNode: NodeInfoEntity!
|
||||
@Published var lastConnectedPeripheral: String
|
||||
@Published var lastConnectionError: String
|
||||
|
||||
|
|
@ -55,7 +54,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
// MARK: init BLEManager
|
||||
override init() {
|
||||
|
||||
self.meshLoggingEnabled = UserDefaults.standard.object(forKey: "meshActivityLog") as? Bool ?? false
|
||||
self.meshLoggingEnabled = true // UserDefaults.standard.object(forKey: "meshActivityLog") as? Bool ?? true
|
||||
self.lastConnectedPeripheral = ""
|
||||
self.lastConnectionError = ""
|
||||
super.init()
|
||||
|
|
@ -220,6 +219,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
|
||||
} catch {
|
||||
print("💥 Fetch NodeInfo Failed")
|
||||
if meshLoggingEnabled { MeshLogger.log("💥 Fetch NodeInfo Failed") }
|
||||
}
|
||||
|
||||
lastConnectedPeripheral = peripheral.identifier.uuidString
|
||||
|
|
@ -257,8 +257,8 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
// We will try and re-connect to this device
|
||||
lastConnectionError = "🚫 \(e.localizedDescription) The app will automatically reconnect to the preferred radio if it reappears within 10 seconds."
|
||||
if peripheral.identifier.uuidString == UserDefaults.standard.object(forKey: "preferredPeripheralId") as? String ?? "" {
|
||||
if meshLoggingEnabled { MeshLogger.log("BLE Reconnecting: \(peripheral.name ?? "Unknown")") }
|
||||
print("BLE Reconnecting: \(peripheral.name ?? "Unknown")")
|
||||
if meshLoggingEnabled { MeshLogger.log("ℹ️ BLE Reconnecting: \(peripheral.name ?? "Unknown")") }
|
||||
print("ℹ️ BLE Reconnecting: \(peripheral.name ?? "Unknown")")
|
||||
self.connectTo(peripheral: peripheral)
|
||||
}
|
||||
} else if errorCode == 7 { // CBError.Code.peripheralDisconnected The specified device has disconnected from us.
|
||||
|
|
@ -340,8 +340,8 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
peripheral.readValue(for: FROMRADIO_characteristic)
|
||||
|
||||
case FROMNUM_UUID:
|
||||
print("FROMNUM (Notify) characteristic OK")
|
||||
if meshLoggingEnabled { MeshLogger.log("✅BLE did discover FROMNUM (Notify) characteristic for Meshtastic by \(peripheral.name ?? "Unknown")") }
|
||||
print("✅ FROMNUM (Notify) characteristic OK")
|
||||
if meshLoggingEnabled { MeshLogger.log("✅ BLE did discover FROMNUM (Notify) characteristic for Meshtastic by \(peripheral.name ?? "Unknown")") }
|
||||
FROMNUM_characteristic = characteristic
|
||||
peripheral.setNotifyValue(true, for: characteristic)
|
||||
|
||||
|
|
@ -390,14 +390,12 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
var decodedInfo = FromRadio()
|
||||
|
||||
decodedInfo = try! FromRadio(serializedData: characteristic.value!)
|
||||
print("Print DecodedInfo")
|
||||
print(decodedInfo)
|
||||
//print("Print DecodedInfo")
|
||||
//print(decodedInfo)
|
||||
|
||||
// MyInfo Data
|
||||
if decodedInfo.myInfo.myNodeNum != 0 {
|
||||
|
||||
print("💾 Save a CoreData MyInfoEntity")
|
||||
|
||||
let fetchMyInfoRequest:NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "MyInfoEntity")
|
||||
fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(decodedInfo.myInfo.myNodeNum))
|
||||
|
||||
|
|
@ -413,6 +411,9 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
myInfo.messageTimeoutMsec = Int32(bitPattern: decodedInfo.myInfo.messageTimeoutMsec)
|
||||
myInfo.minAppVersion = Int32(bitPattern: decodedInfo.myInfo.minAppVersion)
|
||||
myInfo.maxChannels = Int32(bitPattern: decodedInfo.myInfo.maxChannels)
|
||||
connectedPeripheral.num = myInfo.myNodeNum
|
||||
connectedPeripheral.firmwareVersion = myInfo.firmwareVersion ?? "Unknown"
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
|
|
@ -439,17 +440,14 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
}
|
||||
|
||||
} catch {
|
||||
print("💥 Fetch MyInfo Error")
|
||||
|
||||
print("💥 Fetch MyInfo Error")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// NodeInfo Data
|
||||
if decodedInfo.nodeInfo.num != 0 {
|
||||
|
||||
print("💾 Save a CoreData NodeInfoEntity")
|
||||
|
||||
let fetchNodeRequest:NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchNodeRequest.predicate = NSPredicate(format: "num == %lld", Int64(decodedInfo.nodeInfo.num))
|
||||
|
||||
|
|
@ -457,7 +455,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
|
||||
let fetchedNode = try context?.fetch(fetchNodeRequest) as! [NodeInfoEntity]
|
||||
// Not Found Insert
|
||||
if fetchedNode.isEmpty {
|
||||
if fetchedNode.isEmpty && decodedInfo.nodeInfo.hasUser {
|
||||
|
||||
let newNode = NodeInfoEntity(context: context!)
|
||||
newNode.id = Int64(decodedInfo.nodeInfo.num)
|
||||
|
|
@ -469,12 +467,14 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
|
||||
if self.connectedPeripheral != nil && self.connectedPeripheral.num == newNode.id {
|
||||
|
||||
newNode.bleName = self.connectedPeripheral.name
|
||||
|
||||
} else {
|
||||
|
||||
let userIdLast4: String = String(decodedInfo.nodeInfo.user.id.suffix(4))
|
||||
newNode.bleName = "Meshtastic_" + userIdLast4
|
||||
newNode.bleName = self.connectedPeripheral.peripheral.name
|
||||
if decodedInfo.nodeInfo.hasUser {
|
||||
|
||||
connectedPeripheral.name = decodedInfo.nodeInfo.user.longName
|
||||
connectedPeripheral.longName = decodedInfo.nodeInfo.user.longName
|
||||
connectedPeripheral.shortName = decodedInfo.nodeInfo.user.shortName
|
||||
connectedPeripheral.num = Int64(decodedInfo.nodeInfo.num)
|
||||
}
|
||||
}
|
||||
|
||||
if decodedInfo.nodeInfo.hasUser {
|
||||
|
|
@ -486,6 +486,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
newUser.shortName = decodedInfo.nodeInfo.user.shortName
|
||||
newUser.macaddr = decodedInfo.nodeInfo.user.macaddr
|
||||
newUser.hwModel = String(describing: decodedInfo.nodeInfo.user.hwModel).uppercased()
|
||||
newUser.team = (String(describing: decodedInfo.nodeInfo.user.team))
|
||||
newNode.user = newUser
|
||||
}
|
||||
|
||||
|
|
@ -517,7 +518,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
print("💥 Fetch MyInfo Error")
|
||||
}
|
||||
|
||||
} else {
|
||||
} else if decodedInfo.nodeInfo.hasUser {
|
||||
|
||||
fetchedNode[0].id = Int64(decodedInfo.nodeInfo.num)
|
||||
fetchedNode[0].num = Int64(decodedInfo.nodeInfo.num)
|
||||
|
|
@ -530,6 +531,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
fetchedNode[0].user!.longName = decodedInfo.nodeInfo.user.longName
|
||||
fetchedNode[0].user!.shortName = decodedInfo.nodeInfo.user.shortName
|
||||
fetchedNode[0].user!.hwModel = String(describing: decodedInfo.nodeInfo.user.hwModel).uppercased()
|
||||
fetchedNode[0].user!.team = (String(describing: decodedInfo.nodeInfo.user.team))
|
||||
}
|
||||
|
||||
let position = PositionEntity(context: context!)
|
||||
|
|
@ -600,17 +602,15 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
// Handle assorted app packets
|
||||
if decodedInfo.packet.id != 0 {
|
||||
|
||||
print("Handle a Packet")
|
||||
do {
|
||||
|
||||
//!!!: Switch Messages Tab to coredata
|
||||
// Text Message App - Primary Broadcast Channel
|
||||
if decodedInfo.packet.decoded.portnum == PortNum.textMessageApp {
|
||||
|
||||
if let messageText = String(bytes: decodedInfo.packet.decoded.payload, encoding: .utf8) {
|
||||
|
||||
print("Message Text: \(messageText)")
|
||||
if meshLoggingEnabled { MeshLogger.log("BLE FROMRADIO received for text message app \(messageText)") }
|
||||
print("💬 BLE FROMRADIO received for text message app \(messageText)")
|
||||
if meshLoggingEnabled { MeshLogger.log("💬 BLE FROMRADIO received for text message app \(messageText)") }
|
||||
|
||||
let messageUsers:NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "UserEntity")
|
||||
messageUsers.predicate = NSPredicate(format:"num IN %@", [decodedInfo.packet.to, decodedInfo.packet.from])
|
||||
|
|
@ -624,13 +624,13 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
newMessage.messageTimestamp = Int32(bitPattern: decodedInfo.packet.rxTime)
|
||||
newMessage.receivedACK = false
|
||||
newMessage.direction = "IN"
|
||||
//newMessage.toUser = Int64(decodedInfo.packet.to)
|
||||
|
||||
if decodedInfo.packet.to == broadcastNodeNum && fetchedUsers.count == 1 {
|
||||
|
||||
// Save the broadcast user if it does not exist
|
||||
let bcu: UserEntity = UserEntity(context: context!)
|
||||
bcu.shortName = "BC"
|
||||
bcu.longName = "Broadcast"
|
||||
bcu.shortName = "ALL"
|
||||
bcu.longName = "Primary - Broadcast"
|
||||
bcu.hwModel = "UNSET"
|
||||
bcu.num = Int64(broadcastNodeNum)
|
||||
bcu.userId = "BROADCASTNODE"
|
||||
|
|
@ -647,7 +647,8 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
do {
|
||||
|
||||
try context!.save()
|
||||
print("Saved a new message for \(decodedInfo.packet.id)")
|
||||
print("💾 Saved a new message for \(decodedInfo.packet.id)")
|
||||
if meshLoggingEnabled { MeshLogger.log("💾 Saved a new message for \(decodedInfo.packet.id)") }
|
||||
|
||||
// Create an iOS Notification for the received message and schedule it immediately
|
||||
let manager = LocalNotificationManager()
|
||||
|
|
@ -660,19 +661,19 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
content: messageText)
|
||||
]
|
||||
manager.schedule()
|
||||
if meshLoggingEnabled { MeshLogger.log("iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? "Unknown") \(messageText)") }
|
||||
if meshLoggingEnabled { MeshLogger.log("💬 iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? "Unknown") \(messageText)") }
|
||||
|
||||
} catch {
|
||||
|
||||
context!.rollback()
|
||||
|
||||
let nsError = error as NSError
|
||||
print("Failed to save new MessageEntity \(nsError)")
|
||||
print("💥 Failed to save new MessageEntity \(nsError)")
|
||||
}
|
||||
|
||||
} catch {
|
||||
|
||||
print("Fetch Message To and From Users Error")
|
||||
print("💥 Fetch Message To and From Users Error")
|
||||
}
|
||||
}
|
||||
} else if decodedInfo.packet.decoded.portnum == PortNum.nodeinfoApp {
|
||||
|
|
@ -695,29 +696,25 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
return
|
||||
}
|
||||
do {
|
||||
// print(decodedInfo.packet.decoded.payload)
|
||||
|
||||
try context!.save()
|
||||
|
||||
if meshLoggingEnabled {
|
||||
MeshLogger.log("MESH PACKET Updated NodeInfo SNR and Time from Node Info App Packet For: \(Int64(decodedInfo.nodeInfo.num))")
|
||||
}
|
||||
print("Updated NodeInfo SNR and Time from Packet For: \(fetchedNode[0].num)")
|
||||
if meshLoggingEnabled { MeshLogger.log("💾 Updated NodeInfo SNR and Time from Node Info App Packet For: \(Int64(decodedInfo.nodeInfo.num))")}
|
||||
print("💾 Updated NodeInfo SNR and Time from Packet For: \(fetchedNode[0].num)")
|
||||
|
||||
} catch {
|
||||
|
||||
context!.rollback()
|
||||
|
||||
let nsError = error as NSError
|
||||
print("Error Saving NodeInfoEntity from NODEINFO_APP \(nsError)")
|
||||
print("💥 Error Saving NodeInfoEntity from NODEINFO_APP \(nsError)")
|
||||
|
||||
}
|
||||
} catch {
|
||||
|
||||
print("Error Fetching NodeInfoEntity for NODEINFO_APP")
|
||||
print("💥 Error Fetching NodeInfoEntity for NODEINFO_APP")
|
||||
}
|
||||
|
||||
print(decodedInfo.packet.decoded.payload)
|
||||
|
||||
} else if decodedInfo.packet.decoded.portnum == PortNum.positionApp {
|
||||
|
||||
let fetchNodePositionRequest:NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
|
|
@ -730,7 +727,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
if fetchedNode.count == 1 {
|
||||
fetchedNode[0].id = Int64(decodedInfo.packet.from)
|
||||
fetchedNode[0].num = Int64(decodedInfo.packet.from)
|
||||
print(decodedInfo.packet.decoded.payload)
|
||||
if(decodedInfo.packet.rxTime == 0) {
|
||||
|
||||
fetchedNode[0].lastHeard = Date()
|
||||
|
|
@ -760,32 +756,33 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
context!.rollback()
|
||||
|
||||
let nsError = error as NSError
|
||||
print("Error Saving NodeInfoEntity from NODEINFO_APP \(nsError)")
|
||||
print("💥 Error Saving NodeInfoEntity from NODEINFO_APP \(nsError)")
|
||||
}
|
||||
} catch {
|
||||
|
||||
print("Error Fetching NodeInfoEntity for NODEINFO_APP")
|
||||
print("💥 Error Fetching NodeInfoEntity for NODEINFO_APP")
|
||||
}
|
||||
|
||||
} else if decodedInfo.packet.decoded.portnum == PortNum.adminApp {
|
||||
|
||||
if meshLoggingEnabled { MeshLogger.log("MESH PACKET received for Admin App UNHANDLED \(try decodedInfo.packet.jsonString())") }
|
||||
print("Admin App Packet")
|
||||
print(try decodedInfo.packet.jsonString())
|
||||
if meshLoggingEnabled { MeshLogger.log("🚨 MESH PACKET received for Admin App UNHANDLED \(try decodedInfo.packet.jsonString())") }
|
||||
print("🚨 MESH PACKET received for Admin App UNHANDLED \(try decodedInfo.packet.jsonString())")
|
||||
|
||||
} else if decodedInfo.packet.decoded.portnum == PortNum.routingApp {
|
||||
|
||||
if meshLoggingEnabled { MeshLogger.log("MESH PACKET received for Routing App UNHANDLED \(try decodedInfo.packet.jsonString())") }
|
||||
print("Routing App Packet")
|
||||
print(try decodedInfo.packet.jsonString())
|
||||
if meshLoggingEnabled { MeshLogger.log("🚨 MESH PACKET received for Routing App UNHANDLED \(try decodedInfo.packet.jsonString())") }
|
||||
print("🚨 MESH PACKET received for Routing App UNHANDLED \(try decodedInfo.packet.jsonString())")
|
||||
|
||||
} else {
|
||||
if meshLoggingEnabled { MeshLogger.log("MESH PACKET received for Other App UNHANDLED \(try decodedInfo.packet.jsonString())") }
|
||||
print("Other App Packet")
|
||||
print(try decodedInfo.packet.jsonString())
|
||||
|
||||
if meshLoggingEnabled { MeshLogger.log("🚨 MESH PACKET received for Other App UNHANDLED \(try decodedInfo.packet.jsonString())") }
|
||||
print("🚨 MESH PACKET received for Other App UNHANDLED \(try decodedInfo.packet.jsonString())")
|
||||
|
||||
}
|
||||
|
||||
} catch {
|
||||
if meshLoggingEnabled { MeshLogger.log("Fatal Error: Failed to decode json") }
|
||||
fatalError("Failed to decode json")
|
||||
if meshLoggingEnabled { MeshLogger.log("⚰️ Fatal Error: Failed to decode json") }
|
||||
print("⚰️ Fatal Error: Failed to decode json")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -798,8 +795,8 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
}
|
||||
|
||||
default:
|
||||
if meshLoggingEnabled { MeshLogger.log("Unhandled Characteristic UUID: \(characteristic.uuid)") }
|
||||
print("Unhandled Characteristic UUID: \(characteristic.uuid)")
|
||||
if meshLoggingEnabled { MeshLogger.log("🚨 Unhandled Characteristic UUID: \(characteristic.uuid)") }
|
||||
print("🚨 Unhandled Characteristic UUID: \(characteristic.uuid)")
|
||||
}
|
||||
peripheral.readValue(for: FROMRADIO_characteristic)
|
||||
}
|
||||
|
|
@ -826,12 +823,16 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
connectTo(peripheral: lastConnectedPeripheral!.peripheral)
|
||||
}
|
||||
}
|
||||
print("Failed in the top message condition")
|
||||
print("🚫 Message Send Failed, not properly connected to \(lastConnectedPeripheral)")
|
||||
if meshLoggingEnabled { MeshLogger.log("🚫 Message Send Failed, not properly connected to \(lastConnectedPeripheral)") }
|
||||
|
||||
success = false
|
||||
} else if message.count < 1 {
|
||||
// Don's send an empty message
|
||||
print("Don't Send an Empty Message")
|
||||
|
||||
// Don't send an empty message
|
||||
print("🚫 Don't Send an Empty Message")
|
||||
success = false
|
||||
|
||||
} else {
|
||||
|
||||
let fromUserNum:Int64 = self.connectedPeripheral.num
|
||||
|
|
@ -845,7 +846,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
|
||||
if fetchedUsers.isEmpty {
|
||||
|
||||
print("Message Users Not Found, Fail")
|
||||
print("🚫 Message Users Not Found, Fail")
|
||||
success = false
|
||||
}
|
||||
else if fetchedUsers.count >= 1 {
|
||||
|
|
@ -859,8 +860,8 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
if newMessage.toUser == nil {
|
||||
|
||||
let bcu: UserEntity = UserEntity(context: context!)
|
||||
bcu.shortName = "BC"
|
||||
bcu.longName = "Broadcast"
|
||||
bcu.shortName = "ALL"
|
||||
bcu.longName = "Primary - Broadcast"
|
||||
bcu.hwModel = "UNSET"
|
||||
bcu.num = Int64(broadcastNodeNum)
|
||||
bcu.userId = "BROADCASTNODE"
|
||||
|
|
@ -887,12 +888,17 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
toRadio.packet = meshPacket
|
||||
|
||||
let binaryData: Data = try! toRadio.serializedData()
|
||||
|
||||
if meshLoggingEnabled { MeshLogger.log("📲 New message sent to \(newMessage.toUser?.longName! ?? "Unknown")") }
|
||||
print("📲 New message sent to \(newMessage.toUser?.longName! ?? "Unknown")")
|
||||
|
||||
if connectedPeripheral!.peripheral.state == CBPeripheralState.connected {
|
||||
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
|
||||
do {
|
||||
|
||||
try context!.save()
|
||||
print("Saved a new sent message from \(connectedPeripheral.num)")
|
||||
print("💾 Saved a new sent message from \(newMessage.fromUser?.longName! ?? "Unknown")")
|
||||
if meshLoggingEnabled { MeshLogger.log("💾 Saved a new sent message from \(connectedPeripheral.num)") }
|
||||
success = true
|
||||
nextSentMessageId+=1
|
||||
|
||||
|
|
@ -901,7 +907,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
context!.rollback()
|
||||
|
||||
let nsError = error as NSError
|
||||
print("Unresolved error \(nsError)")
|
||||
print("🚫 Unresolved error \(nsError)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@
|
|||
<attribute name="macaddr" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="shortName" attributeType="String"/>
|
||||
<attribute name="team" optional="YES" attributeType="String"/>
|
||||
<attribute name="userId" attributeType="String"/>
|
||||
<relationship name="receivedMessages" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="MessageEntity" inverseName="toUser" inverseEntity="MessageEntity"/>
|
||||
<relationship name="sentMessages" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="MessageEntity" inverseName="fromUser" inverseEntity="MessageEntity"/>
|
||||
|
|
@ -68,6 +69,6 @@
|
|||
<element name="MyInfoEntity" positionX="-18" positionY="81" width="128" height="149"/>
|
||||
<element name="NodeInfoEntity" positionX="-63" positionY="-18" width="128" height="149"/>
|
||||
<element name="PositionEntity" positionX="-54" positionY="54" width="128" height="119"/>
|
||||
<element name="UserEntity" positionX="0" positionY="144" width="128" height="164"/>
|
||||
<element name="UserEntity" positionX="0" positionY="144" width="128" height="179"/>
|
||||
</elements>
|
||||
</model>
|
||||
|
|
@ -21,22 +21,22 @@ struct MeshtasticClientApp: App {
|
|||
.onChange(of: scenePhase) { (newScenePhase) in
|
||||
switch newScenePhase {
|
||||
case .background:
|
||||
print("ℹ️ Scene is in the background")
|
||||
do {
|
||||
|
||||
try persistenceController.container.viewContext.save()
|
||||
print("Saved viewContext when the app went to the background.")
|
||||
print("💾 Saved CoreData ViewContext when the app went to the background.")
|
||||
|
||||
} catch {
|
||||
|
||||
print("Failed to save viewContext when the app goes to the background.")
|
||||
print("💥 Failed to save viewContext when the app goes to the background.")
|
||||
}
|
||||
print("Scene is in the background")
|
||||
case .inactive:
|
||||
print("Scene is inactive")
|
||||
print("ℹ️ Scene is inactive")
|
||||
case .active:
|
||||
print("Scene is active")
|
||||
print("ℹ️ Scene is active")
|
||||
@unknown default:
|
||||
print("Apple must have changed something")
|
||||
print("💥 Apple must have changed something")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ struct ContentView: View {
|
|||
@State private var selection: Tab = .ble
|
||||
|
||||
enum Tab {
|
||||
case contacts
|
||||
case messages
|
||||
case map
|
||||
case ble
|
||||
|
|
@ -18,6 +19,14 @@ struct ContentView: View {
|
|||
var body: some View {
|
||||
|
||||
TabView(selection: $selection) {
|
||||
// Contacts()
|
||||
// .tabItem {
|
||||
// Label("Contacts", systemImage: "person.crop.circle")
|
||||
// .symbolRenderingMode(.hierarchical)
|
||||
// .symbolVariant(.none)
|
||||
//
|
||||
// }
|
||||
// .tag(Tab.contacts)
|
||||
Channels()
|
||||
.tabItem {
|
||||
Label("Messages", systemImage: "text.bubble")
|
||||
|
|
|
|||
126
MeshtasticClient/Views/Messages/Contacts.swift
Normal file
126
MeshtasticClient/Views/Messages/Contacts.swift
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
//
|
||||
// Contacts.swift
|
||||
// MeshtasticClient
|
||||
//
|
||||
// Created by Garth Vander Houwen on 12/21/21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct Contacts: View {
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
||||
@FetchRequest(
|
||||
sortDescriptors: [NSSortDescriptor(key: "longName", ascending: true)],
|
||||
animation: .default)
|
||||
|
||||
private var users: FetchedResults<UserEntity>
|
||||
|
||||
var body: some View {
|
||||
|
||||
NavigationView {
|
||||
|
||||
List(users) { user in
|
||||
|
||||
if user.receivedMessages?.count ?? 0 > 0 {
|
||||
|
||||
let mostRecent = user.receivedMessages?.lastObject as! MessageEntity
|
||||
let lastMessageTime = Date(timeIntervalSince1970: TimeInterval(Int64(mostRecent.messageTimestamp)))
|
||||
let lastMessageDay = Calendar.current.dateComponents([.day], from: lastMessageTime).day ?? 0
|
||||
let currentDay = Calendar.current.dateComponents([.day], from: Date()).day ?? 0
|
||||
|
||||
HStack {
|
||||
|
||||
VStack {
|
||||
|
||||
CircleText(text: user.shortName ?? "???", color: Color.blue)
|
||||
}
|
||||
.padding([.leading, .trailing])
|
||||
|
||||
VStack {
|
||||
|
||||
HStack {
|
||||
|
||||
VStack {
|
||||
|
||||
Text(user.longName ?? "Unknown").font(.headline).fixedSize()
|
||||
}
|
||||
|
||||
VStack {
|
||||
|
||||
if lastMessageDay == currentDay {
|
||||
|
||||
Text(lastMessageTime, style: .time )
|
||||
.font(.caption)
|
||||
.foregroundColor(.gray)
|
||||
|
||||
} else if ( lastMessageDay == (currentDay - 1)) {
|
||||
|
||||
Text("Yesterday")
|
||||
.font(.callout)
|
||||
.foregroundColor(.gray)
|
||||
|
||||
} else if ( lastMessageDay < (currentDay - 1) && lastMessageDay > (currentDay - 5) ) {
|
||||
|
||||
Text(lastMessageTime, style: .date)
|
||||
|
||||
} else {
|
||||
|
||||
Text(lastMessageTime, style: .date)
|
||||
}
|
||||
}.frame(maxWidth: .infinity, alignment: .trailing)
|
||||
}
|
||||
.listRowSeparator(.hidden).frame(height: 5)
|
||||
|
||||
HStack (alignment: .top) {
|
||||
Text(mostRecent.messagePayload ?? "EMPTY MESSSAGE")
|
||||
.frame(height: 60)
|
||||
.truncationMode(.tail)
|
||||
.foregroundColor(Color.gray)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
}.padding(.top, 15)
|
||||
}
|
||||
} else {
|
||||
|
||||
HStack {
|
||||
|
||||
VStack {
|
||||
|
||||
CircleText(text: user.shortName ?? "???", color: Color.blue)
|
||||
}
|
||||
.padding(.trailing)
|
||||
|
||||
VStack {
|
||||
|
||||
HStack{
|
||||
|
||||
VStack {
|
||||
|
||||
Text(user.longName ?? "Unknown").font(.headline).fixedSize()
|
||||
}
|
||||
|
||||
VStack {
|
||||
Text(" ")
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .trailing)
|
||||
}
|
||||
.listRowSeparator(.hidden).frame(height: 5)
|
||||
}
|
||||
}.padding()
|
||||
}
|
||||
//NavigationLink(note.title, destination: NoteEditor(id: note.id))
|
||||
}
|
||||
.navigationTitle("Contacts")
|
||||
}
|
||||
.listStyle(PlainListStyle())
|
||||
}
|
||||
}
|
||||
|
||||
struct Contacts_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Contacts()
|
||||
}
|
||||
}
|
||||
|
|
@ -49,7 +49,6 @@ struct Messages: View {
|
|||
|
||||
HStack(alignment: .top) {
|
||||
let currentUser: Bool = (bleManager.connectedPeripheral == nil) ? false : ((bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.num == message.fromUser?.num) ? true : false )
|
||||
//let currentUser: (Bool = message.fromUser == nil : false : (message.fromUser != nil && bleManager.connectedPeripheral.num == message.fromUser!.num : true)
|
||||
|
||||
CircleText(text: (message.fromUser?.shortName ?? "???"), color: currentUser ? .accentColor : Color(.darkGray)).padding(.all, 5)
|
||||
.gesture(LongPressGesture(minimumDuration: 2)
|
||||
|
|
|
|||
20
MeshtasticClient/Views/Messages/UserMessageList.swift
Normal file
20
MeshtasticClient/Views/Messages/UserMessageList.swift
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
//
|
||||
// UserMessageList.swift
|
||||
// MeshtasticClient
|
||||
//
|
||||
// Created by Garth Vander Houwen on 12/24/21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct UserMessageList: View {
|
||||
var body: some View {
|
||||
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
|
||||
}
|
||||
}
|
||||
|
||||
struct UserMessageList_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
UserMessageList()
|
||||
}
|
||||
}
|
||||
|
|
@ -175,7 +175,7 @@ struct NodeDetail: View {
|
|||
}
|
||||
}.padding()
|
||||
|
||||
if node.positions?.count ?? 0 > 0 {
|
||||
if node.positions?.count ?? 0 > 1 {
|
||||
|
||||
Divider()
|
||||
|
||||
|
|
@ -193,52 +193,51 @@ struct NodeDetail: View {
|
|||
|
||||
ForEach(node.positions!.array as! [PositionEntity], id: \.self) { (mappin: PositionEntity) in
|
||||
|
||||
//if mappin.coordinate != nil {
|
||||
if mappin.coordinate != nil {
|
||||
|
||||
VStack {
|
||||
|
||||
HStack {
|
||||
|
||||
Image(systemName: "mappin.and.ellipse").foregroundColor(.accentColor) //.font(.subheadline)
|
||||
Text("Lat/Long:").font(.caption)
|
||||
Text("\(String(mappin.latitude ?? 0)) \(String(mappin.longitude ?? 0))")
|
||||
.foregroundColor(.gray)
|
||||
.font(.caption)
|
||||
HStack {
|
||||
|
||||
Text("Altitude:")
|
||||
.font(.caption)
|
||||
|
||||
Text("\(String(mappin.altitude))m")
|
||||
.foregroundColor(.gray)
|
||||
.font(.caption)
|
||||
Image(systemName: "mappin.and.ellipse").foregroundColor(.accentColor) //.font(.subheadline)
|
||||
Text("Lat/Long:").font(.caption)
|
||||
Text("\(String(mappin.latitude ?? 0)) \(String(mappin.longitude ?? 0))")
|
||||
.foregroundColor(.gray)
|
||||
.font(.caption)
|
||||
|
||||
Text("Altitude:")
|
||||
.font(.caption)
|
||||
|
||||
Text("\(String(mappin.altitude))m")
|
||||
.foregroundColor(.gray)
|
||||
.font(.caption)
|
||||
}
|
||||
HStack {
|
||||
|
||||
Image(systemName: "clock.badge.checkmark.fill")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.accentColor)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text("Time:")
|
||||
.font(.caption)
|
||||
Text("\(mappin.time!, style: .date) \(mappin.time!, style: .time)")
|
||||
.foregroundColor(.gray)
|
||||
.font(.caption)
|
||||
Divider()
|
||||
|
||||
Text("Battery").font(.caption).fixedSize()
|
||||
Text(String(mappin.batteryLevel) + "%")
|
||||
.font(.caption)
|
||||
.foregroundColor(.gray)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
}
|
||||
}
|
||||
HStack {
|
||||
|
||||
Image(systemName: "clock.badge.checkmark.fill")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.accentColor)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text("Time:")
|
||||
.font(.caption)
|
||||
Text("\(mappin.time!, style: .date) \(mappin.time!, style: .time)")
|
||||
.foregroundColor(.gray)
|
||||
.font(.caption)
|
||||
Divider()
|
||||
|
||||
Text("Battery").font(.caption).fixedSize()
|
||||
Text(String(mappin.batteryLevel) + "%")
|
||||
.font(.caption)
|
||||
.foregroundColor(.gray)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
}
|
||||
}
|
||||
.padding(1)
|
||||
Divider()
|
||||
//}
|
||||
}
|
||||
}
|
||||
.padding(.bottom, 5) // Without some padding here there is a transparent contentview bug
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
.navigationTitle(node.user!.longName ?? "Unknown")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue