diff --git a/Meshtastic Client.xcodeproj/project.pbxproj b/Meshtastic Client.xcodeproj/project.pbxproj index 39370ae8..fa84c0f5 100644 --- a/Meshtastic Client.xcodeproj/project.pbxproj +++ b/Meshtastic Client.xcodeproj/project.pbxproj @@ -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 = ""; }; C9A7BC0F27759A9600760B50 /* PositionAnnotationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionAnnotationView.swift; sourceTree = ""; }; + DD1BF2F82776FE2E008C8D2F /* UserMessageList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserMessageList.swift; sourceTree = ""; }; DD23A50E26FD1B4400D9B90C /* PeripheralModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeripheralModel.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 = ""; }; @@ -86,6 +89,7 @@ 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 = ""; }; + DD882F5C2772E4640005BF05 /* Contacts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Contacts.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 = ""; }; DD90860D26F69BAE00DC5189 /* NodeMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeMap.swift; sourceTree = ""; }; @@ -315,6 +319,8 @@ children = ( DD47E3DA26F3901A00029299 /* Channels.swift */, DD47E3DC26F390A000029299 /* Messages.swift */, + DD882F5C2772E4640005BF05 /* Contacts.swift */, + DD1BF2F82776FE2E008C8D2F /* UserMessageList.swift */, ); path = Messages; sourceTree = ""; @@ -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; 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 a69d2766..40af0c6f 100644 --- a/Meshtastic Client.xcodeproj/xcuserdata/garthvanderhouwen.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/Meshtastic Client.xcodeproj/xcuserdata/garthvanderhouwen.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -83,15 +83,15 @@ @@ -99,15 +99,31 @@ + + + + @@ -115,16 +131,16 @@ diff --git a/MeshtasticClient/Helpers/BLEManager.swift b/MeshtasticClient/Helpers/BLEManager.swift index 7ad8f118..0f2ba231 100644 --- a/MeshtasticClient/Helpers/BLEManager.swift +++ b/MeshtasticClient/Helpers/BLEManager.swift @@ -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 = 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 = 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 = 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 = 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)") } } } diff --git a/MeshtasticClient/Meshtastic.xcdatamodeld/CoreDataSample.xcdatamodel/contents b/MeshtasticClient/Meshtastic.xcdatamodeld/CoreDataSample.xcdatamodel/contents index b966753b..3cce2d60 100644 --- a/MeshtasticClient/Meshtastic.xcdatamodeld/CoreDataSample.xcdatamodel/contents +++ b/MeshtasticClient/Meshtastic.xcdatamodeld/CoreDataSample.xcdatamodel/contents @@ -58,6 +58,7 @@ + @@ -68,6 +69,6 @@ - + \ No newline at end of file diff --git a/MeshtasticClient/MeshtasticClientApp.swift b/MeshtasticClient/MeshtasticClientApp.swift index 66a2c13d..8bf762bc 100644 --- a/MeshtasticClient/MeshtasticClientApp.swift +++ b/MeshtasticClient/MeshtasticClientApp.swift @@ -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") } } } diff --git a/MeshtasticClient/Views/ContentView.swift b/MeshtasticClient/Views/ContentView.swift index f950a0fc..ca20500d 100644 --- a/MeshtasticClient/Views/ContentView.swift +++ b/MeshtasticClient/Views/ContentView.swift @@ -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") diff --git a/MeshtasticClient/Views/Messages/Contacts.swift b/MeshtasticClient/Views/Messages/Contacts.swift new file mode 100644 index 00000000..fbb05da6 --- /dev/null +++ b/MeshtasticClient/Views/Messages/Contacts.swift @@ -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 + + 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() + } +} diff --git a/MeshtasticClient/Views/Messages/Messages.swift b/MeshtasticClient/Views/Messages/Messages.swift index 92a1d1a6..990f13a4 100644 --- a/MeshtasticClient/Views/Messages/Messages.swift +++ b/MeshtasticClient/Views/Messages/Messages.swift @@ -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) diff --git a/MeshtasticClient/Views/Messages/UserMessageList.swift b/MeshtasticClient/Views/Messages/UserMessageList.swift new file mode 100644 index 00000000..56411298 --- /dev/null +++ b/MeshtasticClient/Views/Messages/UserMessageList.swift @@ -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() + } +} diff --git a/MeshtasticClient/Views/Nodes/NodeDetail.swift b/MeshtasticClient/Views/Nodes/NodeDetail.swift index d491a887..eec2b014 100644 --- a/MeshtasticClient/Views/Nodes/NodeDetail.swift +++ b/MeshtasticClient/Views/Nodes/NodeDetail.swift @@ -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")