mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
CoreData commit 1 Messages replacement
This commit is contained in:
parent
98368640c9
commit
4a08431766
8 changed files with 422 additions and 269 deletions
|
|
@ -29,6 +29,7 @@
|
|||
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 */; };
|
||||
DD9D8F2F2764403B00080993 /* Meshtastic.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DD9D8F2D2764403B00080993 /* Meshtastic.xcdatamodeld */; };
|
||||
DDAF8C5326EB1DF10058C060 /* BLEManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAF8C5226EB1DF10058C060 /* BLEManager.swift */; };
|
||||
DDAF8C5826ED07FD0058C060 /* mesh.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAF8C5726ED07FD0058C060 /* mesh.pb.swift */; };
|
||||
DDAF8C5D26ED09490058C060 /* portnums.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAF8C5C26ED09490058C060 /* portnums.pb.swift */; };
|
||||
|
|
@ -92,6 +93,7 @@
|
|||
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>"; };
|
||||
DD913638270DFF4C00D7ACF3 /* LocalNotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalNotificationManager.swift; sourceTree = "<group>"; };
|
||||
DD9D8F2E2764403B00080993 /* CoreDataSample.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = CoreDataSample.xcdatamodel; sourceTree = "<group>"; };
|
||||
DDAF8C5226EB1DF10058C060 /* BLEManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BLEManager.swift; sourceTree = "<group>"; };
|
||||
DDAF8C5726ED07FD0058C060 /* mesh.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = mesh.pb.swift; sourceTree = "<group>"; };
|
||||
DDAF8C5C26ED09490058C060 /* portnums.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = portnums.pb.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -203,6 +205,7 @@
|
|||
DDC2E14B26CE248E0042C5E4 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DD9D8F2D2764403B00080993 /* Meshtastic.xcdatamodeld */,
|
||||
DDC2E15626CE248E0042C5E4 /* MeshtasticClient */,
|
||||
DDC2E16D26CE248F0042C5E4 /* MeshtasticClientTests */,
|
||||
DDC2E17826CE248F0042C5E4 /* MeshtasticClientUITests */,
|
||||
|
|
@ -517,6 +520,7 @@
|
|||
DDAF8C6226ED0A230058C060 /* mqtt.pb.swift in Sources */,
|
||||
DDF924CA26FBB953009FE055 /* ConnectedDevice.swift in Sources */,
|
||||
DDAF8C5D26ED09490058C060 /* portnums.pb.swift in Sources */,
|
||||
DD9D8F2F2764403B00080993 /* Meshtastic.xcdatamodeld in Sources */,
|
||||
DD47E3DF26F39D9F00029299 /* MyInfoModel.swift in Sources */,
|
||||
DD23A50F26FD1B4400D9B90C /* PeripheralModel.swift in Sources */,
|
||||
DD47E3CE26F103C600029299 /* NodeList.swift in Sources */,
|
||||
|
|
@ -883,6 +887,20 @@
|
|||
productName = SwiftProtobuf;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
|
||||
/* Begin XCVersionGroup section */
|
||||
DD9D8F2D2764403B00080993 /* Meshtastic.xcdatamodeld */ = {
|
||||
isa = XCVersionGroup;
|
||||
children = (
|
||||
DD9D8F2E2764403B00080993 /* CoreDataSample.xcdatamodel */,
|
||||
);
|
||||
currentVersion = DD9D8F2E2764403B00080993 /* CoreDataSample.xcdatamodel */;
|
||||
name = Meshtastic.xcdatamodeld;
|
||||
path = MeshtasticClient/Meshtastic.xcdatamodeld;
|
||||
sourceTree = "<group>";
|
||||
versionGroupType = wrapper.xcdatamodel;
|
||||
};
|
||||
/* End XCVersionGroup section */
|
||||
};
|
||||
rootObject = DDC2E14C26CE248E0042C5E4 /* Project object */;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,38 +32,6 @@
|
|||
stopOnStyle = "0">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "F27E80D7-F96D-46E5-8456-4F1098121908"
|
||||
shouldBeEnabled = "No"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "MeshtasticClient/Helpers/BLEManager.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "500"
|
||||
endingLineNumber = "500"
|
||||
landmarkName = "peripheral(_:didUpdateValueFor:error:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "DB6E77E0-7B23-469A-B2C0-01FB3B30FBB2"
|
||||
shouldBeEnabled = "No"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "MeshtasticClient/Helpers/BLEManager.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "456"
|
||||
endingLineNumber = "456"
|
||||
landmarkName = "peripheral(_:didUpdateValueFor:error:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
|
|
@ -97,15 +65,15 @@
|
|||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "B8840159-8C82-48AD-85A9-682A3AC08A01"
|
||||
shouldBeEnabled = "No"
|
||||
uuid = "C16C366D-F4CF-4FE2-A87D-3A8BBA3A2840"
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "MeshtasticClient/Helpers/BLEManager.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "400"
|
||||
endingLineNumber = "400"
|
||||
startingLineNumber = "555"
|
||||
endingLineNumber = "555"
|
||||
landmarkName = "peripheral(_:didUpdateValueFor:error:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
|
|
|
|||
|
|
@ -8,25 +8,20 @@ import SwiftUI
|
|||
// ---------------------------------------------------------------------------------------
|
||||
class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeripheralDelegate {
|
||||
|
||||
static let shared = BLEManager()
|
||||
|
||||
private static var documentsFolder: URL {
|
||||
do {
|
||||
return try FileManager.default.url(
|
||||
for: .documentDirectory,
|
||||
in: .userDomainMask,
|
||||
appropriateFor: nil,
|
||||
create: true)
|
||||
return try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
|
||||
} catch {
|
||||
fatalError("Can't find documents directory.")
|
||||
}
|
||||
}
|
||||
|
||||
// Core Data
|
||||
@Environment(\.managedObjectContext) private var viewContext
|
||||
|
||||
@Published var meshData: MeshData
|
||||
@Published var messageData: MessageData
|
||||
|
||||
private var centralManager: CBCentralManager!
|
||||
|
||||
var context: NSManagedObjectContext?
|
||||
private var centralManager: CBCentralManager!
|
||||
|
||||
@Published var peripherals = [Peripheral]()
|
||||
|
||||
@Published var connectedPeripheral: Peripheral!
|
||||
@Published var connectedNode: NodeInfoModel!
|
||||
|
|
@ -37,13 +32,9 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
@Published var isScanning: Bool = false
|
||||
@Published var isConnected: Bool = false
|
||||
|
||||
@Published var peripherals = [Peripheral]()
|
||||
|
||||
var timeoutTimer: Timer?
|
||||
var timeoutTimerCount = 0
|
||||
|
||||
private var meshLoggingEnabled: Bool = false
|
||||
|
||||
private var broadcastNodeId: UInt32 = 4294967295
|
||||
|
||||
/* Meshtastic Service Details */
|
||||
|
|
@ -55,24 +46,31 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
let TORADIO_UUID = CBUUID(string: "0xF75C76D2-129E-4DAD-A1DD-7866124401E7")
|
||||
let FROMRADIO_UUID = CBUUID(string: "0x8BA2BCC2-EE02-4A55-A531-C525C5E454D5")
|
||||
let FROMNUM_UUID = CBUUID(string: "0xED9DA18C-A800-4F66-A670-AA7547E34453")
|
||||
|
||||
|
||||
private var meshLoggingEnabled: Bool = false
|
||||
let meshLog = documentsFolder.appendingPathComponent("meshlog.txt")
|
||||
|
||||
/* init BLEManager */
|
||||
|
||||
//Eventually Delete
|
||||
@Published var meshData: MeshData
|
||||
//@Published var messageData: MessageData
|
||||
|
||||
// MARK: init BLEManager
|
||||
override init() {
|
||||
|
||||
self.meshLoggingEnabled = UserDefaults.standard.object(forKey: "meshActivityLog") as? Bool ?? false
|
||||
self.meshData = MeshData()
|
||||
self.messageData = MessageData()
|
||||
//self.messageData = MessageData()
|
||||
self.lastConnectedPeripheral = ""
|
||||
self.lastConnectionError = ""
|
||||
super.init()
|
||||
//let bleQueue: DispatchQueue = DispatchQueue(label: "CentralManager")
|
||||
centralManager = CBCentralManager(delegate: self, queue: nil)
|
||||
meshData.load()
|
||||
messageData.load()
|
||||
//messageData.load()
|
||||
}
|
||||
|
||||
// called when bluetooth is enabled/disabled for the app
|
||||
// MARK: Bluetooth enabled/disabled for the app
|
||||
func centralManagerDidUpdateState(_ central: CBCentralManager) {
|
||||
if central.state == .poweredOn {
|
||||
|
||||
|
|
@ -84,6 +82,8 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Scanning for BLE Devices
|
||||
// Scan for nearby BLE devices using the Meshtastic BLE service ID
|
||||
func startScanning() {
|
||||
|
||||
|
|
@ -95,7 +95,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
}
|
||||
}
|
||||
|
||||
// Stop Scanning For BLE Devices
|
||||
// Stop Scanning For BLE Devices
|
||||
func stopScanning() {
|
||||
|
||||
if centralManager.isScanning {
|
||||
|
|
@ -106,6 +106,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: BLE Connect functions
|
||||
/// The action after the timeout-timer has fired
|
||||
///
|
||||
/// - Parameters:
|
||||
|
|
@ -160,15 +161,14 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
RunLoop.current.add(self.timeoutTimer!, forMode: .common)
|
||||
}
|
||||
|
||||
// Disconnect Peripheral function
|
||||
// Disconnect Connected Peripheral
|
||||
func disconnectPeripheral() {
|
||||
|
||||
guard let connectedPeripheral = connectedPeripheral else { return }
|
||||
self.centralManager?.cancelPeripheralConnection(connectedPeripheral.peripheral)
|
||||
|
||||
}
|
||||
|
||||
// Called each time a peripheral is discovered
|
||||
//Called each time a peripheral is discovered
|
||||
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String: Any], rssi RSSI: NSNumber) {
|
||||
|
||||
var peripheralName: String = peripheral.name ?? "Unknown"
|
||||
|
|
@ -197,7 +197,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
}
|
||||
}
|
||||
|
||||
// called when a peripheral is connected
|
||||
// Called when a peripheral is connected
|
||||
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
|
||||
|
||||
// guard let connectedPeripheral = connectedPeripheral else { return }
|
||||
|
|
@ -223,6 +223,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
|
||||
}
|
||||
|
||||
// Called when a Peripheral fails to connect
|
||||
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
|
||||
|
||||
if meshLoggingEnabled { MeshLogger.log("BLE Failed to Connect: \(peripheral.name ?? "Unknown")") }
|
||||
|
|
@ -282,7 +283,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
}
|
||||
}
|
||||
|
||||
// Discover Services Event
|
||||
//MARK: Peripheral Services functions
|
||||
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
|
||||
|
||||
if let e = error {
|
||||
|
|
@ -303,7 +304,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
}
|
||||
}
|
||||
|
||||
// Discover Characteristics Event
|
||||
//MARK: Discover Characteristics Event
|
||||
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
|
||||
if let e = error {
|
||||
|
||||
|
|
@ -348,31 +349,15 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
|
||||
print("didUpdateNotificationStateFor char: \(characteristic.uuid.uuidString) \(characteristic.isNotifying)")
|
||||
if meshLoggingEnabled { MeshLogger.log("didUpdateNotificationStateFor char: \(characteristic.uuid.uuidString) \(characteristic.isNotifying)") }
|
||||
|
||||
if let errorText = error?.localizedDescription {
|
||||
print("didUpdateNotificationStateFor error: \(errorText)")
|
||||
}
|
||||
// commandLock.lock()
|
||||
|
||||
// if let index = commandConditions.firstIndex(where: { (condition) -> Bool in
|
||||
// if case .notificationStateUpdate(characteristic: characteristic, enabled: characteristic.isNotifying) = condition {
|
||||
// return true
|
||||
// } else {
|
||||
// return false
|
||||
// }
|
||||
// }) {
|
||||
// commandConditions.remove(at: index)
|
||||
// commandError = error
|
||||
|
||||
// if commandConditions.isEmpty {
|
||||
// commandLock.broadcast()
|
||||
// }
|
||||
// }
|
||||
|
||||
// commandLock.unlock()
|
||||
// ?.peripheralManager(self, didUpdateNotificationStateFor: characteristic)
|
||||
}
|
||||
}
|
||||
|
||||
// Data Read / Update Characteristic Event
|
||||
//MARK: Data Read / Update Characteristic Event
|
||||
//TODO: Convert to CoreData
|
||||
//FIXME: Remove broken JSON file data layer implementation
|
||||
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
|
||||
if let e = error {
|
||||
|
||||
|
|
@ -383,10 +368,11 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
switch characteristic.uuid {
|
||||
case FROMNUM_UUID:
|
||||
peripheral.readValue(for: FROMNUM_characteristic)
|
||||
// let byteArrayFromData: [UInt8] = [UInt8](characteristic.value!)
|
||||
// let stringFromByteArray = String(data: Data(_: byteArrayFromData), encoding: .utf8)
|
||||
// print("string array data \(stringFromByteArray!)")
|
||||
// print(characteristic.value?. ?? "no value")
|
||||
let characteristicValue: [UInt8] = [UInt8](characteristic.value!)
|
||||
let bigEndianUInt32 = characteristicValue.withUnsafeBytes { $0.load(as: UInt32.self) }
|
||||
let returnValue = CFByteOrderGetCurrent() == CFByteOrder(CFByteOrderLittleEndian.rawValue)
|
||||
? UInt32(bigEndian: bigEndianUInt32) : bigEndianUInt32
|
||||
//print(returnValue)
|
||||
|
||||
case FROMRADIO_UUID:
|
||||
if characteristic.value == nil || characteristic.value!.isEmpty {
|
||||
|
|
@ -400,74 +386,220 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
print("Print DecodedInfo")
|
||||
print(decodedInfo)
|
||||
|
||||
// MyInfo Data
|
||||
if decodedInfo.myInfo.myNodeNum != 0 {
|
||||
|
||||
// Create a MyInfoModel
|
||||
let myInfoModel = MyInfoModel(
|
||||
myNodeNum: decodedInfo.myInfo.myNodeNum,
|
||||
hasGps: decodedInfo.myInfo.hasGps_p,
|
||||
numBands: decodedInfo.myInfo.numBands,
|
||||
maxChannels: decodedInfo.myInfo.maxChannels,
|
||||
firmwareVersion: decodedInfo.myInfo.firmwareVersion,
|
||||
messageTimeoutMsec: decodedInfo.myInfo.messageTimeoutMsec,
|
||||
minAppVersion: decodedInfo.myInfo.minAppVersion)
|
||||
|
||||
// Save it to the connected nodeInfo
|
||||
if connectedPeripheral != nil {
|
||||
connectedPeripheral.myInfo = myInfoModel
|
||||
// Save it to the connected node
|
||||
connectedNode = meshData.nodes.first(where: {$0.num == myInfoModel.myNodeNum})
|
||||
|
||||
}
|
||||
// Since the data is from the device itself we save all myInfo objects since they are always the most up to date
|
||||
if connectedNode != nil {
|
||||
|
||||
connectedNode.myInfo = myInfoModel
|
||||
let nodeIndex = meshData.nodes.firstIndex(where: { $0.id == decodedInfo.myInfo.myNodeNum })
|
||||
// meshData.nodes.remove(at: nodeIndex!)
|
||||
// meshData.nodes.append(connectedNode)
|
||||
if nodeIndex != nil {
|
||||
meshData.nodes[nodeIndex!] = connectedNode
|
||||
meshData.save()
|
||||
|
||||
print("Save a CoreData MyInfoEntity")
|
||||
|
||||
let fetchMyInfoRequest:NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "MyInfoEntity")
|
||||
fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %i", Int64(decodedInfo.myInfo.myNodeNum))
|
||||
|
||||
|
||||
do {
|
||||
let fetchedMyInfo = try context?.fetch(fetchMyInfoRequest) as! [MyInfoEntity]
|
||||
// Not Found Insert
|
||||
if fetchedMyInfo.isEmpty {
|
||||
let myInfo = MyInfoEntity(context: context!)
|
||||
myInfo.myNodeNum = Int64(decodedInfo.myInfo.myNodeNum)
|
||||
myInfo.hasGps = decodedInfo.myInfo.hasGps_p
|
||||
myInfo.numBands = Int32(bitPattern: decodedInfo.myInfo.numBands)
|
||||
myInfo.firmwareVersion = decodedInfo.myInfo.firmwareVersion
|
||||
myInfo.messageTimeoutMsec = Int32(bitPattern: decodedInfo.myInfo.messageTimeoutMsec)
|
||||
myInfo.minAppVersion = Int32(bitPattern: decodedInfo.myInfo.minAppVersion)
|
||||
myInfo.maxChannels = Int32(bitPattern: decodedInfo.myInfo.maxChannels)
|
||||
|
||||
do {
|
||||
|
||||
try context!.save()
|
||||
print("Saved a myInfo for \(decodedInfo.myInfo.myNodeNum)")
|
||||
|
||||
} catch {
|
||||
|
||||
context!.rollback()
|
||||
|
||||
let nsError = error as NSError
|
||||
print("Error Saving CoreData MyInfoEntity: \(nsError)")
|
||||
}
|
||||
}
|
||||
print("Saved a myInfo for \(decodedInfo.myInfo.myNodeNum)")
|
||||
if meshLoggingEnabled { MeshLogger.log("BLE FROMRADIO received and myInfo saved for \(peripheral.name ?? "Unknown")") }
|
||||
} catch {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//if meshLoggingEnabled { MeshLogger.log("BLE FROMRADIO received and myInfo saved for \(peripheral.name ?? String(myInfo.myNodeNum))") }
|
||||
}
|
||||
|
||||
// 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 == %i", Int64(decodedInfo.nodeInfo.num))
|
||||
|
||||
do {
|
||||
let fetchedNode = try context?.fetch(fetchNodeRequest) as! [NodeInfoEntity]
|
||||
// Not Found Insert
|
||||
if fetchedNode.isEmpty {
|
||||
|
||||
let newNode = NodeInfoEntity(context: context!)
|
||||
newNode.timestamp = Date()
|
||||
newNode.id = Int64(decodedInfo.nodeInfo.num)
|
||||
newNode.num = Int64(decodedInfo.nodeInfo.num)
|
||||
newNode.lastHeard = Int32(bitPattern: decodedInfo.nodeInfo.lastHeard)
|
||||
newNode.snr = decodedInfo.nodeInfo.snr
|
||||
|
||||
let userIdLast4: String = String(decodedInfo.nodeInfo.user.id.suffix(4))
|
||||
newNode.bleName = "Meshtastic_" + userIdLast4
|
||||
|
||||
if decodedInfo.nodeInfo.hasUser {
|
||||
|
||||
print("Save a nodeInfo")
|
||||
print(decodedInfo.nodeInfo)
|
||||
if meshData.nodes.contains(where: {$0.id == decodedInfo.nodeInfo.num}) {
|
||||
|
||||
// Found a matching node lets update it
|
||||
let nodeMatch = meshData.nodes.first(where: { $0.id == decodedInfo.nodeInfo.num })
|
||||
if connectedPeripheral != nil && connectedPeripheral.myInfo?.myNodeNum == nodeMatch?.num {
|
||||
connectedNode = nodeMatch
|
||||
}
|
||||
|
||||
if nodeMatch?.lastHeard ?? 0 < decodedInfo.nodeInfo.lastHeard && nodeMatch?.user != nil && nodeMatch?.user.longName.count ?? 0 > 0 {
|
||||
// The data coming from the device is newer
|
||||
|
||||
let nodeIndex = meshData.nodes.firstIndex(where: { $0.id == decodedInfo.nodeInfo.num })
|
||||
meshData.nodes.remove(at: nodeIndex!)
|
||||
meshData.save()
|
||||
|
||||
let newUser = UserEntity(context: context!)
|
||||
newUser.userId = decodedInfo.nodeInfo.user.id
|
||||
newUser.num = Int64(decodedInfo.nodeInfo.num)
|
||||
newUser.longName = decodedInfo.nodeInfo.user.longName
|
||||
newUser.shortName = decodedInfo.nodeInfo.user.shortName
|
||||
newUser.macaddr = decodedInfo.nodeInfo.user.macaddr
|
||||
newUser.hwModel = String(describing: decodedInfo.nodeInfo.user.hwModel).uppercased()
|
||||
newNode.user = newUser
|
||||
}
|
||||
|
||||
if decodedInfo.nodeInfo.hasPosition && decodedInfo.nodeInfo.position.latitudeI != 0 {
|
||||
|
||||
let position = PositionEntity(context: context!)
|
||||
position.latitudeI = decodedInfo.nodeInfo.position.latitudeI
|
||||
position.longitudeI = decodedInfo.nodeInfo.position.longitudeI
|
||||
position.altitude = decodedInfo.nodeInfo.position.altitude
|
||||
position.batteryLevel = decodedInfo.nodeInfo.position.batteryLevel
|
||||
position.time = Int32(bitPattern: decodedInfo.nodeInfo.position.time)
|
||||
|
||||
var newPostions = [PositionEntity]()
|
||||
newPostions.append(position)
|
||||
newNode.positions? = NSOrderedSet(array : newPostions)
|
||||
}
|
||||
|
||||
// Look for a MyInfo
|
||||
let fetchMyInfoRequest:NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "MyInfoEntity")
|
||||
fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %i", Int64(decodedInfo.nodeInfo.num))
|
||||
|
||||
do {
|
||||
|
||||
let fetchedMyInfo = try context?.fetch(fetchMyInfoRequest) as! [MyInfoEntity]
|
||||
if fetchedMyInfo.count > 0 {
|
||||
newNode.myInfo = fetchedMyInfo[0]
|
||||
|
||||
}
|
||||
|
||||
} catch {
|
||||
print("Fetch MyInfo Error")
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
let updatedNode = fetchedNode[0]
|
||||
updatedNode.timestamp = Date()
|
||||
updatedNode.lastHeard = Int32(bitPattern: decodedInfo.nodeInfo.lastHeard)
|
||||
updatedNode.snr = decodedInfo.nodeInfo.snr
|
||||
|
||||
if decodedInfo.nodeInfo.hasUser {
|
||||
|
||||
// Data is older than what the app already has
|
||||
return
|
||||
updatedNode.user!.userId = decodedInfo.nodeInfo.user.id
|
||||
updatedNode.user!.longName = decodedInfo.nodeInfo.user.longName
|
||||
updatedNode.user!.shortName = decodedInfo.nodeInfo.user.shortName
|
||||
updatedNode.user!.hwModel = String(describing: decodedInfo.nodeInfo.user.hwModel).uppercased()
|
||||
}
|
||||
if decodedInfo.nodeInfo.hasPosition {
|
||||
|
||||
let position = PositionEntity(context: context!)
|
||||
position.latitudeI = decodedInfo.nodeInfo.position.latitudeI
|
||||
position.longitudeI = decodedInfo.nodeInfo.position.longitudeI
|
||||
position.altitude = decodedInfo.nodeInfo.position.altitude
|
||||
position.batteryLevel = decodedInfo.nodeInfo.position.batteryLevel
|
||||
position.time = Int32(bitPattern: decodedInfo.nodeInfo.position.time)
|
||||
|
||||
if position.latitudeI != 0 {
|
||||
let mutablePositions = updatedNode.positions!.mutableCopy() as! NSMutableOrderedSet
|
||||
mutablePositions.add(position)
|
||||
updatedNode.positions = mutablePositions.copy() as? NSOrderedSet
|
||||
}
|
||||
}
|
||||
// Look for a MyInfo
|
||||
let fetchMyInfoRequest:NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "MyInfoEntity")
|
||||
fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %i", Int64(decodedInfo.nodeInfo.num))
|
||||
|
||||
do {
|
||||
|
||||
let fetchedMyInfo = try context?.fetch(fetchMyInfoRequest) as! [MyInfoEntity]
|
||||
if fetchedMyInfo.count > 0 {
|
||||
|
||||
updatedNode.myInfo = fetchedMyInfo[0]
|
||||
|
||||
}
|
||||
|
||||
} catch {
|
||||
print("Fetch MyInfo Error")
|
||||
}
|
||||
}
|
||||
do {
|
||||
|
||||
try context!.save()
|
||||
print("Saved a nodeInfo for \(decodedInfo.nodeInfo.num)")
|
||||
|
||||
} catch {
|
||||
|
||||
context!.rollback()
|
||||
|
||||
let nsError = error as NSError
|
||||
print("Error Saving CoreData NodeInfoEntity: \(nsError)")
|
||||
}
|
||||
|
||||
} catch {
|
||||
|
||||
print("Fetch NodeInfoEntity Error")
|
||||
}
|
||||
|
||||
if decodedInfo.nodeInfo.hasUser {
|
||||
|
||||
print("BLE FROMRADIO received and nodeInfo saved for \(decodedInfo.nodeInfo.user.longName)")
|
||||
if meshLoggingEnabled { MeshLogger.log("BLE FROMRADIO received and nodeInfo saved for \(decodedInfo.nodeInfo.user.longName)") }
|
||||
|
||||
} else {
|
||||
|
||||
print("BLE FROMRADIO received and nodeInfo saved for \(decodedInfo.nodeInfo.num)")
|
||||
if meshLoggingEnabled { MeshLogger.log("BLE FROMRADIO received and nodeInfo saved for \(decodedInfo.nodeInfo.num)") }
|
||||
}
|
||||
// Set the connected node if the nodeInfo is for the connected node.
|
||||
if connectedPeripheral != nil && connectedPeripheral.myInfo?.myNodeNum == decodedInfo.nodeInfo.num {
|
||||
|
||||
let nodeMatch = meshData.nodes.first(where: { $0.id == decodedInfo.nodeInfo.num })
|
||||
if nodeMatch != nil {
|
||||
connectedNode = nodeMatch
|
||||
}
|
||||
}
|
||||
// if meshData.nodes.contains(where: {$0.id == decodedInfo.nodeInfo.num}) {
|
||||
//
|
||||
// // Found a matching node lets update it
|
||||
// let nodeMatch = meshData.nodes.first(where: { $0.id == decodedInfo.nodeInfo.num })
|
||||
// if connectedPeripheral != nil && connectedPeripheral.myInfo?.myNodeNum == nodeMatch?.num {
|
||||
// connectedNode = nodeMatch
|
||||
// }
|
||||
//
|
||||
// if nodeMatch?.lastHeard ?? 0 < decodedInfo.nodeInfo.lastHeard && nodeMatch?.user != nil && nodeMatch?.user.longName.count ?? 0 > 0 {
|
||||
// // The data coming from the device is newer
|
||||
//
|
||||
// let nodeIndex = meshData.nodes.firstIndex(where: { $0.id == decodedInfo.nodeInfo.num })
|
||||
// meshData.nodes.remove(at: nodeIndex!)
|
||||
// meshData.save()
|
||||
//
|
||||
// } else {
|
||||
//
|
||||
// // Data is older than what the app already has
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
// Set the connected node if the nodeInfo is for the connected node.
|
||||
// if connectedPeripheral != nil && connectedPeripheral.myInfo?.myNodeNum == decodedInfo.nodeInfo.num {
|
||||
//
|
||||
// let nodeMatch = meshData.nodes.first(where: { $0.id == decodedInfo.nodeInfo.num })
|
||||
// if nodeMatch != nil {
|
||||
// connectedNode = nodeMatch
|
||||
// }
|
||||
// }
|
||||
if decodedInfo.nodeInfo.hasUser {
|
||||
|
||||
meshData.nodes.append(
|
||||
|
|
@ -493,11 +625,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
)
|
||||
meshData.save()
|
||||
if meshLoggingEnabled { MeshLogger.log("BLE FROMRADIO received and nodeInfo saved for \(decodedInfo.nodeInfo.user.longName)") }
|
||||
|
||||
if connectedNode == nil {
|
||||
|
||||
// connectedNode = meshData.nodes.first(where: {$0.num == connectedPeripheral.myInfo!.myNodeNum})
|
||||
}
|
||||
}
|
||||
}
|
||||
// Handle assorted app packets
|
||||
|
|
@ -505,6 +632,8 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
|
||||
print("Handle a Packet")
|
||||
do {
|
||||
|
||||
//!!!: Switch Messages Tab to coredata
|
||||
// Text Message App - Primary Broadcast Channel
|
||||
if decodedInfo.packet.decoded.portnum == PortNum.textMessageApp {
|
||||
|
||||
|
|
@ -512,50 +641,71 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
|
||||
print("Message Text: \(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])
|
||||
|
||||
do {
|
||||
|
||||
let fetchedUsers = try context?.fetch(messageUsers) as! [UserEntity]
|
||||
|
||||
let newMessage = MessageEntity(context: context!)
|
||||
newMessage.messageId = Int32(bitPattern: decodedInfo.packet.id)
|
||||
newMessage.messageTimestamp = Int32(bitPattern: decodedInfo.packet.rxTime)
|
||||
newMessage.receivedACK = false
|
||||
newMessage.direction = "IN"
|
||||
|
||||
if decodedInfo.packet.to == broadcastNodeId {
|
||||
|
||||
let bcu: UserEntity = UserEntity(context: context!)
|
||||
bcu.shortName = "BC"
|
||||
bcu.longName = "Broadcast"
|
||||
bcu.hwModel = "UNSET"
|
||||
bcu.num = Int64(broadcastNodeId)
|
||||
bcu.userId = "BROADCASTNODE"
|
||||
newMessage.toUser = bcu
|
||||
|
||||
} else {
|
||||
|
||||
newMessage.toUser = fetchedUsers.first(where: { $0.num == decodedInfo.packet.to })
|
||||
}
|
||||
|
||||
newMessage.fromUser = fetchedUsers.first(where: { $0.num == decodedInfo.packet.from })
|
||||
newMessage.messagePayload = messageText
|
||||
|
||||
do {
|
||||
|
||||
try context!.save()
|
||||
print("Saved a new message for \(decodedInfo.packet.id)")
|
||||
|
||||
// Create an iOS Notification for the received message and schedule it immediately
|
||||
let manager = LocalNotificationManager()
|
||||
|
||||
let fromUser = meshData.nodes.first(where: { $0.id == decodedInfo.packet.from })
|
||||
|
||||
var toUserLongName: String = "Broadcast"
|
||||
var toUserShortName: String = "BC"
|
||||
|
||||
if decodedInfo.packet.to != broadcastNodeId {
|
||||
|
||||
let toUser = meshData.nodes.first(where: { $0.id == decodedInfo.packet.from })
|
||||
toUserLongName = toUser?.user.longName ?? "Unknown"
|
||||
toUserShortName = toUser?.user.shortName ?? "???"
|
||||
manager.notifications = [
|
||||
Notification(
|
||||
id: ("notification.id.\(decodedInfo.packet.id)"),
|
||||
title: "\(newMessage.fromUser?.longName ?? "Unknown")",
|
||||
subtitle: "AKA \(newMessage.fromUser?.shortName ?? "???")",
|
||||
content: messageText)
|
||||
]
|
||||
manager.schedule()
|
||||
if meshLoggingEnabled { MeshLogger.log("iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? "Unknown") \(messageText)") }
|
||||
|
||||
} catch {
|
||||
|
||||
print("Something went wrong: \(error)")
|
||||
context!.rollback()
|
||||
|
||||
let nsError = error as NSError
|
||||
print("Unresolved error \(nsError)")
|
||||
}
|
||||
|
||||
} catch {
|
||||
|
||||
print("Fetch Message To and From Users Error")
|
||||
}
|
||||
|
||||
// Add the received message to the local messages list / file and save
|
||||
messageData.messages.append(
|
||||
MessageModel(
|
||||
messageId: decodedInfo.packet.id,
|
||||
messageTimeStamp: decodedInfo.packet.rxTime,
|
||||
fromUserId: decodedInfo.packet.from,
|
||||
toUserId: decodedInfo.packet.to,
|
||||
fromUserLongName: fromUser?.user.longName ?? "Unknown",
|
||||
toUserLongName: toUserLongName,
|
||||
fromUserShortName: fromUser?.user.shortName ?? "???",
|
||||
toUserShortName: toUserShortName,
|
||||
receivedACK: decodedInfo.packet.decoded.wantResponse,
|
||||
messagePayload: messageText,
|
||||
direction: "IN")
|
||||
)
|
||||
messageData.save()
|
||||
|
||||
// Create an iOS Notification for the received message and schedule it immediately
|
||||
let manager = LocalNotificationManager()
|
||||
|
||||
manager.notifications = [
|
||||
Notification(
|
||||
id: ("notification.id.\(decodedInfo.packet.id)"),
|
||||
title: "\(fromUser?.user.longName ?? "Unknown")",
|
||||
subtitle: "AKA \(fromUser?.user.shortName ?? "???")",
|
||||
content: messageText)
|
||||
]
|
||||
manager.schedule()
|
||||
if meshLoggingEnabled { MeshLogger.log("iOS Notification Scheduled for text message from \(fromUser?.user.longName ?? "Unknown") \(messageText)") }
|
||||
}
|
||||
} else if decodedInfo.packet.decoded.portnum == PortNum.nodeinfoApp {
|
||||
} else if decodedInfo.packet.decoded.portnum == PortNum.nodeinfoApp {
|
||||
|
||||
var updatedNode = meshData.nodes.first(where: {$0.id == decodedInfo.packet.from })
|
||||
if updatedNode != nil {
|
||||
|
|
@ -684,8 +834,8 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
let binaryData: Data = try! toRadio.serializedData()
|
||||
if connectedPeripheral!.peripheral.state == CBPeripheralState.connected {
|
||||
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
|
||||
messageData.messages.append(messageModel)
|
||||
messageData.save()
|
||||
//messageData.messages.append(messageModel)
|
||||
//messageData.save()
|
||||
success = true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,10 @@ import CoreData
|
|||
|
||||
@main
|
||||
struct MeshtasticClientApp: App {
|
||||
|
||||
@ObservedObject private var bleManager: BLEManager = BLEManager()
|
||||
|
||||
let persistenceController = PersistenceController.shared
|
||||
|
||||
@ObservedObject private var bleManager: BLEManager = BLEManager.shared
|
||||
@ObservedObject private var userSettings: UserSettings = UserSettings()
|
||||
|
||||
@Environment(\.scenePhase) var scenePhase
|
||||
|
|
@ -12,6 +14,7 @@ struct MeshtasticClientApp: App {
|
|||
var body: some Scene {
|
||||
WindowGroup {
|
||||
ContentView()
|
||||
.environment(\.managedObjectContext, persistenceController.container.viewContext)
|
||||
.environmentObject(userSettings)
|
||||
.environmentObject(bleManager)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,52 +6,54 @@
|
|||
//
|
||||
|
||||
import CoreData
|
||||
/*
|
||||
|
||||
struct PersistenceController {
|
||||
static let shared = PersistenceController()
|
||||
class PersistenceController {
|
||||
|
||||
static let shared = PersistenceController()
|
||||
|
||||
static var preview: PersistenceController = {
|
||||
let result = PersistenceController(inMemory: false)
|
||||
let viewContext = result.container.viewContext
|
||||
for _ in 0..<10 {
|
||||
let newItem = NodeInfoEntity(context: viewContext)
|
||||
newItem.timestamp = Date()
|
||||
}
|
||||
do {
|
||||
try viewContext.save()
|
||||
} catch {
|
||||
// Replace this implementation with code to handle the error appropriately.
|
||||
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
|
||||
let nsError = error as NSError
|
||||
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
|
||||
}
|
||||
return result
|
||||
}()
|
||||
static var preview: PersistenceController = {
|
||||
let result = PersistenceController(inMemory: false)
|
||||
let viewContext = result.container.viewContext
|
||||
for _ in 0..<10 {
|
||||
let newItem = NodeInfoEntity(context: viewContext)
|
||||
newItem.timestamp = Date()
|
||||
}
|
||||
do {
|
||||
try viewContext.save()
|
||||
} catch {
|
||||
// Replace this implementation with code to handle the error appropriately.
|
||||
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
|
||||
let nsError = error as NSError
|
||||
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
|
||||
}
|
||||
return result
|
||||
}()
|
||||
|
||||
let container: NSPersistentContainer
|
||||
let container: NSPersistentContainer
|
||||
|
||||
init(inMemory: Bool = false) {
|
||||
container = NSPersistentContainer(name: "Meshtastic")
|
||||
if inMemory {
|
||||
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
|
||||
}
|
||||
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
|
||||
if let error = error as NSError? {
|
||||
// Replace this implementation with code to handle the error appropriately.
|
||||
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
|
||||
init(inMemory: Bool = false) {
|
||||
container = NSPersistentContainer(name: "Meshtastic")
|
||||
if inMemory {
|
||||
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
|
||||
}
|
||||
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
|
||||
// Merge policy that favors in memory data over data in the db
|
||||
self.container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
|
||||
|
||||
if let error = error as NSError? {
|
||||
// Replace this implementation with code to handle the error appropriately.
|
||||
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
|
||||
|
||||
/*
|
||||
Typical reasons for an error here include:
|
||||
* The parent directory does not exist, cannot be created, or disallows writing.
|
||||
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
|
||||
* The device is out of space.
|
||||
* The store could not be migrated to the current model version.
|
||||
Check the error message to determine what the actual problem was.
|
||||
*/
|
||||
fatalError("Unresolved error \(error), \(error.userInfo)")
|
||||
}
|
||||
})
|
||||
}
|
||||
/*
|
||||
Typical reasons for an error here include:
|
||||
* The parent directory does not exist, cannot be created, or disallows writing.
|
||||
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
|
||||
* The device is out of space.
|
||||
* The store could not be migrated to the current model version.
|
||||
Check the error message to determine what the actual problem was.
|
||||
*/
|
||||
fatalError("Unresolved error \(error), \(error.userInfo)")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@ import CoreBluetooth
|
|||
|
||||
struct Connect: View {
|
||||
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
@EnvironmentObject var userSettings: UserSettings
|
||||
|
||||
@State var isPreferredRadio: Bool = false
|
||||
|
|
@ -60,8 +61,8 @@ struct Connect: View {
|
|||
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.myInfo != nil {
|
||||
Text("FW Version: ").font(.caption)+Text(bleManager.connectedPeripheral.myInfo?.firmwareVersion ?? "(null)").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)
|
||||
}
|
||||
if bleManager.connectedPeripheral.subscribed {
|
||||
Text("Properly Subscribed").font(.caption)
|
||||
|
|
@ -207,6 +208,8 @@ struct Connect: View {
|
|||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
.onAppear(perform: {
|
||||
|
||||
self.bleManager.context = context
|
||||
|
||||
if bleManager.connectedPeripheral != nil && userSettings.preferredPeripheralId == bleManager.connectedPeripheral.peripheral.identifier.uuidString {
|
||||
isPreferredRadio = true
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,15 @@ struct Messages: View {
|
|||
enum Field: Hashable {
|
||||
case messageText
|
||||
}
|
||||
|
||||
// CoreData
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
||||
@FetchRequest(
|
||||
sortDescriptors: [NSSortDescriptor(keyPath: \MessageEntity.messageTimestamp, ascending: true)],
|
||||
animation: .default)
|
||||
private var messages: FetchedResults<MessageEntity>
|
||||
|
||||
// Keyboard State
|
||||
@State var typingMessage: String = ""
|
||||
|
|
@ -16,14 +25,8 @@ struct Messages: View {
|
|||
@State private var lastTypingMessage = ""
|
||||
@FocusState private var focusedField: Field?
|
||||
|
||||
@Namespace var topId
|
||||
@Namespace var bottomId
|
||||
|
||||
@State var showDeleteMessageAlert = false
|
||||
@State private var deleteMessageId: UInt32 = 0
|
||||
|
||||
// Message Data and Bluetooth
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
@State private var deleteMessageId: Int32 = 0
|
||||
|
||||
public var broadcastNodeId: UInt32 = 4294967295
|
||||
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
|
||||
|
|
@ -38,18 +41,16 @@ struct Messages: View {
|
|||
|
||||
ScrollViewReader { scrollView in
|
||||
|
||||
if self.bleManager.messageData.messages.count > 0 {
|
||||
if self.messages.count > 0 {
|
||||
|
||||
ScrollView {
|
||||
|
||||
Text("Hidden Top Anchor").hidden().frame(height: 0).id(topId)
|
||||
|
||||
ForEach(bleManager.messageData.messages.sorted(by: { $0.messageTimestamp < $1.messageTimestamp })) { message in
|
||||
ForEach(messages) { message in
|
||||
|
||||
HStack(alignment: .top) {
|
||||
let currentUser: Bool = (bleManager.connectedNode != nil) && ((bleManager.connectedNode.id) == message.fromUserId)
|
||||
let currentUser: Bool = false//(bleManager.connectedNode != nil) && ((bleManager.connectedNode.id) == message.fromUser!.num)
|
||||
|
||||
CircleText(text: message.fromUserShortName, color: currentUser ? .accentColor : Color(.darkGray)).padding(.all, 5)
|
||||
CircleText(text: "???", color: currentUser ? .accentColor : Color(.darkGray)).padding(.all, 5)
|
||||
.gesture(LongPressGesture(minimumDuration: 2)
|
||||
.onEnded {_ in
|
||||
print("I want to delete message: \(message.messageId)")
|
||||
|
|
@ -59,7 +60,7 @@ struct Messages: View {
|
|||
})
|
||||
|
||||
VStack(alignment: .leading) {
|
||||
Text(message.messagePayload)
|
||||
Text(message.messagePayload ?? "EMPTY MESSAGE")
|
||||
.textSelection(.enabled)
|
||||
.padding(10)
|
||||
.foregroundColor(.white)
|
||||
|
|
@ -87,11 +88,12 @@ struct Messages: View {
|
|||
print("OK button tapped")
|
||||
if deleteMessageId > 0 {
|
||||
|
||||
let messageIndex = bleManager.messageData.messages.firstIndex(where: { $0.messageId == deleteMessageId })
|
||||
bleManager.messageData.messages.remove(at: messageIndex!)
|
||||
bleManager.messageData.save()
|
||||
print("Deleted message: \(message.messageId)")
|
||||
showDeleteMessageAlert = false
|
||||
//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
|
||||
}
|
||||
},
|
||||
|
|
@ -99,16 +101,23 @@ struct Messages: View {
|
|||
)
|
||||
}
|
||||
}
|
||||
.onAppear(perform: { scrollView.scrollTo(bottomId) })
|
||||
Text("Hidden Bottom Anchor").hidden().frame(height: 0).id(bottomId)
|
||||
.onAppear(perform: {
|
||||
|
||||
self.bleManager.context = context
|
||||
messageCount = messages.count
|
||||
if messageCount > 0 {
|
||||
scrollView.scrollTo(messages[messageCount-1].id, anchor: .bottom)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
.onReceive(timer) { _ in
|
||||
|
||||
if messageCount < bleManager.messageData.messages.count {
|
||||
if messageCount < messages.count {
|
||||
|
||||
scrollView.scrollTo(messages[messageCount].id, anchor: .bottom)
|
||||
messageCount = messages.count
|
||||
|
||||
bleManager.messageData.load()
|
||||
scrollView.scrollTo(bottomId)
|
||||
messageCount = bleManager.messageData.messages.count
|
||||
}
|
||||
}
|
||||
.padding(.horizontal)
|
||||
|
|
@ -193,7 +202,7 @@ struct Messages: View {
|
|||
})
|
||||
.onAppear {
|
||||
|
||||
messageCount = bleManager.messageData.messages.count
|
||||
messageCount = messages.count
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ class UserSettings: ObservableObject {
|
|||
self.preferredPeripheralId = UserDefaults.standard.object(forKey: "preferredPeripheralId") as? String ?? ""
|
||||
self.provideLocation = UserDefaults.standard.object(forKey: "provideLocation") as? Bool ?? false
|
||||
self.keyboardType = UserDefaults.standard.object(forKey: "keyboardType") as? Int ?? 0
|
||||
self.meshActivityLog = UserDefaults.standard.object(forKey: "meshActivityLog") as? Bool ?? true
|
||||
self.meshActivityLog = UserDefaults.standard.object(forKey: "meshActivityLog") as? Bool ?? false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue