From 6a3e6613458a12d07e5859f8078c5da84525a730 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Thu, 16 Dec 2021 14:13:54 -0800 Subject: [PATCH] Checkpoint 2 CoreData upgrade --- Meshtastic Client.xcodeproj/project.pbxproj | 32 +- .../xcdebugger/Breakpoints_v2.xcbkptlist | 36 +- MeshtasticClient/Helpers/BLEManager.swift | 47 +-- MeshtasticClient/Helpers/MeshLogger.swift | 2 +- MeshtasticClient/Model/MeshData.swift | 60 --- MeshtasticClient/Model/PeripheralModel.swift | 10 +- .../Persistence/PositionEntityExtension.swift | 45 +++ .../Views/Bluetooth/Connect.swift | 20 +- .../Views/Messages/Messages.swift | 29 +- MeshtasticClient/Views/Nodes/NodeDetail.swift | 353 +++++++++--------- .../Views/Nodes/NodeInfoEntityDetail.swift | 175 --------- MeshtasticClient/Views/Nodes/NodeList.swift | 21 +- MeshtasticClient/Views/Nodes/NodeMap.swift | 48 +-- MeshtasticClient/Views/Nodes/NodeRow.swift | 128 +++---- .../Views/Settings/AppSettings.swift | 7 +- 15 files changed, 416 insertions(+), 597 deletions(-) delete mode 100644 MeshtasticClient/Model/MeshData.swift create mode 100644 MeshtasticClient/Persistence/PositionEntityExtension.swift delete mode 100644 MeshtasticClient/Views/Nodes/NodeInfoEntityDetail.swift diff --git a/Meshtastic Client.xcodeproj/project.pbxproj b/Meshtastic Client.xcodeproj/project.pbxproj index bbad33eb..b8d5ce3c 100644 --- a/Meshtastic Client.xcodeproj/project.pbxproj +++ b/Meshtastic Client.xcodeproj/project.pbxproj @@ -9,11 +9,9 @@ /* Begin PBXBuildFile section */ DD23A50F26FD1B4400D9B90C /* PeripheralModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD23A50E26FD1B4400D9B90C /* PeripheralModel.swift */; }; DD23A51326FEF5D500D9B90C /* MessageData.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD23A51226FEF5D500D9B90C /* MessageData.swift */; }; - DD2E652427679E4000E45FC5 /* NodeInfoEntityRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2E652327679E4000E45FC5 /* NodeInfoEntityRow.swift */; }; - DD2E65262767A01F00E45FC5 /* NodeInfoEntityDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2E65252767A01F00E45FC5 /* NodeInfoEntityDetail.swift */; }; - DD47E3CC26F0E51D00029299 /* NodeDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD47E3CB26F0E51D00029299 /* NodeDetail.swift */; }; + DD2E652427679E4000E45FC5 /* NodeRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2E652327679E4000E45FC5 /* NodeRow.swift */; }; + DD2E65262767A01F00E45FC5 /* NodeDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2E65252767A01F00E45FC5 /* NodeDetail.swift */; }; DD47E3CE26F103C600029299 /* NodeList.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD47E3CD26F103C600029299 /* NodeList.swift */; }; - DD47E3D026F1073F00029299 /* NodeRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD47E3CF26F1073F00029299 /* NodeRow.swift */; }; DD47E3D626F17ED900029299 /* CircleText.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD47E3D526F17ED900029299 /* CircleText.swift */; }; DD47E3D926F3093800029299 /* MessageBubble.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD47E3D826F3093800029299 /* MessageBubble.swift */; }; DD47E3DB26F3901B00029299 /* Channels.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD47E3DA26F3901A00029299 /* Channels.swift */; }; @@ -22,11 +20,11 @@ DD4A911E2708C65400501B7E /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD4A911D2708C65400501B7E /* AppSettings.swift */; }; DD4A91202708C66600501B7E /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD4A911F2708C66600501B7E /* Configuration.swift */; }; DD5394FC276993AD00AD86B1 /* SwiftProtobuf in Frameworks */ = {isa = PBXBuildFile; productRef = DD5394FB276993AD00AD86B1 /* SwiftProtobuf */; }; + DD5394FE276BA0EF00AD86B1 /* PositionEntityExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD5394FD276BA0EF00AD86B1 /* PositionEntityExtension.swift */; }; DD8169F9271F1A6100F4AB02 /* MeshLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8169F8271F1A6100F4AB02 /* MeshLogger.swift */; }; 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 */; }; - DD836AED26F858F900ABCC23 /* MeshData.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD836AEC26F858F900ABCC23 /* MeshData.swift */; }; DD836AEF26F85D8D00ABCC23 /* NodeInfoModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD836AEE26F85D8D00ABCC23 /* NodeInfoModel.swift */; }; DD90860C26F684AF00DC5189 /* BatteryIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD90860B26F684AF00DC5189 /* BatteryIcon.swift */; }; DD90860E26F69BAE00DC5189 /* NodeMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD90860D26F69BAE00DC5189 /* NodeMap.swift */; }; @@ -75,11 +73,9 @@ /* Begin PBXFileReference section */ DD23A50E26FD1B4400D9B90C /* PeripheralModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeripheralModel.swift; sourceTree = ""; }; DD23A51226FEF5D500D9B90C /* MessageData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageData.swift; sourceTree = ""; }; - DD2E652327679E4000E45FC5 /* NodeInfoEntityRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeInfoEntityRow.swift; sourceTree = ""; }; - DD2E65252767A01F00E45FC5 /* NodeInfoEntityDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeInfoEntityDetail.swift; sourceTree = ""; }; - DD47E3CB26F0E51D00029299 /* NodeDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeDetail.swift; sourceTree = ""; }; + DD2E652327679E4000E45FC5 /* NodeRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeRow.swift; sourceTree = ""; }; + DD2E65252767A01F00E45FC5 /* NodeDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeDetail.swift; sourceTree = ""; }; DD47E3CD26F103C600029299 /* NodeList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeList.swift; sourceTree = ""; }; - DD47E3CF26F1073F00029299 /* NodeRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeRow.swift; sourceTree = ""; }; DD47E3D526F17ED900029299 /* CircleText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircleText.swift; sourceTree = ""; }; DD47E3D826F3093800029299 /* MessageBubble.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageBubble.swift; sourceTree = ""; }; DD47E3DA26F3901A00029299 /* Channels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Channels.swift; sourceTree = ""; }; @@ -87,11 +83,11 @@ DD47E3DE26F39D9F00029299 /* MyInfoModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyInfoModel.swift; sourceTree = ""; }; DD4A911D2708C65400501B7E /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = ""; }; DD4A911F2708C66600501B7E /* Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Configuration.swift; sourceTree = ""; }; + DD5394FD276BA0EF00AD86B1 /* PositionEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionEntityExtension.swift; sourceTree = ""; }; DD8169F8271F1A6100F4AB02 /* MeshLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshLogger.swift; sourceTree = ""; }; DD8169FA271F1F3A00F4AB02 /* MeshLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshLog.swift; sourceTree = ""; }; DD8169FE272476C700F4AB02 /* LogDocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogDocument.swift; sourceTree = ""; }; DD836AE626F6B38600ABCC23 /* Connect.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Connect.swift; sourceTree = ""; }; - DD836AEC26F858F900ABCC23 /* MeshData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshData.swift; sourceTree = ""; }; DD836AEE26F85D8D00ABCC23 /* NodeInfoModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeInfoModel.swift; sourceTree = ""; }; DD90860A26F645B700DC5189 /* MeshtasticClient.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MeshtasticClient.entitlements; sourceTree = ""; }; DD90860B26F684AF00DC5189 /* BatteryIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryIcon.swift; sourceTree = ""; }; @@ -156,12 +152,10 @@ DD47E3CA26F0E50300029299 /* Nodes */ = { isa = PBXGroup; children = ( - DD47E3CB26F0E51D00029299 /* NodeDetail.swift */, DD47E3CD26F103C600029299 /* NodeList.swift */, - DD47E3CF26F1073F00029299 /* NodeRow.swift */, DD90860D26F69BAE00DC5189 /* NodeMap.swift */, - DD2E652327679E4000E45FC5 /* NodeInfoEntityRow.swift */, - DD2E65252767A01F00E45FC5 /* NodeInfoEntityDetail.swift */, + DD2E652327679E4000E45FC5 /* NodeRow.swift */, + DD2E65252767A01F00E45FC5 /* NodeDetail.swift */, ); path = Nodes; sourceTree = ""; @@ -292,7 +286,6 @@ children = ( DD47E3DE26F39D9F00029299 /* MyInfoModel.swift */, DDF924C526FA2375009FE055 /* MessageModel.swift */, - DD836AEC26F858F900ABCC23 /* MeshData.swift */, DD836AEE26F85D8D00ABCC23 /* NodeInfoModel.swift */, DD23A50E26FD1B4400D9B90C /* PeripheralModel.swift */, DD23A51226FEF5D500D9B90C /* MessageData.swift */, @@ -344,6 +337,7 @@ isa = PBXGroup; children = ( DDC4D567275499A500A4208E /* Persistence.swift */, + DD5394FD276BA0EF00AD86B1 /* PositionEntityExtension.swift */, ); path = Persistence; sourceTree = ""; @@ -506,20 +500,19 @@ files = ( DD836AE726F6B38600ABCC23 /* Connect.swift in Sources */, DDAF8C6E26ED19040058C060 /* Extensions.swift in Sources */, - DD47E3CC26F0E51D00029299 /* NodeDetail.swift in Sources */, DD836AEF26F85D8D00ABCC23 /* NodeInfoModel.swift in Sources */, DDC2E1A726CEB3400042C5E4 /* LocationHelper.swift in Sources */, DDAF8C5F26ED09B50058C060 /* radioconfig.pb.swift in Sources */, + DD5394FE276BA0EF00AD86B1 /* PositionEntityExtension.swift in Sources */, DD913639270DFF4C00D7ACF3 /* LocalNotificationManager.swift in Sources */, DDAF8C5326EB1DF10058C060 /* BLEManager.swift in Sources */, DDC4D568275499A500A4208E /* Persistence.swift in Sources */, - DD2E652427679E4000E45FC5 /* NodeInfoEntityRow.swift in Sources */, + DD2E652427679E4000E45FC5 /* NodeRow.swift in Sources */, DD90860E26F69BAE00DC5189 /* NodeMap.swift in Sources */, DD47E3DB26F3901B00029299 /* Channels.swift in Sources */, DD8169FF272476C700F4AB02 /* LogDocument.swift in Sources */, DDAF8C6926ED0D070058C060 /* deviceonly.pb.swift in Sources */, DD23A51326FEF5D500D9B90C /* MessageData.swift in Sources */, - DD836AED26F858F900ABCC23 /* MeshData.swift in Sources */, DDAF8C6B26ED0DD80058C060 /* environmental_measurement.pb.swift in Sources */, DD90860C26F684AF00DC5189 /* BatteryIcon.swift in Sources */, DD4A91202708C66600501B7E /* Configuration.swift in Sources */, @@ -531,14 +524,13 @@ DD47E3DF26F39D9F00029299 /* MyInfoModel.swift in Sources */, DD23A50F26FD1B4400D9B90C /* PeripheralModel.swift in Sources */, DD47E3CE26F103C600029299 /* NodeList.swift in Sources */, - DD47E3D026F1073F00029299 /* NodeRow.swift in Sources */, DD47E3D626F17ED900029299 /* CircleText.swift in Sources */, DDF924C626FA2375009FE055 /* MessageModel.swift in Sources */, DDC2E18F26CE25FE0042C5E4 /* ContentView.swift in Sources */, DDAF8C6326ED0A230058C060 /* admin.pb.swift in Sources */, DDAF8C5826ED07FD0058C060 /* mesh.pb.swift in Sources */, DD8169FB271F1F3A00F4AB02 /* MeshLog.swift in Sources */, - DD2E65262767A01F00E45FC5 /* NodeInfoEntityDetail.swift in Sources */, + DD2E65262767A01F00E45FC5 /* NodeDetail.swift in Sources */, DD47E3D926F3093800029299 /* MessageBubble.swift in Sources */, DD8169F9271F1A6100F4AB02 /* MeshLogger.swift in Sources */, DDAF8C6726ED0C8C0058C060 /* remote_hardware.pb.swift in Sources */, diff --git a/Meshtastic Client.xcodeproj/xcuserdata/garthvanderhouwen.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Meshtastic Client.xcodeproj/xcuserdata/garthvanderhouwen.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 4612af2b..1583e051 100644 --- a/Meshtastic Client.xcodeproj/xcuserdata/garthvanderhouwen.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/Meshtastic Client.xcodeproj/xcuserdata/garthvanderhouwen.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -72,8 +72,8 @@ filePath = "MeshtasticClient/Helpers/BLEManager.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "839" - endingLineNumber = "839" + startingLineNumber = "824" + endingLineNumber = "824" landmarkName = "sendMessage(message:)" landmarkType = "7"> @@ -88,8 +88,8 @@ filePath = "MeshtasticClient/Helpers/BLEManager.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "862" - endingLineNumber = "862" + startingLineNumber = "847" + endingLineNumber = "847" landmarkName = "sendMessage(message:)" landmarkType = "7"> @@ -113,33 +113,17 @@ - - - - + startingLineNumber = "447" + endingLineNumber = "447" + landmarkName = "peripheral(_:didUpdateValueFor:error:)" + landmarkType = "7"> diff --git a/MeshtasticClient/Helpers/BLEManager.swift b/MeshtasticClient/Helpers/BLEManager.swift index d2e979f9..ff46d2b1 100644 --- a/MeshtasticClient/Helpers/BLEManager.swift +++ b/MeshtasticClient/Helpers/BLEManager.swift @@ -109,7 +109,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph self.timeoutTimerCount += 1 - if timeoutTimerCount == 6 { + if timeoutTimerCount == 10 { if connectedPeripheral != nil { @@ -167,16 +167,16 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph peripheralName = name } - let newPeripheral = Peripheral(id: peripheral.identifier.uuidString, name: peripheralName, firmwareVersion: "Unknown", rssi: RSSI.intValue, subscribed: false, peripheral: peripheral, myInfo: nil) + let newPeripheral = Peripheral(id: peripheral.identifier.uuidString, name: peripheralName, shortName: "", longName: "", firmwareVersion: "Unknown", rssi: RSSI.intValue, subscribed: false, peripheral: peripheral) let peripheralIndex = peripherals.firstIndex(where: { $0.id == newPeripheral.id }) if peripheralIndex != nil && newPeripheral.peripheral.state != CBPeripheralState.connected { - //newPeripheral.myInfo = peripherals.first(where: { $0.id == newPeripheral.id })?.myInfo peripherals[peripheralIndex!] = newPeripheral peripherals.remove(at: peripheralIndex!) peripherals.append(newPeripheral) print("Updating peripheral: \(peripheralName)") + } else { if newPeripheral.peripheral.state != CBPeripheralState.connected { @@ -190,7 +190,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph // Called when a peripheral is connected func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { - // guard let connectedPeripheral = connectedPeripheral else { return } self.isConnected = true // Invalidate and reset connection timer count, remove any connection errors @@ -202,15 +201,16 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph connectedPeripheral = peripherals.filter({ $0.peripheral.identifier == peripheral.identifier }).first connectedPeripheral.peripheral.delegate = self - let fetchNodeRequest:NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") - fetchNodeRequest.predicate = NSPredicate(format: "bleName MATCHES %@", String(peripheral.name ?? "???")) + let fetchConnectedPeripheralRequest:NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") + fetchConnectedPeripheralRequest.predicate = NSPredicate(format: "bleName MATCHES %@", String(peripheral.name ?? "???")) do { - let fetchedNode = try context?.fetch(fetchNodeRequest) as! [NodeInfoEntity] + let fetchedNode = try context?.fetch(fetchConnectedPeripheralRequest) as! [NodeInfoEntity] if fetchedNode.count == 1 { - connectedPeripheral.name = fetchedNode[0].user!.longName! + connectedPeripheral.shortName = fetchedNode[0].user!.shortName! + connectedPeripheral.longName = fetchedNode[0].user!.longName! connectedPeripheral.firmwareVersion = (fetchedNode[0].myInfo?.firmwareVersion ?? "Unknown") } @@ -395,7 +395,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph print("Save a CoreData MyInfoEntity") let fetchMyInfoRequest:NSFetchRequest = NSFetchRequest.init(entityName: "MyInfoEntity") - fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %i", Int64(decodedInfo.myInfo.myNodeNum)) + fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(decodedInfo.myInfo.myNodeNum)) do { let fetchedMyInfo = try context?.fetch(fetchMyInfoRequest) as! [MyInfoEntity] @@ -493,7 +493,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph // Look for a MyInfo let fetchMyInfoRequest:NSFetchRequest = NSFetchRequest.init(entityName: "MyInfoEntity") - fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %i", Int64(decodedInfo.nodeInfo.num)) + fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", Int64(decodedInfo.nodeInfo.num)) do { @@ -661,15 +661,12 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph } } else if decodedInfo.packet.decoded.portnum == PortNum.nodeinfoApp { - //let fetchNodeInfoAppRequest:NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") - //fetchNodeInfoAppRequest.predicate = NSPredicate(format: "num == %i", Int64(decodedInfo.packet.from)) - - let fetchNodeRequest:NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") - fetchNodeRequest.predicate = NSPredicate(format: "num == %lld", Int64(decodedInfo.packet.from)) + let fetchNodeInfoAppRequest:NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") + fetchNodeInfoAppRequest.predicate = NSPredicate(format: "num == %lld", Int64(decodedInfo.packet.from)) do { - let fetchedNode = try context?.fetch(fetchNodeRequest) as! [NodeInfoEntity] + let fetchedNode = try context?.fetch(fetchNodeInfoAppRequest) as! [NodeInfoEntity] if fetchedNode.count == 1 { fetchedNode[0].id = Int64(decodedInfo.nodeInfo.num) @@ -686,7 +683,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph try context!.save() if meshLoggingEnabled { - MeshLogger.log("MESH PACKET Updated NodeInfo SNR and Time from Node Info App Packet For: \(fetchedNode[0].num)") + 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)") @@ -696,17 +693,15 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph let nsError = error as NSError print("Error Saving NodeInfoEntity from NODEINFO_APP \(nsError)") + } } catch { print("Error Fetching NodeInfoEntity for NODEINFO_APP") } - - + print(decodedInfo.packet.decoded.payload) - - } else if decodedInfo.packet.decoded.portnum == PortNum.positionApp { let fetchNodePositionRequest:NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") @@ -746,16 +741,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph print("Error Fetching NodeInfoEntity for NODEINFO_APP") } - - - print(decodedInfo.packet.decoded.payload) -// if meshLoggingEnabled { -// MeshLogger.log("MESH PACKET Updated NodeInfo SNR and Time from Position App Packet For: \(updatedNode.num)") -// } -// print("Updated NodeInfo SNR and Time from Packet For: \(updatedNode.num)") -// -// print("Postion Payload") -// print(try decodedInfo.packet.jsonString()) } else if decodedInfo.packet.decoded.portnum == PortNum.adminApp { diff --git a/MeshtasticClient/Helpers/MeshLogger.swift b/MeshtasticClient/Helpers/MeshLogger.swift index 2dc383d6..76d7ef8f 100644 --- a/MeshtasticClient/Helpers/MeshLogger.swift +++ b/MeshtasticClient/Helpers/MeshLogger.swift @@ -1,7 +1,7 @@ import Foundation class MeshLogger { - + static var logFile: URL? { guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return nil } let fileName = "mesh.log" diff --git a/MeshtasticClient/Model/MeshData.swift b/MeshtasticClient/Model/MeshData.swift deleted file mode 100644 index 75e7fffd..00000000 --- a/MeshtasticClient/Model/MeshData.swift +++ /dev/null @@ -1,60 +0,0 @@ -import Foundation - -class MeshData: ObservableObject { - private static var documentsFolder: URL { - do { - return try FileManager.default.url( - for: .documentDirectory, - in: .userDomainMask, - appropriateFor: nil, - create: true) - } catch { - fatalError("Can't find documents directory.") - } - } - - private static var fileURL: URL { - return documentsFolder.appendingPathComponent("nodeinfo.data") - } - - @Published var nodes: [NodeInfoModel] = [] - - func load() { - DispatchQueue.global(qos: .background).async { [weak self] in - guard let data = try? Data(contentsOf: Self.fileURL) else { - #if DEBUG - DispatchQueue.main.async { - self?.nodes = NodeInfoModel.data - } - #endif - return - } - guard let nodeList = try? JSONDecoder().decode([NodeInfoModel].self, from: data) else { - do { - // If the file is borked delete it so we stop crashing - try FileManager.default.removeItem(at: Self.fileURL) - } catch { - - fatalError("Can't delete saved node data.") - } - - fatalError("Can't decode saved node data.") - } - DispatchQueue.main.async { - self?.nodes = nodeList - } - } - } - func save() { - DispatchQueue.global(qos: .background).async { [weak self] in - guard let scrums = self?.nodes else { fatalError("Self out of scope") } - guard let data = try? JSONEncoder().encode(scrums) else { fatalError("Error encoding data") } - do { - let outfile = Self.fileURL - try data.write(to: outfile) - } catch { - fatalError("Can't write to file") - } - } - } -} diff --git a/MeshtasticClient/Model/PeripheralModel.swift b/MeshtasticClient/Model/PeripheralModel.swift index 695b2d14..a02855b2 100644 --- a/MeshtasticClient/Model/PeripheralModel.swift +++ b/MeshtasticClient/Model/PeripheralModel.swift @@ -4,20 +4,24 @@ import CoreBluetooth struct Peripheral: Identifiable { var id: String var name: String + var shortName: String + var longName: String var firmwareVersion: String var rssi: Int var subscribed: Bool var peripheral: CBPeripheral - var myInfo: MyInfoModel? + //var myInfo: MyInfoModel? - init(id: String, name: String, firmwareVersion: String, rssi: Int, subscribed: Bool, peripheral: CBPeripheral, myInfo: MyInfoModel?) { + init(id: String, name: String, shortName: String, longName: String, firmwareVersion: String, rssi: Int, subscribed: Bool, peripheral: CBPeripheral) {//, myInfo: MyInfoModel?) { self.id = id self.name = name + self.shortName = shortName + self.longName = longName self.firmwareVersion = firmwareVersion self.rssi = rssi self.subscribed = subscribed self.peripheral = peripheral - self.myInfo = myInfo + //self.myInfo = myInfo } } diff --git a/MeshtasticClient/Persistence/PositionEntityExtension.swift b/MeshtasticClient/Persistence/PositionEntityExtension.swift new file mode 100644 index 00000000..ec5ccd6e --- /dev/null +++ b/MeshtasticClient/Persistence/PositionEntityExtension.swift @@ -0,0 +1,45 @@ +import CoreData +import CoreLocation +import MapKit +import SwiftUI + +extension PositionEntity { + + var latitude: Double? { + + let d = Double(latitudeI) + if d == 0 { + return nil + } + return d / 1e7 + } + + var longitude: Double? { + + let d = Double(longitudeI) + if d == 0 { + return nil + } + return d / 1e7 + } + + var coordinate: CLLocationCoordinate2D? { + if latitude != nil && longitude != nil { + let coord = CLLocationCoordinate2D(latitude: latitude!, longitude: longitude!) + + return coord + } else { + return nil + } + } + + var annotaton: MKPointAnnotation { + let pointAnn = MKPointAnnotation() + if coordinate != nil { + + pointAnn.coordinate = coordinate! + } + return pointAnn + } + +} diff --git a/MeshtasticClient/Views/Bluetooth/Connect.swift b/MeshtasticClient/Views/Bluetooth/Connect.swift index d7e798e6..d13995b2 100644 --- a/MeshtasticClient/Views/Bluetooth/Connect.swift +++ b/MeshtasticClient/Views/Bluetooth/Connect.swift @@ -52,17 +52,17 @@ struct Connect: View { if bleManager.connectedPeripheral != nil { - Text(bleManager.connectedPeripheral.name).font(.title2) + Text(bleManager.connectedPeripheral.longName).font(.title2) + } else { Text(String(bleManager.connectedPeripheral.peripheral.name ?? "Unknown")).font(.title2) } + Text("BLE Name: ").font(.caption)+Text(bleManager.connectedPeripheral.name) + .font(.caption).foregroundColor(Color.gray) if bleManager.connectedPeripheral != nil { - //Text("Model: ").font(.caption)+Text(bleManager.connectedNode?.user!.hwModel ?? "(null)").font(.caption).foregroundColor(Color.gray) - } - Text("BLE Name: ").font(.caption)+Text(bleManager.connectedPeripheral.name).font(.caption).foregroundColor(Color.gray) - if bleManager.connectedPeripheral != nil { - //Text("FW Version: ").font(.caption)+Text(bleManager.connectedPeripheral.myInfo?.firmwareVersion ?? "(null)").font(.caption).foregroundColor(Color.gray) + Text("FW Version: ").font(.caption)+Text(bleManager.connectedPeripheral.firmwareVersion) + .font(.caption).foregroundColor(Color.gray) } if bleManager.connectedPeripheral.subscribed { Text("Properly Subscribed").font(.caption) @@ -200,8 +200,12 @@ struct Connect: View { .navigationBarItems(trailing: ZStack { - - //ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedNode != nil) ? bleManager.connectedNode.user.shortName : ((bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.name : "Unknown") ) + + ConnectedDevice( + bluetoothOn: bleManager.isSwitchedOn, + deviceConnected: bleManager.connectedPeripheral != nil, + name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : + "???") } ) } diff --git a/MeshtasticClient/Views/Messages/Messages.swift b/MeshtasticClient/Views/Messages/Messages.swift index 5bb14b74..555e5135 100644 --- a/MeshtasticClient/Views/Messages/Messages.swift +++ b/MeshtasticClient/Views/Messages/Messages.swift @@ -57,6 +57,7 @@ struct Messages: View { print("I want to delete message: \(message.messageId)") self.showDeleteMessageAlert = true self.deleteMessageId = message.messageId + print(deleteMessageId) }) @@ -89,13 +90,19 @@ struct Messages: View { print("OK button tapped") if deleteMessageId > 0 { - //let message = messages.first.where: { $0.messageId == deleteMessageId }) - //context.delete(object: message) - //bleManager.messageData.messages.remove(at: messageIndex!) - //bleManager.messageData.save() - //print("Deleted message: \(message.messageId)") - //showDeleteMessageAlert = false - deleteMessageId = 0 + let message = messages.first(where: { $0.messageId == deleteMessageId }) + + context.delete(message!) + do { + + try context.save() + deleteMessageId = 0 + messageCount = messages.count + + } catch { + print("Failed to delete message \(deleteMessageId)") + } + } }, secondaryButton: .cancel() @@ -194,12 +201,16 @@ struct Messages: View { } } .navigationTitle("Channel - Primary") - //.navigationBarTitleDisplayMode(.inline) + .navigationBarTitleDisplayMode(.inline) .navigationBarItems(trailing: ZStack { - //ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedNode != nil) ? bleManager.connectedNode.user.shortName : ((bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.name : "Unknown") ) + ConnectedDevice( + bluetoothOn: bleManager.isSwitchedOn, + deviceConnected: bleManager.connectedPeripheral != nil, + name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : + "???") }) .onAppear(perform: { diff --git a/MeshtasticClient/Views/Nodes/NodeDetail.swift b/MeshtasticClient/Views/Nodes/NodeDetail.swift index 3563b7d0..e19bd30d 100644 --- a/MeshtasticClient/Views/Nodes/NodeDetail.swift +++ b/MeshtasticClient/Views/Nodes/NodeDetail.swift @@ -1,176 +1,187 @@ -///* -//Abstract: -//A view showing the details for a node. -//*/ +/* +Abstract: +A view showing the details for a node. +*/ + +import SwiftUI +import MapKit +import CoreLocation + +struct NodeDetail: View { + + // CoreData + @Environment(\.managedObjectContext) var context + @EnvironmentObject var bleManager: BLEManager + + var node: NodeInfoEntity + + struct MapLocation: Identifiable { + let id = UUID() + let name: String + let coordinate: CLLocationCoordinate2D + } + + var body: some View { + + GeometryReader { bounds in + + VStack { + + if node.positions != nil && node.positions!.count > 0 { + +// let nodeCoordinatePosition = CLLocationCoordinate2D(latitude: node.position.latitude!, longitude: node.position.longitude!) // -//import SwiftUI -//import MapKit -//import CoreLocation +// let regionBinding = Binding( +// get: { +// MKCoordinateRegion(center: nodeCoordinatePosition, span: MKCoordinateSpan(latitudeDelta: 0.005, longitudeDelta: 0.005)) +// }, +// set: { _ in } +// ) +// let annotations = [MapLocation(name: node.user.shortName, coordinate: node.position.coordinate!)] // -//struct NodeDetail: View { -// -// @EnvironmentObject var bleManager: BLEManager -// -// var node: NodeInfoModel -// -// struct MapLocation: Identifiable { -// let id = UUID() -// let name: String -// let coordinate: CLLocationCoordinate2D -// } -// -// var body: some View { -// -// GeometryReader { bounds in -// -// VStack { -// -// if node.position.coordinate != nil { -// -// let nodeCoordinatePosition = CLLocationCoordinate2D(latitude: node.position.latitude!, longitude: node.position.longitude!) -// -// let regionBinding = Binding( -// get: { -// MKCoordinateRegion(center: nodeCoordinatePosition, span: MKCoordinateSpan(latitudeDelta: 0.005, longitudeDelta: 0.005)) -// }, -// set: { _ in } -// ) -// let annotations = [MapLocation(name: node.user.shortName, coordinate: node.position.coordinate!)] -// -// Map(coordinateRegion: regionBinding, showsUserLocation: true, userTrackingMode: .none, annotationItems: annotations) { location in -// MapAnnotation( -// coordinate: location.coordinate, -// content: { +// Map(coordinateRegion: regionBinding, showsUserLocation: true, userTrackingMode: .none, annotationItems: annotations) { location in +// MapAnnotation( +// coordinate: location.coordinate, +// content: { // CircleText(text: node.user.shortName, color: .accentColor) -// } -// ) -// }.frame(idealWidth: bounds.size.width, minHeight: bounds.size.height / 2) -// } else { -// Image(node.user.hwModel) -// .resizable() -// .aspectRatio(contentMode: .fit) -// .frame(width: bounds.size.width, height: bounds.size.height / 2) -// } -// ScrollView { -// -// HStack { -// -// VStack(alignment: .center) { -// Text("AKA").font(.title2).fixedSize() -// CircleText(text: node.user.shortName, color: .accentColor) -// .offset(y: 10) -// } -// .padding([.leading, .trailing, .bottom]) -// Divider() -// if node.snr != nil && node.snr! > 0 { -// VStack(alignment: .center) { -// -// Image(systemName: "waveform.path") -// .font(.title) -// .foregroundColor(.accentColor) -// .symbolRenderingMode(.hierarchical) -// Text("SNR").font(.title2).fixedSize() -// Text(String(node.snr ?? 0)) -// .font(.title2) -// .foregroundColor(.gray) -// } -// Divider() -// } -// VStack(alignment: .center) { +// } +// ) +// }.frame(idealWidth: bounds.size.width, minHeight: bounds.size.height / 2) + } else { + Image(node.user?.hwModel ?? "UNSET") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: bounds.size.width, height: bounds.size.height / 2) + } + ScrollView { + + HStack { + + VStack(alignment: .center) { + Text("AKA").font(.title2).fixedSize() + CircleText(text: node.user?.shortName ?? "???", color: .accentColor) + .offset(y: 10) + } + .padding([.leading, .trailing, .bottom]) + Divider() + if node.snr > 0 { + VStack(alignment: .center) { + + Image(systemName: "waveform.path") + .font(.title) + .foregroundColor(.accentColor) + .symbolRenderingMode(.hierarchical) + Text("SNR").font(.title2).fixedSize() + Text(String(node.snr)) + .font(.title2) + .foregroundColor(.gray) + } + Divider() + } +// VStack(alignment: .center) { // BatteryIcon(batteryLevel: node.position.batteryLevel, font: .title, color: .accentColor) -// if node.position.batteryLevel != nil && node.position.batteryLevel! > 0 { -// Text("Battery").font(.title2).fixedSize() -// Text(String(node.position.batteryLevel!) + "%") -// .font(.title2) -// .foregroundColor(.gray) -// .symbolRenderingMode(.hierarchical) -// } else { -// Text("Powered").font(.title2) -// } -// } -// -// }.padding(4) -// Divider() -// HStack { -// -// Image(node.user.hwModel) -// .resizable() -// .frame(width: 60, height: 60) -// .cornerRadius(5) -// -// Text("Model: " + String(node.user.hwModel)) -// .font(.title3) -// } -// .padding() -// Divider() -// -// if node.lastHeard > 0 { -// -// HStack { -// -// Image(systemName: "clock").font(.title2).foregroundColor(.accentColor) -// let lastHeard = Date(timeIntervalSince1970: TimeInterval(node.lastHeard)) -// Text("Last Heard: \(lastHeard, style: .relative) ago").font(.title3) -// }.padding() -// Divider() -// } -// -// if node.position.coordinate != nil { -// HStack(alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/, spacing: 14) { +// if node.position.batteryLevel != nil && node.position.batteryLevel! > 0 { +// Text("Battery").font(.title2).fixedSize() +// Text(String(node.position.batteryLevel!) + "%") +// .font(.title2) +// .foregroundColor(.gray) +// .symbolRenderingMode(.hierarchical) +// } else { +// Text("Powered").font(.title2) +// } +// } + + }.padding(4) + Divider() + HStack { + + Image(node.user!.hwModel ?? "UNSET") + .resizable() + .frame(width: 60, height: 60) + .cornerRadius(5) + + Text("Model: " + String(node.user!.hwModel ?? "UNSET")) + .font(.title3) + } + .padding() + Divider() + + if node.lastHeard > 0 { + + HStack { + + Image(systemName: "clock").font(.title2).foregroundColor(.accentColor) + let lastHeard = Date(timeIntervalSince1970: TimeInterval(node.lastHeard)) + Text("Last Heard: \(lastHeard, style: .relative) ago").font(.title3) + }.padding() + Divider() + } + +// if node.position.coordinate != nil { +// HStack(alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/, spacing: 14) { // Image(systemName: "mappin").font(.title).foregroundColor(.accentColor) -// VStack(alignment: .leading) { -// Text("Latitude").font(.headline) -// Text(String(node.position.latitude ?? 0)).font(.caption).foregroundColor(.gray) -// } -// Divider() -// VStack(alignment: .leading) { -// Text("Longitude").font(.headline) -// Text(String(node.position.longitude ?? 0)).font(.caption).foregroundColor(.gray) -// } -// Divider() -// VStack(alignment: .leading) { -// Text("Altitude").font(.headline) -// Text(String(node.position.altitude ?? 0) + " m").font(.caption).foregroundColor(.gray) -// } -// }.padding() -// Divider() -// } -// HStack(alignment: .center) { -// VStack { -// HStack { -// Image(systemName: "person").font(.title3).foregroundColor(.accentColor) -// Text("Unique Id:").font(.title3) -// } -// Text(node.user.id).font(.headline).foregroundColor(.gray) -// } -// Divider() -// VStack { -// HStack { -// Image(systemName: "number").font(.title3).foregroundColor(.accentColor) -// Text("Node Number:").font(.title3) -// } -// Text(String(node.num)).font(.headline).foregroundColor(.gray) -// } -// }.padding() -// } -// }.navigationTitle(node.user.longName) -// .navigationBarTitleDisplayMode(.inline) -// .navigationBarItems(trailing: -// -// ZStack { -// // ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedNode != nil) ? bleManager.connectedNode.user.shortName : ((bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.name : "Unknown") ) -// }) -// }.ignoresSafeArea(.all, edges: [.leading, .trailing]) -// } -//} -// -//struct NodeDetail_Previews: PreviewProvider { -// static let bleManager = BLEManager() -// -// static var previews: some View { -// Group { -// NodeDetail(node: bleManager.meshData.nodes[0]) -// NodeDetail(node: bleManager.meshData.nodes[1]) -// } -// } -//} +// VStack(alignment: .leading) { +// Text("Latitude").font(.headline) +// Text(String(node.position.latitude ?? 0)).font(.caption).foregroundColor(.gray) +// } +// Divider() +// VStack(alignment: .leading) { +// Text("Longitude").font(.headline) +// Text(String(node.position.longitude ?? 0)).font(.caption).foregroundColor(.gray) +// } +// Divider() +// VStack(alignment: .leading) { +// Text("Altitude").font(.headline) +// Text(String(node.position.altitude ?? 0) + " m").font(.caption).foregroundColor(.gray) +// } +// }.padding() +// Divider() +// } + HStack(alignment: .center) { + VStack { + HStack { + Image(systemName: "person").font(.title3).foregroundColor(.accentColor) + Text("Unique Id:").font(.title3) + } + Text(node.user?.userId ?? "??????").font(.headline).foregroundColor(.gray) + } + Divider() + VStack { + HStack { + Image(systemName: "number").font(.title3).foregroundColor(.accentColor) + Text("Node Number:").font(.title3) + } + Text(String(node.num)).font(.headline).foregroundColor(.gray) + } + }.padding() + } + }.navigationTitle(node.user!.longName ?? "Unknown") + .navigationBarTitleDisplayMode(.inline) + .navigationBarItems(trailing: + + ZStack { + + ConnectedDevice( + bluetoothOn: bleManager.isSwitchedOn, + deviceConnected: bleManager.connectedPeripheral != nil, + name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : + "???") + }) + .onAppear(perform: { + + self.bleManager.context = context + + }) + }.ignoresSafeArea(.all, edges: [.leading, .trailing]) + } +} + +struct NodeInfoEntityDetail_Previews: PreviewProvider { + static let bleManager = BLEManager() + + static var previews: some View { + Group { + //NodeInfoEntityDetail(node: node) + } + } +} diff --git a/MeshtasticClient/Views/Nodes/NodeInfoEntityDetail.swift b/MeshtasticClient/Views/Nodes/NodeInfoEntityDetail.swift deleted file mode 100644 index 790517d5..00000000 --- a/MeshtasticClient/Views/Nodes/NodeInfoEntityDetail.swift +++ /dev/null @@ -1,175 +0,0 @@ -/* -Abstract: -A view showing the details for a node. -*/ - -import SwiftUI -import MapKit -import CoreLocation - -struct NodeInfoEntityDetail: View { - - @EnvironmentObject var bleManager: BLEManager - - var node: NodeInfoEntity - - struct MapLocation: Identifiable { - let id = UUID() - let name: String - let coordinate: CLLocationCoordinate2D - } - - var body: some View { - - GeometryReader { bounds in - - VStack { - - if node.positions!.count > 0 { - -// let nodeCoordinatePosition = CLLocationCoordinate2D(latitude: node.position.latitude!, longitude: node.position.longitude!) -// -// let regionBinding = Binding( -// get: { -// MKCoordinateRegion(center: nodeCoordinatePosition, span: MKCoordinateSpan(latitudeDelta: 0.005, longitudeDelta: 0.005)) -// }, -// set: { _ in } -// ) -// let annotations = [MapLocation(name: node.user.shortName, coordinate: node.position.coordinate!)] -// -// Map(coordinateRegion: regionBinding, showsUserLocation: true, userTrackingMode: .none, annotationItems: annotations) { location in -// MapAnnotation( -// coordinate: location.coordinate, -// content: { -// CircleText(text: node.user.shortName, color: .accentColor) -// } -// ) -// }.frame(idealWidth: bounds.size.width, minHeight: bounds.size.height / 2) - } else { - Image(node.user?.hwModel ?? "UNSET") - .resizable() - .aspectRatio(contentMode: .fit) - .frame(width: bounds.size.width, height: bounds.size.height / 2) - } - ScrollView { - - HStack { - - VStack(alignment: .center) { - Text("AKA").font(.title2).fixedSize() - CircleText(text: node.user?.shortName ?? "???", color: .accentColor) - .offset(y: 10) - } - .padding([.leading, .trailing, .bottom]) - Divider() - if node.snr > 0 { - VStack(alignment: .center) { - - Image(systemName: "waveform.path") - .font(.title) - .foregroundColor(.accentColor) - .symbolRenderingMode(.hierarchical) - Text("SNR").font(.title2).fixedSize() - Text(String(node.snr)) - .font(.title2) - .foregroundColor(.gray) - } - Divider() - } -// VStack(alignment: .center) { -// BatteryIcon(batteryLevel: node.position.batteryLevel, font: .title, color: .accentColor) -// if node.position.batteryLevel != nil && node.position.batteryLevel! > 0 { -// Text("Battery").font(.title2).fixedSize() -// Text(String(node.position.batteryLevel!) + "%") -// .font(.title2) -// .foregroundColor(.gray) -// .symbolRenderingMode(.hierarchical) -// } else { -// Text("Powered").font(.title2) -// } -// } - - }.padding(4) - Divider() - HStack { - - Image(node.user!.hwModel ?? "UNSET") - .resizable() - .frame(width: 60, height: 60) - .cornerRadius(5) - - Text("Model: " + String(node.user!.hwModel ?? "UNSET")) - .font(.title3) - } - .padding() - Divider() - - if node.lastHeard > 0 { - - HStack { - - Image(systemName: "clock").font(.title2).foregroundColor(.accentColor) - let lastHeard = Date(timeIntervalSince1970: TimeInterval(node.lastHeard)) - Text("Last Heard: \(lastHeard, style: .relative) ago").font(.title3) - }.padding() - Divider() - } - -// if node.position.coordinate != nil { -// HStack(alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/, spacing: 14) { -// Image(systemName: "mappin").font(.title).foregroundColor(.accentColor) -// VStack(alignment: .leading) { -// Text("Latitude").font(.headline) -// Text(String(node.position.latitude ?? 0)).font(.caption).foregroundColor(.gray) -// } -// Divider() -// VStack(alignment: .leading) { -// Text("Longitude").font(.headline) -// Text(String(node.position.longitude ?? 0)).font(.caption).foregroundColor(.gray) -// } -// Divider() -// VStack(alignment: .leading) { -// Text("Altitude").font(.headline) -// Text(String(node.position.altitude ?? 0) + " m").font(.caption).foregroundColor(.gray) -// } -// }.padding() -// Divider() -// } - HStack(alignment: .center) { - VStack { - HStack { - Image(systemName: "person").font(.title3).foregroundColor(.accentColor) - Text("Unique Id:").font(.title3) - } - Text(node.user?.userId ?? "??????").font(.headline).foregroundColor(.gray) - } - Divider() - VStack { - HStack { - Image(systemName: "number").font(.title3).foregroundColor(.accentColor) - Text("Node Number:").font(.title3) - } - Text(String(node.num)).font(.headline).foregroundColor(.gray) - } - }.padding() - } - }.navigationTitle(node.user!.longName ?? "Unknown") - .navigationBarTitleDisplayMode(.inline) - .navigationBarItems(trailing: - - ZStack { - // ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedNode != nil) ? bleManager.connectedNode.user.shortName : ((bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.name : "Unknown") ) - }) - }.ignoresSafeArea(.all, edges: [.leading, .trailing]) - } -} - -struct NodeInfoEntityDetail_Previews: PreviewProvider { - static let bleManager = BLEManager() - - static var previews: some View { - Group { - //NodeInfoEntityDetail(node: node) - } - } -} diff --git a/MeshtasticClient/Views/Nodes/NodeList.swift b/MeshtasticClient/Views/Nodes/NodeList.swift index 3c4f928e..f19e3050 100644 --- a/MeshtasticClient/Views/Nodes/NodeList.swift +++ b/MeshtasticClient/Views/Nodes/NodeList.swift @@ -42,17 +42,28 @@ struct NodeList: View { } else { ForEach( nodes ) { node in let index = nodes.firstIndex(where: { $0.id == node.id }) - NavigationLink(destination: NodeInfoEntityDetail(node: node), tag: String(index!), selection: $selection) { + NavigationLink(destination: NodeDetail(node: node), tag: String(index!), selection: $selection) { - if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.myInfo != nil { + if bleManager.connectedPeripheral != nil { - let connected: Bool = (bleManager.connectedPeripheral.myInfo!.myNodeNum == node.id) - NodeInfoEntityRow(node: node, connected: connected) + let connected: Bool = (bleManager.connectedPeripheral.name == node.bleName) + NodeRow(node: node, connected: connected) } else { - NodeInfoEntityRow(node: node, connected: false) + NodeRow(node: node, connected: false) } } + .swipeActions { + Button { + + context.delete(node) + + } label: { + + Label("Delete from app", systemImage: "trash") + } + .tint(.red) + } } } } diff --git a/MeshtasticClient/Views/Nodes/NodeMap.swift b/MeshtasticClient/Views/Nodes/NodeMap.swift index bd5f9932..3d53d6d3 100644 --- a/MeshtasticClient/Views/Nodes/NodeMap.swift +++ b/MeshtasticClient/Views/Nodes/NodeMap.swift @@ -21,13 +21,6 @@ struct NodeMap: View { animation: .default) private var locationNodes: FetchedResults - - //var locationNodes: [NodeInfoModel]// { - //bleManager.meshData.nodes.filter { node in - // (node.position.coordinate != nil) - // } - //} - struct MapLocation: Identifiable { let id = UUID() @@ -38,6 +31,7 @@ struct NodeMap: View { var body: some View { let location = LocationHelper.currentLocation + let currentCoordinatePosition = CLLocationCoordinate2D(latitude: location.latitude, longitude: location.longitude) let regionBinding = Binding( get: { @@ -49,20 +43,32 @@ struct NodeMap: View { NavigationView { ZStack { -// Map(coordinateRegion: regionBinding, -// interactionModes: [.all], -// showsUserLocation: true, -// userTrackingMode: .constant(.follow), annotationItems: locationNodes) { location in -// -// MapAnnotation( -// coordinate: location.position.coordinate!, -// content: { -// CircleText(text: location.user.shortName, color: .accentColor) -// } -// ) -// } -// .frame(maxHeight: .infinity) -// .ignoresSafeArea(.all, edges: [.leading, .trailing]) + + Map(coordinateRegion: regionBinding, showsUserLocation: true, userTrackingMode: .none) + .frame(maxHeight: .infinity) + //, annotationItems: locationNodes[0].positions?) { location in +// MapAnnotation( +// coordinate: location.coordinate, +// content: { +// CircleText(text: location.latitude, color: .accentColor) +// } +// ) +// }.frame(maxHeight: .infinity) + + //Map(coordinateRegion: regionBinding, + // interactionModes: [.all], + // showsUserLocation: true, + // userTrackingMode: .constant(.follow), annotationItems: locationNodes) { node in + + //MapAnnotation( + //coordinate: node.positions[0].coordinate, + //content: { + // CircleText(text: node.user!.shortName, color: .accentColor) + //} + // ) + //} + //.frame(maxHeight: .infinity) + //.ignoresSafeArea(.all, edges: [.leading, .trailing]) } .navigationTitle("Mesh Map") .navigationBarTitleDisplayMode(.inline) diff --git a/MeshtasticClient/Views/Nodes/NodeRow.swift b/MeshtasticClient/Views/Nodes/NodeRow.swift index d9eb2fa0..160f7613 100644 --- a/MeshtasticClient/Views/Nodes/NodeRow.swift +++ b/MeshtasticClient/Views/Nodes/NodeRow.swift @@ -1,64 +1,64 @@ -//import SwiftUI -// -//struct NodeRow: View { -// var node: NodeInfoModel -// var connected: Bool -// -// var body: some View { -// VStack(alignment: .leading) { -// -// HStack { -// -// CircleText(text: node.user.shortName, color: Color.accentColor).offset(y: 1).padding(.trailing, 5) -// .offset(x: -15) -// -// if UIDevice.current.userInterfaceIdiom == .pad { -// Text(node.user.longName).font(.headline) -// .offset(x: -15) -// } else { -// Text(node.user.longName).font(.title) -// .offset(x: -15) -// } -// } -// .padding(.bottom, 10) -// -// HStack(alignment: .bottom) { -// -// Image(systemName: "clock.badge.checkmark.fill").font(.headline).foregroundColor(.accentColor).symbolRenderingMode(.hierarchical) -// -// if UIDevice.current.userInterfaceIdiom == .pad { -// -// if connected { -// Text("Currently Connected").font(.caption).foregroundColor(Color.accentColor) -// } else if node.lastHeard > 0 { -// let lastHeard = Date(timeIntervalSince1970: TimeInterval(node.lastHeard)) -// Text("Last Heard: \(lastHeard, style: .relative) ago").font(.caption).foregroundColor(.gray) -// } else { -// Text("Last Heard: Unknown").font(.caption).foregroundColor(.gray) -// } -// -// } else { -// if connected { -// Text("Currently Connected").font(.subheadline).foregroundColor(Color.accentColor) -// } else if node.lastHeard > 0 { -// let lastHeard = Date(timeIntervalSince1970: TimeInterval(node.lastHeard)) -// Text("Last Heard: \(lastHeard, style: .relative) ago").font(.subheadline).foregroundColor(.gray) -// } else { -// Text("Last Heard: Unknown").font(.subheadline).foregroundColor(.gray) -// } -// } -// } -// }.padding([.leading, .top, .bottom]) -// } -//} -// -//struct NodeRow_Previews: PreviewProvider { -// static var nodes = BLEManager().meshData.nodes -// -// static var previews: some View { -// Group { -// NodeRow(node: nodes[0], connected: true) -// } -// .previewLayout(.fixed(width: 300, height: 70)) -// } -//} +import SwiftUI + +struct NodeRow: View { + var node: NodeInfoEntity + var connected: Bool + + var body: some View { + VStack(alignment: .leading) { + + HStack { + + CircleText(text: node.user?.shortName ?? "???", color: Color.accentColor).offset(y: 1).padding(.trailing, 5) + .offset(x: -15) + + if UIDevice.current.userInterfaceIdiom == .pad { + Text(node.user?.longName ?? "Unknown").font(.headline) + .offset(x: -15) + } else { + Text(node.user?.longName ?? "Unknown").font(.title) + .offset(x: -15) + } + } + .padding(.bottom, 10) + + HStack(alignment: .bottom) { + + Image(systemName: "clock.badge.checkmark.fill").font(.headline).foregroundColor(.accentColor).symbolRenderingMode(.hierarchical) + + if UIDevice.current.userInterfaceIdiom == .pad { + + if connected { + Text("Currently Connected").font(.caption).foregroundColor(Color.accentColor) + } else if node.lastHeard > 0 { + let lastHeard = Date(timeIntervalSince1970: TimeInterval(node.lastHeard)) + Text("Last Heard: \(lastHeard, style: .relative) ago").font(.caption).foregroundColor(.gray) + } else { + Text("Last Heard: Unknown").font(.caption).foregroundColor(.gray) + } + + } else { + if connected { + Text("Currently Connected").font(.subheadline).foregroundColor(Color.accentColor) + } else if node.lastHeard > 0 { + let lastHeard = Date(timeIntervalSince1970: TimeInterval(node.lastHeard)) + Text("Last Heard: \(lastHeard, style: .relative) ago").font(.subheadline).foregroundColor(.gray) + } else { + Text("Last Heard: Unknown").font(.subheadline).foregroundColor(.gray) + } + } + } + }.padding([.leading, .top, .bottom]) + } +} + +struct NodeRow_Previews: PreviewProvider { + //static var nodes = BLEManager().meshData.nodes + + static var previews: some View { + Group { + //NodeInfoEntityRow(node: nodes[0], connected: true) + } + .previewLayout(.fixed(width: 300, height: 70)) + } +} diff --git a/MeshtasticClient/Views/Settings/AppSettings.swift b/MeshtasticClient/Views/Settings/AppSettings.swift index 39428d76..32f12b05 100644 --- a/MeshtasticClient/Views/Settings/AppSettings.swift +++ b/MeshtasticClient/Views/Settings/AppSettings.swift @@ -144,7 +144,9 @@ struct AppSettings: View { .navigationBarItems(trailing: ZStack { - // ConnectedDevice(bluetoothOn: self.bleManager.isSwitchedOn, deviceConnected: self.bleManager.connectedPeripheral != nil, name: (self.bleManager.connectedNode != nil) ? self.bleManager.connectedNode.user.shortName : ((self.bleManager.connectedPeripheral != nil) ? self.bleManager.connectedPeripheral.name : "Unknown") ) + + + //ConnectedDevice(bluetoothOn: self.bleManager.isSwitchedOn, deviceConnected: self.bleManager.connectedPeripheral != nil, name: (self.bleManager.connectedNode != nil) ? self.bleManager.connectedNode.user.shortName : ((self.bleManager.connectedPeripheral != nil) ? self.bleManager.connectedPeripheral.name : "Unknown") ) }) } .navigationViewStyle(StackNavigationViewStyle()) @@ -152,8 +154,7 @@ struct AppSettings: View { } struct AppSettings_Previews: PreviewProvider { - static let meshData = MeshData() - + static var previews: some View { Group { AppSettings()