mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Move upserts in UpdateCoreData to the MeshPackets actor
This commit is contained in:
parent
140c1ab734
commit
82640216ac
15 changed files with 1937 additions and 1699 deletions
|
|
@ -166,7 +166,7 @@ extension AccessoryManager {
|
|||
|
||||
// Update local database with the new node info
|
||||
// FUTURE: after https://github.com/meshtastic/firmware/pull/8495 is merged, `favorite: true` becomes `favorite: (connectedDeviceRole != DeviceRoles.clientBase)`
|
||||
upsertNodeInfoPacket(packet: nodeMeshPacket, favorite: true, context: context)
|
||||
await MeshPackets.shared.upsertNodeInfoPacket(packet: nodeMeshPacket, favorite: true)
|
||||
}
|
||||
} catch {
|
||||
Logger.data.error("Failed to decode contact data: \(error.localizedDescription, privacy: .public)")
|
||||
|
|
@ -856,7 +856,7 @@ extension AccessoryManager {
|
|||
|
||||
try await sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription)
|
||||
|
||||
upsertAmbientLightingModuleConfigPacket(config: config, nodeNum: toUser.num, context: context)
|
||||
await MeshPackets.shared.upsertAmbientLightingModuleConfigPacket(config: config, nodeNum: toUser.num)
|
||||
|
||||
return Int64(meshPacket.id)
|
||||
|
||||
|
|
@ -912,7 +912,7 @@ extension AccessoryManager {
|
|||
|
||||
try await sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription)
|
||||
|
||||
upsertCannedMessagesModuleConfigPacket(config: config, nodeNum: toUser.num, context: context)
|
||||
await MeshPackets.shared.upsertCannedMessagesModuleConfigPacket(config: config, nodeNum: toUser.num, context: context)
|
||||
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
@ -995,7 +995,7 @@ extension AccessoryManager {
|
|||
let messageDescription = "🛟 Saved Detection Sensor Module Config for \(toUser.longName ?? "Unknown".localized)"
|
||||
try await sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription)
|
||||
|
||||
upsertDetectionSensorModuleConfigPacket(config: config, nodeNum: toUser.num, context: context)
|
||||
await MeshPackets.shared.upsertDetectionSensorModuleConfigPacket(config: config, nodeNum: toUser.num, context: context)
|
||||
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
@ -1049,7 +1049,7 @@ extension AccessoryManager {
|
|||
let messageDescription = "🛟 Saved External Notification Module Config for \(toUser.longName ?? "Unknown".localized)"
|
||||
try await sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription)
|
||||
|
||||
upsertExternalNotificationModuleConfigPacket(config: config, nodeNum: toUser.num, context: context)
|
||||
await MeshPackets.shared.upsertExternalNotificationModuleConfigPacket(config: config, nodeNum: toUser.num, context: context)
|
||||
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
@ -1079,7 +1079,7 @@ extension AccessoryManager {
|
|||
let messageDescription = "🛟 Saved PAX Counter Module Config for \(toUser.longName ?? "Unknown".localized)"
|
||||
try await sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription)
|
||||
|
||||
upsertPaxCounterModuleConfigPacket(config: config, nodeNum: toUser.num, context: context)
|
||||
await MeshPackets.shared.upsertPaxCounterModuleConfigPacket(config: config, nodeNum: toUser.num, context: context)
|
||||
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
@ -1109,7 +1109,7 @@ extension AccessoryManager {
|
|||
|
||||
try await sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription)
|
||||
|
||||
upsertRtttlConfigPacket(ringtone: ringtone, nodeNum: toUser.num, context: context)
|
||||
await MeshPackets.shared.upsertRtttlConfigPacket(ringtone: ringtone, nodeNum: toUser.num, context: context)
|
||||
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
@ -1140,7 +1140,7 @@ extension AccessoryManager {
|
|||
let messageDescription = "🛟 Saved MQTT Config for \(toUser.longName ?? "Unknown".localized)"
|
||||
try await sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription)
|
||||
|
||||
upsertMqttModuleConfigPacket(config: config, nodeNum: toUser.num, context: context)
|
||||
await MeshPackets.shared.upsertMqttModuleConfigPacket(config: config, nodeNum: toUser.num, context: context)
|
||||
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
@ -1170,7 +1170,7 @@ extension AccessoryManager {
|
|||
|
||||
try await sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription)
|
||||
|
||||
upsertRangeTestModuleConfigPacket(config: config, nodeNum: toUser.num, context: context)
|
||||
await MeshPackets.shared.upsertRangeTestModuleConfigPacket(config: config, nodeNum: toUser.num, context: context)
|
||||
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
@ -1200,7 +1200,7 @@ extension AccessoryManager {
|
|||
let messageDescription = "🛟 Saved Serial Module Config for \(toUser.longName ?? "Unknown".localized)"
|
||||
try await sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription)
|
||||
|
||||
upsertSerialModuleConfigPacket(config: config, nodeNum: toUser.num, context: context)
|
||||
await MeshPackets.shared.upsertSerialModuleConfigPacket(config: config, nodeNum: toUser.num, context: context)
|
||||
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
@ -1379,7 +1379,7 @@ extension AccessoryManager {
|
|||
let messageDescription = "🛟 Saved Store & Forward Module Config for \(toUser.longName ?? "Unknown".localized)"
|
||||
try await sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription)
|
||||
|
||||
upsertStoreForwardModuleConfigPacket(config: config, nodeNum: toUser.num, context: context)
|
||||
await MeshPackets.shared.upsertStoreForwardModuleConfigPacket(config: config, nodeNum: toUser.num, context: context)
|
||||
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
@ -1615,7 +1615,7 @@ extension AccessoryManager {
|
|||
|
||||
try await sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription)
|
||||
|
||||
upsertPositionConfigPacket(config: config, nodeNum: toUser.num, context: context)
|
||||
try await MeshPackets.shared.upsertPositionConfigPacket(config: config, nodeNum: toUser.num)
|
||||
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
@ -1669,7 +1669,7 @@ extension AccessoryManager {
|
|||
|
||||
try await sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription)
|
||||
|
||||
upsertPowerConfigPacket(config: config, nodeNum: toUser.num, context: context)
|
||||
await MeshPackets.shared.upsertPowerConfigPacket(config: config, nodeNum: toUser.num)
|
||||
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
@ -1725,7 +1725,7 @@ extension AccessoryManager {
|
|||
|
||||
try await sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription)
|
||||
|
||||
upsertNetworkConfigPacket(config: config, nodeNum: toUser.num, context: context)
|
||||
await MeshPackets.shared.upsertNetworkConfigPacket(config: config, nodeNum: toUser.num)
|
||||
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
@ -1756,7 +1756,7 @@ extension AccessoryManager {
|
|||
|
||||
try await sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription)
|
||||
|
||||
upsertSecurityConfigPacket(config: config, nodeNum: toUser.num, context: context)
|
||||
await MeshPackets.shared.upsertSecurityConfigPacket(config: config, nodeNum: toUser.num)
|
||||
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
@ -1890,7 +1890,7 @@ extension AccessoryManager {
|
|||
|
||||
try await sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription)
|
||||
|
||||
upsertBluetoothConfigPacket(config: config, nodeNum: toUser.num, sessionPasskey: toUser.userNode?.sessionPasskey, context: context)
|
||||
await MeshPackets.shared.upsertBluetoothConfigPacket(config: config, nodeNum: toUser.num, sessionPasskey: toUser.userNode?.sessionPasskey)
|
||||
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
@ -1920,7 +1920,7 @@ extension AccessoryManager {
|
|||
let messageDescription = "Saved Telemetry Module Config for \(toUser.longName ?? "Unknown".localized)"
|
||||
try await sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription)
|
||||
|
||||
upsertTelemetryModuleConfigPacket(config: config, nodeNum: toUser.num, context: context)
|
||||
await MeshPackets.shared.upsertTelemetryModuleConfigPacket(config: config, nodeNum: toUser.num, context: context)
|
||||
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
@ -1973,7 +1973,7 @@ extension AccessoryManager {
|
|||
let messageDescription = "🛟 Saved Display Config for \(toUser.longName ?? "Unknown".localized)"
|
||||
try await sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription)
|
||||
|
||||
upsertDisplayConfigPacket(config: config, nodeNum: toUser.num, sessionPasskey: toUser.userNode?.sessionPasskey, context: context)
|
||||
await MeshPackets.shared.upsertDisplayConfigPacket(config: config, nodeNum: toUser.num, sessionPasskey: toUser.userNode?.sessionPasskey)
|
||||
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
@ -2052,7 +2052,7 @@ extension AccessoryManager {
|
|||
let messageDescription = "🛟 Saved Device Config for \(toUser.longName ?? "Unknown".localized)"
|
||||
try await sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription)
|
||||
|
||||
upsertDeviceConfigPacket(config: config, nodeNum: toUser.num, sessionPasskey: toUser.userNode?.sessionPasskey, context: context)
|
||||
await MeshPackets.shared.upsertDeviceConfigPacket(config: config, nodeNum: toUser.num, sessionPasskey: toUser.userNode?.sessionPasskey)
|
||||
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
@ -2081,7 +2081,7 @@ extension AccessoryManager {
|
|||
|
||||
try await sendAdminMessageToRadio(meshPacket: meshPacket, adminDescription: messageDescription)
|
||||
|
||||
upsertLoRaConfigPacket(config: config, nodeNum: toUser.num, sessionPasskey: toUser.userNode?.sessionPasskey, context: context)
|
||||
await MeshPackets.shared.upsertLoRaConfigPacket(config: config, nodeNum: toUser.num, sessionPasskey: toUser.userNode?.sessionPasskey)
|
||||
|
||||
return Int64(meshPacket.id)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ class AccessoryManager: ObservableObject, MqttClientProxyManagerDelegate {
|
|||
return
|
||||
}
|
||||
|
||||
_ = clearStaleNodes(nodeExpireDays: Int(UserDefaults.purgeStaleNodeDays), context: self.context)
|
||||
_ = await MeshPackets.shared.clearStaleNodes(nodeExpireDays: Int(UserDefaults.purgeStaleNodeDays))
|
||||
|
||||
try await withTaskCancellationHandler {
|
||||
var toRadio: ToRadio = ToRadio()
|
||||
|
|
@ -497,7 +497,7 @@ class AccessoryManager: ObservableObject, MqttClientProxyManagerDelegate {
|
|||
case .packet(let packet):
|
||||
// All received packets get passed through updateAnyPacketFrom to update lastHeard, rxSnr, etc. (like firmware's NodeDB::updateFrom).
|
||||
if let connectedNodeNum = self.activeDeviceNum {
|
||||
updateAnyPacketFrom(packet: packet, activeDeviceNum: connectedNodeNum, context: context)
|
||||
await MeshPackets.shared.updateAnyPacketFrom(packet: packet, activeDeviceNum: connectedNodeNum)
|
||||
} else {
|
||||
Logger.mesh.error("🕸️ Unable to determine connectedNodeNum for updateAnyPacketFrom. Skipping.")
|
||||
}
|
||||
|
|
@ -510,7 +510,7 @@ class AccessoryManager: ObservableObject, MqttClientProxyManagerDelegate {
|
|||
case .remoteHardwareApp:
|
||||
Logger.mesh.info("🕸️ MESH PACKET received for Remote Hardware App UNHANDLED \((try? decodedInfo.packet.jsonString()) ?? "JSON Decode Failure", privacy: .public)")
|
||||
case .positionApp:
|
||||
upsertPositionPacket(packet: packet, context: context)
|
||||
await MeshPackets.shared.upsertPositionPacket(packet: packet)
|
||||
case .waypointApp:
|
||||
await MeshPackets.shared.waypointPacket(packet: packet)
|
||||
case .nodeinfoApp:
|
||||
|
|
@ -519,7 +519,7 @@ class AccessoryManager: ObservableObject, MqttClientProxyManagerDelegate {
|
|||
return
|
||||
}
|
||||
if packet.from != connectedNodeNum {
|
||||
upsertNodeInfoPacket(packet: packet, context: context)
|
||||
await MeshPackets.shared.upsertNodeInfoPacket(packet: packet)
|
||||
} else {
|
||||
Logger.mesh.error("🕸️ Received a node info packet from ourselves over the mesh. Dropping.")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import Foundation
|
|||
import SwiftUI
|
||||
import OSLog
|
||||
|
||||
@MainActor
|
||||
class LocalNotificationManager {
|
||||
|
||||
var notifications = [Notification]()
|
||||
|
|
@ -10,20 +11,23 @@ class LocalNotificationManager {
|
|||
let replyInputAction = UNTextInputNotificationAction(identifier: "messageNotification.replyInputAction", title: "Reply".localized, options: [])
|
||||
|
||||
// Step 1 Request Permissions for notifications
|
||||
private func requestAuthorization() {
|
||||
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in
|
||||
|
||||
if granted == true && error == nil {
|
||||
self.scheduleNotifications()
|
||||
private func requestAuthorization() async {
|
||||
do {
|
||||
let granted = try await UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound])
|
||||
if granted {
|
||||
self.scheduleNotifications()
|
||||
}
|
||||
} catch {
|
||||
Logger.services.error("Error requesting notification authorization: \(error.localizedDescription, privacy: .public)")
|
||||
}
|
||||
}
|
||||
|
||||
func schedule() {
|
||||
UNUserNotificationCenter.current().getNotificationSettings { settings in
|
||||
Task { @MainActor in
|
||||
let settings = await UNUserNotificationCenter.current().notificationSettings()
|
||||
switch settings.authorizationStatus {
|
||||
case .notDetermined:
|
||||
self.requestAuthorization()
|
||||
await self.requestAuthorization()
|
||||
case .authorized, .provisional:
|
||||
self.scheduleNotifications()
|
||||
default:
|
||||
|
|
@ -97,7 +101,7 @@ class LocalNotificationManager {
|
|||
for notification in notifications {
|
||||
if let userInfo = notification.content.userInfo["messageId"] as? Int64, userInfo == messageId {
|
||||
Logger.services.debug("Cancelling notification with id: \(notification.identifier)")
|
||||
center.removePendingNotificationRequests(withIdentifiers: [notification.identifier])
|
||||
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [notification.identifier])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,69 +60,63 @@ actor MeshPackets {
|
|||
|
||||
// Create an actor-level background context
|
||||
// We keep this alive so sequential writes happen on the same context (efficient)
|
||||
private lazy var backgroundContext: NSManagedObjectContext = {
|
||||
lazy var backgroundContext: NSManagedObjectContext = {
|
||||
let ctx = PersistenceController.shared.container.newBackgroundContext()
|
||||
ctx.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy // Handle conflicts automatically
|
||||
return ctx
|
||||
}()
|
||||
|
||||
func localConfig (config: Config, nodeNum: Int64, nodeLongName: String) async {
|
||||
let context = self.backgroundContext
|
||||
await context.perform { [weak self] in
|
||||
switch config.payloadVariant {
|
||||
case .bluetooth:
|
||||
upsertBluetoothConfigPacket(config: config.bluetooth, nodeNum: nodeNum, context: context)
|
||||
case .device:
|
||||
upsertDeviceConfigPacket(config: config.device, nodeNum: nodeNum, context: context)
|
||||
case .display:
|
||||
upsertDisplayConfigPacket(config: config.display, nodeNum: nodeNum, context: context)
|
||||
case .lora:
|
||||
upsertLoRaConfigPacket(config: config.lora, nodeNum: nodeNum, context: context)
|
||||
case .network:
|
||||
upsertNetworkConfigPacket(config: config.network, nodeNum: nodeNum, context: context)
|
||||
case .position:
|
||||
upsertPositionConfigPacket(config: config.position, nodeNum: nodeNum, context: context)
|
||||
case .power:
|
||||
upsertPowerConfigPacket(config: config.power, nodeNum: nodeNum, context: context)
|
||||
case .security:
|
||||
upsertSecurityConfigPacket(config: config.security, nodeNum: nodeNum, context: context)
|
||||
default:
|
||||
switch config.payloadVariant {
|
||||
case .bluetooth:
|
||||
await self.upsertBluetoothConfigPacket(config: config.bluetooth, nodeNum: nodeNum)
|
||||
case .device:
|
||||
await self.upsertDeviceConfigPacket(config: config.device, nodeNum: nodeNum)
|
||||
case .display:
|
||||
await self.upsertDisplayConfigPacket(config: config.display, nodeNum: nodeNum)
|
||||
case .lora:
|
||||
await self.upsertLoRaConfigPacket(config: config.lora, nodeNum: nodeNum)
|
||||
case .network:
|
||||
await self.upsertNetworkConfigPacket(config: config.network, nodeNum: nodeNum)
|
||||
case .position:
|
||||
await self.upsertPositionConfigPacket(config: config.position, nodeNum: nodeNum)
|
||||
case .power:
|
||||
await self.upsertPowerConfigPacket(config: config.power, nodeNum: nodeNum)
|
||||
case .security:
|
||||
await self.upsertSecurityConfigPacket(config: config.security, nodeNum: nodeNum)
|
||||
default:
|
||||
#if DEBUG
|
||||
Logger.services.error("⁉️ Unknown Config variant UNHANDLED \(config.payloadVariant.debugDescription, privacy: .public)")
|
||||
Logger.services.error("⁉️ Unknown Config variant UNHANDLED \(config.payloadVariant.debugDescription, privacy: .public)")
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func moduleConfig (config: ModuleConfig, nodeNum: Int64, nodeLongName: String) async {
|
||||
let context = self.backgroundContext
|
||||
await context.perform {
|
||||
switch config.payloadVariant {
|
||||
case .ambientLighting:
|
||||
upsertAmbientLightingModuleConfigPacket(config: config.ambientLighting, nodeNum: nodeNum, context: context)
|
||||
case .cannedMessage:
|
||||
upsertCannedMessagesModuleConfigPacket(config: config.cannedMessage, nodeNum: nodeNum, context: context)
|
||||
case .detectionSensor:
|
||||
upsertDetectionSensorModuleConfigPacket(config: config.detectionSensor, nodeNum: nodeNum, context: context)
|
||||
case .externalNotification:
|
||||
upsertExternalNotificationModuleConfigPacket(config: config.externalNotification, nodeNum: nodeNum, context: context)
|
||||
case .mqtt:
|
||||
upsertMqttModuleConfigPacket(config: config.mqtt, nodeNum: nodeNum, context: context)
|
||||
case .paxcounter:
|
||||
upsertPaxCounterModuleConfigPacket(config: config.paxcounter, nodeNum: nodeNum, context: context)
|
||||
case .rangeTest:
|
||||
upsertRangeTestModuleConfigPacket(config: config.rangeTest, nodeNum: nodeNum, context: context)
|
||||
case .serial:
|
||||
upsertSerialModuleConfigPacket(config: config.serial, nodeNum: nodeNum, context: context)
|
||||
case .telemetry:
|
||||
upsertTelemetryModuleConfigPacket(config: config.telemetry, nodeNum: nodeNum, context: context)
|
||||
case .storeForward:
|
||||
upsertStoreForwardModuleConfigPacket(config: config.storeForward, nodeNum: nodeNum, context: context)
|
||||
default:
|
||||
switch config.payloadVariant {
|
||||
case .ambientLighting:
|
||||
await self.upsertAmbientLightingModuleConfigPacket(config: config.ambientLighting, nodeNum: nodeNum)
|
||||
case .cannedMessage:
|
||||
await self.upsertCannedMessagesModuleConfigPacket(config: config.cannedMessage, nodeNum: nodeNum)
|
||||
case .detectionSensor:
|
||||
await self.upsertDetectionSensorModuleConfigPacket(config: config.detectionSensor, nodeNum: nodeNum)
|
||||
case .externalNotification:
|
||||
await self.upsertExternalNotificationModuleConfigPacket(config: config.externalNotification, nodeNum: nodeNum)
|
||||
case .mqtt:
|
||||
await self.upsertMqttModuleConfigPacket(config: config.mqtt, nodeNum: nodeNum)
|
||||
case .paxcounter:
|
||||
await self.upsertPaxCounterModuleConfigPacket(config: config.paxcounter, nodeNum: nodeNum)
|
||||
case .rangeTest:
|
||||
await self.upsertRangeTestModuleConfigPacket(config: config.rangeTest, nodeNum: nodeNum)
|
||||
case .serial:
|
||||
await self.upsertSerialModuleConfigPacket(config: config.serial, nodeNum: nodeNum)
|
||||
case .telemetry:
|
||||
await self.upsertTelemetryModuleConfigPacket(config: config.telemetry, nodeNum: nodeNum)
|
||||
case .storeForward:
|
||||
await self.upsertStoreForwardModuleConfigPacket(config: config.storeForward, nodeNum: nodeNum)
|
||||
default:
|
||||
#if DEBUG
|
||||
Logger.services.error("⁉️ Unknown Module Config variant UNHANDLED \(config.payloadVariant.debugDescription, privacy: .public)")
|
||||
Logger.services.error("⁉️ Unknown Module Config variant UNHANDLED \(config.payloadVariant.debugDescription, privacy: .public)")
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -178,12 +172,13 @@ actor MeshPackets {
|
|||
}
|
||||
|
||||
func channelPacket (channel: Channel, fromNum: Int64) async {
|
||||
await backgroundContext.perform {
|
||||
self.channelPacket(channel: channel, fromNum: fromNum, context: self.backgroundContext)
|
||||
let context = self.backgroundContext
|
||||
await context.perform {
|
||||
self.channelPacket(channel: channel, fromNum: fromNum, context: context)
|
||||
}
|
||||
}
|
||||
|
||||
private func channelPacket (channel: Channel, fromNum: Int64, context: NSManagedObjectContext) {
|
||||
nonisolated private func channelPacket (channel: Channel, fromNum: Int64, context: NSManagedObjectContext) {
|
||||
if channel.isInitialized && channel.hasSettings && channel.role != Channel.Role.disabled {
|
||||
let logString = String.localizedStringWithFormat("mesh.log.channel.received %d %@".localized, channel.index, String(fromNum))
|
||||
Logger.mesh.info("🎛️ \(logString, privacy: .public)")
|
||||
|
|
@ -235,12 +230,13 @@ actor MeshPackets {
|
|||
}
|
||||
|
||||
func deviceMetadataPacket (metadata: DeviceMetadata, fromNum: Int64, sessionPasskey: Data? = Data()) async {
|
||||
await self.backgroundContext.perform {
|
||||
self.deviceMetadataPacket(metadata: metadata, fromNum: fromNum, sessionPasskey: sessionPasskey, context: self.backgroundContext)
|
||||
let context = self.backgroundContext
|
||||
await context.perform {
|
||||
self.deviceMetadataPacket(metadata: metadata, fromNum: fromNum, sessionPasskey: sessionPasskey, context: context)
|
||||
}
|
||||
}
|
||||
|
||||
private func deviceMetadataPacket (metadata: DeviceMetadata, fromNum: Int64, sessionPasskey: Data? = Data(), context: NSManagedObjectContext) {
|
||||
nonisolated private func deviceMetadataPacket (metadata: DeviceMetadata, fromNum: Int64, sessionPasskey: Data? = Data(), context: NSManagedObjectContext) {
|
||||
if metadata.isInitialized {
|
||||
let logString = String.localizedStringWithFormat("Device Metadata received from: %@".localized, fromNum.toHex())
|
||||
Logger.mesh.info("🏷️ \(logString, privacy: .public)")
|
||||
|
|
@ -595,46 +591,46 @@ actor MeshPackets {
|
|||
} else if adminMessage.payloadVariant == AdminMessage.OneOf_PayloadVariant.getConfigResponse(adminMessage.getConfigResponse) {
|
||||
let config = adminMessage.getConfigResponse
|
||||
if config.payloadVariant == Config.OneOf_PayloadVariant.bluetooth(config.bluetooth) {
|
||||
upsertBluetoothConfigPacket(config: config.bluetooth, nodeNum: Int64(packet.from), sessionPasskey: adminMessage.sessionPasskey, context: context)
|
||||
MeshPackets.shared.upsertBluetoothConfigPacket(config: config.bluetooth, nodeNum: Int64(packet.from), sessionPasskey: adminMessage.sessionPasskey, context: context)
|
||||
} else if config.payloadVariant == Config.OneOf_PayloadVariant.device(config.device) {
|
||||
upsertDeviceConfigPacket(config: config.device, nodeNum: Int64(packet.from), sessionPasskey: adminMessage.sessionPasskey, context: context)
|
||||
MeshPackets.shared.upsertDeviceConfigPacket(config: config.device, nodeNum: Int64(packet.from), sessionPasskey: adminMessage.sessionPasskey, context: context)
|
||||
} else if config.payloadVariant == Config.OneOf_PayloadVariant.display(config.display) {
|
||||
upsertDisplayConfigPacket(config: config.display, nodeNum: Int64(packet.from), sessionPasskey: adminMessage.sessionPasskey, context: context)
|
||||
self.upsertDisplayConfigPacket(config: config.display, nodeNum: Int64(packet.from), sessionPasskey: adminMessage.sessionPasskey, context: context)
|
||||
} else if config.payloadVariant == Config.OneOf_PayloadVariant.lora(config.lora) {
|
||||
upsertLoRaConfigPacket(config: config.lora, nodeNum: Int64(packet.from), sessionPasskey: adminMessage.sessionPasskey, context: context)
|
||||
self.upsertLoRaConfigPacket(config: config.lora, nodeNum: Int64(packet.from), sessionPasskey: adminMessage.sessionPasskey, context: context)
|
||||
} else if config.payloadVariant == Config.OneOf_PayloadVariant.network(config.network) {
|
||||
upsertNetworkConfigPacket(config: config.network, nodeNum: Int64(packet.from), sessionPasskey: adminMessage.sessionPasskey, context: context)
|
||||
self.upsertNetworkConfigPacket(config: config.network, nodeNum: Int64(packet.from), sessionPasskey: adminMessage.sessionPasskey, context: context)
|
||||
} else if config.payloadVariant == Config.OneOf_PayloadVariant.position(config.position) {
|
||||
upsertPositionConfigPacket(config: config.position, nodeNum: Int64(packet.from), sessionPasskey: adminMessage.sessionPasskey, context: context)
|
||||
self.upsertPositionConfigPacket(config: config.position, nodeNum: Int64(packet.from), sessionPasskey: adminMessage.sessionPasskey, context: context)
|
||||
} else if config.payloadVariant == Config.OneOf_PayloadVariant.power(config.power) {
|
||||
upsertPowerConfigPacket(config: config.power, nodeNum: Int64(packet.from), sessionPasskey: adminMessage.sessionPasskey, context: context)
|
||||
self.upsertPowerConfigPacket(config: config.power, nodeNum: Int64(packet.from), sessionPasskey: adminMessage.sessionPasskey, context: context)
|
||||
} else if config.payloadVariant == Config.OneOf_PayloadVariant.security(config.security) {
|
||||
upsertSecurityConfigPacket(config: config.security, nodeNum: Int64(packet.from), sessionPasskey: adminMessage.sessionPasskey, context: context)
|
||||
self.upsertSecurityConfigPacket(config: config.security, nodeNum: Int64(packet.from), sessionPasskey: adminMessage.sessionPasskey, context: context)
|
||||
}
|
||||
} else if adminMessage.payloadVariant == AdminMessage.OneOf_PayloadVariant.getModuleConfigResponse(adminMessage.getModuleConfigResponse) {
|
||||
let moduleConfig = adminMessage.getModuleConfigResponse
|
||||
if moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.ambientLighting(moduleConfig.ambientLighting) {
|
||||
upsertAmbientLightingModuleConfigPacket(config: moduleConfig.ambientLighting, nodeNum: Int64(packet.from), context: context)
|
||||
self.upsertAmbientLightingModuleConfigPacket(config: moduleConfig.ambientLighting, nodeNum: Int64(packet.from), context: context)
|
||||
} else if moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.cannedMessage(moduleConfig.cannedMessage) {
|
||||
upsertCannedMessagesModuleConfigPacket(config: moduleConfig.cannedMessage, nodeNum: Int64(packet.from), context: context)
|
||||
self.upsertCannedMessagesModuleConfigPacket(config: moduleConfig.cannedMessage, nodeNum: Int64(packet.from), context: context)
|
||||
} else if moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.detectionSensor(moduleConfig.detectionSensor) {
|
||||
upsertDetectionSensorModuleConfigPacket(config: moduleConfig.detectionSensor, nodeNum: Int64(packet.from), context: context)
|
||||
self.upsertDetectionSensorModuleConfigPacket(config: moduleConfig.detectionSensor, nodeNum: Int64(packet.from), context: context)
|
||||
} else if moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.externalNotification(moduleConfig.externalNotification) {
|
||||
upsertExternalNotificationModuleConfigPacket(config: moduleConfig.externalNotification, nodeNum: Int64(packet.from), context: context)
|
||||
self.upsertExternalNotificationModuleConfigPacket(config: moduleConfig.externalNotification, nodeNum: Int64(packet.from), context: context)
|
||||
} else if moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.mqtt(moduleConfig.mqtt) {
|
||||
upsertMqttModuleConfigPacket(config: moduleConfig.mqtt, nodeNum: Int64(packet.from), context: context)
|
||||
self.upsertMqttModuleConfigPacket(config: moduleConfig.mqtt, nodeNum: Int64(packet.from), context: context)
|
||||
} else if moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.rangeTest(moduleConfig.rangeTest) {
|
||||
upsertRangeTestModuleConfigPacket(config: moduleConfig.rangeTest, nodeNum: Int64(packet.from), context: context)
|
||||
self.upsertRangeTestModuleConfigPacket(config: moduleConfig.rangeTest, nodeNum: Int64(packet.from), context: context)
|
||||
} else if moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.serial(moduleConfig.serial) {
|
||||
upsertSerialModuleConfigPacket(config: moduleConfig.serial, nodeNum: Int64(packet.from), context: context)
|
||||
self.upsertSerialModuleConfigPacket(config: moduleConfig.serial, nodeNum: Int64(packet.from), context: context)
|
||||
} else if moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.storeForward(moduleConfig.storeForward) {
|
||||
upsertStoreForwardModuleConfigPacket(config: moduleConfig.storeForward, nodeNum: Int64(packet.from), context: context)
|
||||
self.upsertStoreForwardModuleConfigPacket(config: moduleConfig.storeForward, nodeNum: Int64(packet.from), context: context)
|
||||
} else if moduleConfig.payloadVariant == ModuleConfig.OneOf_PayloadVariant.telemetry(moduleConfig.telemetry) {
|
||||
upsertTelemetryModuleConfigPacket(config: moduleConfig.telemetry, nodeNum: Int64(packet.from), context: context)
|
||||
self.upsertTelemetryModuleConfigPacket(config: moduleConfig.telemetry, nodeNum: Int64(packet.from), context: context)
|
||||
}
|
||||
} else if adminMessage.payloadVariant == AdminMessage.OneOf_PayloadVariant.getRingtoneResponse(adminMessage.getRingtoneResponse) {
|
||||
if let rt = try? RTTTLConfig(serializedBytes: packet.decoded.payload) {
|
||||
upsertRtttlConfigPacket(ringtone: rt.ringtone, nodeNum: Int64(packet.from), context: context)
|
||||
self.upsertRtttlConfigPacket(ringtone: rt.ringtone, nodeNum: Int64(packet.from), context: context)
|
||||
}
|
||||
} else {
|
||||
Logger.mesh.error("🕸️ MESH PACKET received Admin App UNHANDLED \((try? packet.decoded.jsonString()) ?? "JSON Decode Failure", privacy: .public)")
|
||||
|
|
@ -645,7 +641,7 @@ actor MeshPackets {
|
|||
}
|
||||
}
|
||||
|
||||
private func adminResponseAck (packet: MeshPacket, context: NSManagedObjectContext) {
|
||||
nonisolated private func adminResponseAck (packet: MeshPacket, context: NSManagedObjectContext) {
|
||||
let fetchedAdminMessageRequest = MessageEntity.fetchRequest()
|
||||
fetchedAdminMessageRequest.predicate = NSPredicate(format: "messageId == %lld", packet.decoded.requestID)
|
||||
do {
|
||||
|
|
@ -879,19 +875,21 @@ actor MeshPackets {
|
|||
// Low Battery notification
|
||||
if connectedNode == Int64(packet.from) {
|
||||
let batteryLevel = telemetry.batteryLevel ?? 0
|
||||
if UserDefaults.lowBatteryNotifications && batteryLevel > 0 && batteryLevel < 4 {
|
||||
let manager = LocalNotificationManager()
|
||||
manager.notifications = [
|
||||
Notification(
|
||||
id: ("notification.id.\(UUID().uuidString)"),
|
||||
title: "Critically Low Battery!",
|
||||
subtitle: "AKA \(telemetry.nodeTelemetry?.user?.shortName ?? "UNK")",
|
||||
content: "Time to charge your radio, there is \(telemetry.batteryLevel?.formatted(.number) ?? Constants.nilValueIndicator)% battery remaining.",
|
||||
target: "nodes",
|
||||
path: "meshtastic:///nodes?nodenum=\(telemetry.nodeTelemetry?.num ?? 0)"
|
||||
)
|
||||
]
|
||||
manager.schedule()
|
||||
Task {@MainActor in
|
||||
if UserDefaults.lowBatteryNotifications && batteryLevel > 0 && batteryLevel < 4 {
|
||||
let manager = LocalNotificationManager()
|
||||
manager.notifications = [
|
||||
Notification(
|
||||
id: ("notification.id.\(UUID().uuidString)"),
|
||||
title: "Critically Low Battery!",
|
||||
subtitle: "AKA \(telemetry.nodeTelemetry?.user?.shortName ?? "UNK")",
|
||||
content: "Time to charge your radio, there is \(telemetry.batteryLevel?.formatted(.number) ?? Constants.nilValueIndicator)% battery remaining.",
|
||||
target: "nodes",
|
||||
path: "meshtastic:///nodes?nodenum=\(telemetry.nodeTelemetry?.num ?? 0)"
|
||||
)
|
||||
]
|
||||
manager.schedule()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if telemetry.metricsType == 4 {
|
||||
|
|
@ -1098,23 +1096,26 @@ actor MeshPackets {
|
|||
}
|
||||
if !(newMessage.fromUser?.mute ?? false) && newMessage.isEmoji == false {
|
||||
// Create an iOS Notification for the received DM message
|
||||
let manager = LocalNotificationManager()
|
||||
manager.notifications = [
|
||||
Notification(
|
||||
id: ("notification.id.\(newMessage.messageId)"),
|
||||
title: "\(newMessage.fromUser?.longName ?? "Unknown".localized)",
|
||||
subtitle: "AKA \(newMessage.fromUser?.shortName ?? "?")",
|
||||
content: messageText!,
|
||||
target: "messages",
|
||||
path: "meshtastic:///messages?userNum=\(newMessage.fromUser?.num ?? 0)&messageId=\(newMessage.isEmoji ? newMessage.replyID : newMessage.messageId)",
|
||||
messageId: newMessage.messageId,
|
||||
channel: newMessage.channel,
|
||||
userNum: Int64(packet.from),
|
||||
critical: critical
|
||||
)
|
||||
]
|
||||
manager.schedule()
|
||||
Logger.services.debug("iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? "Unknown".localized, privacy: .public)")
|
||||
Task {@MainActor in
|
||||
let manager = LocalNotificationManager()
|
||||
manager.notifications = [
|
||||
Notification(
|
||||
id: ("notification.id.\(newMessage.messageId)"),
|
||||
title: "\(newMessage.fromUser?.longName ?? "Unknown".localized)",
|
||||
subtitle: "AKA \(newMessage.fromUser?.shortName ?? "?")",
|
||||
content: messageText!,
|
||||
target: "messages",
|
||||
path: "meshtastic:///messages?userNum=\(newMessage.fromUser?.num ?? 0)&messageId=\(newMessage.isEmoji ? newMessage.replyID : newMessage.messageId)",
|
||||
messageId: newMessage.messageId,
|
||||
channel: newMessage.channel,
|
||||
userNum: Int64(packet.from),
|
||||
critical: critical
|
||||
)
|
||||
]
|
||||
manager.schedule()
|
||||
|
||||
Logger.services.debug("iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? "Unknown".localized, privacy: .public)")
|
||||
}
|
||||
}
|
||||
} else if newMessage.fromUser != nil && newMessage.toUser == nil {
|
||||
let fetchMyInfoRequest = MyInfoEntity.fetchRequest()
|
||||
|
|
@ -1122,30 +1123,32 @@ actor MeshPackets {
|
|||
do {
|
||||
let fetchedMyInfo = try context.fetch(fetchMyInfoRequest)
|
||||
if !fetchedMyInfo.isEmpty {
|
||||
appState?.unreadChannelMessages = fetchedMyInfo[0].unreadMessages(context: context)
|
||||
for channel in (fetchedMyInfo[0].channels?.array ?? []) as? [ChannelEntity] ?? [] {
|
||||
if channel.index == newMessage.channel {
|
||||
context.refresh(channel, mergeChanges: true)
|
||||
}
|
||||
if channel.index == newMessage.channel && !channel.mute && UserDefaults.channelMessageNotifications && newMessage.isEmoji == false {
|
||||
// Create an iOS Notification for the received channel message
|
||||
let manager = LocalNotificationManager()
|
||||
manager.notifications = [
|
||||
Notification(
|
||||
id: ("notification.id.\(newMessage.messageId)"),
|
||||
title: "\(newMessage.fromUser?.longName ?? "Unknown".localized)",
|
||||
subtitle: "AKA \(newMessage.fromUser?.shortName ?? "?")",
|
||||
content: messageText!,
|
||||
target: "messages",
|
||||
path: "meshtastic:///messages?channelId=\(newMessage.channel)&messageId=\(newMessage.isEmoji ? newMessage.replyID : newMessage.messageId)",
|
||||
messageId: newMessage.messageId,
|
||||
channel: newMessage.channel,
|
||||
userNum: Int64(newMessage.fromUser?.userId ?? "0"),
|
||||
critical: critical
|
||||
)
|
||||
]
|
||||
manager.schedule()
|
||||
Logger.services.debug("iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? "Unknown".localized, privacy: .public)")
|
||||
Task {@MainActor in
|
||||
appState?.unreadChannelMessages = fetchedMyInfo[0].unreadMessages(context: context)
|
||||
for channel in (fetchedMyInfo[0].channels?.array ?? []) as? [ChannelEntity] ?? [] {
|
||||
if channel.index == newMessage.channel {
|
||||
context.refresh(channel, mergeChanges: true)
|
||||
}
|
||||
if channel.index == newMessage.channel && !channel.mute && UserDefaults.channelMessageNotifications && newMessage.isEmoji == false {
|
||||
// Create an iOS Notification for the received channel message
|
||||
let manager = LocalNotificationManager()
|
||||
manager.notifications = [
|
||||
Notification(
|
||||
id: ("notification.id.\(newMessage.messageId)"),
|
||||
title: "\(newMessage.fromUser?.longName ?? "Unknown".localized)",
|
||||
subtitle: "AKA \(newMessage.fromUser?.shortName ?? "?")",
|
||||
content: messageText!,
|
||||
target: "messages",
|
||||
path: "meshtastic:///messages?channelId=\(newMessage.channel)&messageId=\(newMessage.isEmoji ? newMessage.replyID : newMessage.messageId)",
|
||||
messageId: newMessage.messageId,
|
||||
channel: newMessage.channel,
|
||||
userNum: Int64(newMessage.fromUser?.userId ?? "0"),
|
||||
critical: critical
|
||||
)
|
||||
]
|
||||
manager.schedule()
|
||||
Logger.services.debug("iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? "Unknown".localized, privacy: .public)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1205,22 +1208,25 @@ actor MeshPackets {
|
|||
do {
|
||||
try context.save()
|
||||
Logger.data.info("💾 Added Node Waypoint App Packet For: \(waypoint.id, privacy: .public)")
|
||||
let manager = LocalNotificationManager()
|
||||
let icon = String(UnicodeScalar(Int(waypoint.icon)) ?? "📍")
|
||||
let latitude = Double(waypoint.latitudeI) / 1e7
|
||||
let longitude = Double(waypoint.longitudeI) / 1e7
|
||||
manager.notifications = [
|
||||
Notification(
|
||||
id: ("notification.id.\(waypoint.id)"),
|
||||
title: "New Waypoint From \(nodeShortName)",
|
||||
subtitle: "\(icon) \(waypoint.name ?? "Dropped Pin")",
|
||||
content: "\(waypoint.longDescription ?? "\(latitude), \(longitude)")",
|
||||
target: "map",
|
||||
path: "meshtastic:///map?waypointid=\(waypoint.id)"
|
||||
)
|
||||
]
|
||||
Logger.data.debug("meshtastic:///map?waypointid=\(waypoint.id, privacy: .public)")
|
||||
manager.schedule()
|
||||
|
||||
Task { @MainActor in
|
||||
let manager = LocalNotificationManager()
|
||||
let icon = String(UnicodeScalar(Int(waypoint.icon)) ?? "📍")
|
||||
let latitude = Double(waypoint.latitudeI) / 1e7
|
||||
let longitude = Double(waypoint.longitudeI) / 1e7
|
||||
manager.notifications = [
|
||||
Notification(
|
||||
id: ("notification.id.\(waypoint.id)"),
|
||||
title: "New Waypoint From \(nodeShortName)",
|
||||
subtitle: "\(icon) \(waypoint.name ?? "Dropped Pin")",
|
||||
content: "\(waypoint.longDescription ?? "\(latitude), \(longitude)")",
|
||||
target: "map",
|
||||
path: "meshtastic:///map?waypointid=\(waypoint.id)"
|
||||
)
|
||||
]
|
||||
Logger.data.debug("meshtastic:///map?waypointid=\(waypoint.id, privacy: .public)")
|
||||
manager.schedule()
|
||||
}
|
||||
} catch {
|
||||
context.rollback()
|
||||
let nsError = error as NSError
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -511,23 +511,23 @@ struct ManualConnectionMenu: View {
|
|||
})
|
||||
}.confirmationDialog("Connecting to a new radio will clear all app data on the phone.", isPresented: $presentingSwitchPreferredPeripheral, titleVisibility: .visible) {
|
||||
Button("Connect to new radio?", role: .destructive) {
|
||||
if let device = deviceForManualConnection {
|
||||
UserDefaults.preferredPeripheralId = device.id.uuidString
|
||||
UserDefaults.preferredPeripheralNum = 0
|
||||
if accessoryManager.allowDisconnect {
|
||||
Task { try await accessoryManager.disconnect() }
|
||||
}
|
||||
clearCoreDataDatabase(context: context, includeRoutes: false)
|
||||
clearNotifications()
|
||||
Task {
|
||||
try await selectedTransport?.transport.manuallyConnect(toDevice: device)
|
||||
}
|
||||
|
||||
// Clean up just in case
|
||||
deviceForManualConnection = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
Task {
|
||||
if let device = deviceForManualConnection {
|
||||
UserDefaults.preferredPeripheralId = device.id.uuidString
|
||||
UserDefaults.preferredPeripheralNum = 0
|
||||
if accessoryManager.allowDisconnect {
|
||||
try await accessoryManager.disconnect()
|
||||
}
|
||||
await MeshPackets.shared.clearCoreDataDatabase(includeRoutes: false)
|
||||
clearNotifications()
|
||||
try await selectedTransport?.transport.manuallyConnect(toDevice: device)
|
||||
|
||||
// Clean up just in case
|
||||
deviceForManualConnection = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -593,15 +593,17 @@ struct DeviceConnectRow: View {
|
|||
}.padding([.bottom, .top])
|
||||
.confirmationDialog("Connecting to a new radio will clear all app data on the phone.", isPresented: $presentingSwitchPreferredPeripheral, titleVisibility: .visible) {
|
||||
Button("Connect to new radio?", role: .destructive) {
|
||||
UserDefaults.preferredPeripheralId = device.id.uuidString
|
||||
UserDefaults.preferredPeripheralNum = 0
|
||||
if accessoryManager.allowDisconnect {
|
||||
Task { try await accessoryManager.disconnect() }
|
||||
}
|
||||
clearCoreDataDatabase(context: context, includeRoutes: false)
|
||||
clearNotifications()
|
||||
Task {
|
||||
UserDefaults.preferredPeripheralId = device.id.uuidString
|
||||
UserDefaults.preferredPeripheralNum = 0
|
||||
if accessoryManager.allowDisconnect {
|
||||
try await accessoryManager.disconnect()
|
||||
}
|
||||
await MeshPackets.shared.clearCoreDataDatabase(includeRoutes: false)
|
||||
clearNotifications()
|
||||
|
||||
try await accessoryManager.connect(to: device)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -160,9 +160,11 @@ struct ChannelList: View {
|
|||
titleVisibility: .visible
|
||||
) {
|
||||
Button(role: .destructive) {
|
||||
deleteChannelMessages(channel: channelToDeleteMessages!, context: context)
|
||||
context.refresh(myInfo, mergeChanges: true)
|
||||
channelToDeleteMessages = nil
|
||||
Task {
|
||||
await MeshPackets.shared.deleteChannelMessages(channel: channelToDeleteMessages!)
|
||||
context.refresh(myInfo, mergeChanges: true)
|
||||
channelToDeleteMessages = nil
|
||||
}
|
||||
} label: {
|
||||
Text("Delete")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -224,8 +224,10 @@ fileprivate struct FilteredUserList: View {
|
|||
titleVisibility: .visible
|
||||
) {
|
||||
Button(role: .destructive) {
|
||||
deleteUserMessages(user: userToDeleteMessages!, context: context)
|
||||
context.refresh(node!.user!, mergeChanges: true)
|
||||
Task {
|
||||
await MeshPackets.shared.deleteUserMessages(user: userToDeleteMessages!)
|
||||
context.refresh(node!.user!, mergeChanges: true)
|
||||
}
|
||||
} label: {
|
||||
Text("Delete")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -199,10 +199,12 @@ struct DeviceMetricsLog: View {
|
|||
titleVisibility: .visible
|
||||
) {
|
||||
Button("Delete all device metrics?", role: .destructive) {
|
||||
if clearTelemetry(destNum: node.num, metricsType: 0, context: context) {
|
||||
Logger.data.notice("Cleared Device Metrics for \(node.num, privacy: .public)")
|
||||
} else {
|
||||
Logger.data.error("Clear Device Metrics Log Failed")
|
||||
Task {
|
||||
if await MeshPackets.shared.clearTelemetry(destNum: node.num, metricsType: 0) {
|
||||
Logger.data.notice("Cleared Device Metrics for \(node.num, privacy: .public)")
|
||||
} else {
|
||||
Logger.data.error("Clear Device Metrics Log Failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,8 +128,10 @@ struct EnvironmentMetricsLog: View {
|
|||
titleVisibility: .visible
|
||||
) {
|
||||
Button("Delete all environment metrics?", role: .destructive) {
|
||||
if clearTelemetry(destNum: node.num, metricsType: 1, context: context) {
|
||||
Logger.services.error("Clear Environment Metrics Log Failed")
|
||||
Task {
|
||||
if await MeshPackets.shared.clearTelemetry(destNum: node.num, metricsType: 1) {
|
||||
Logger.services.error("Clear Environment Metrics Log Failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -175,10 +175,12 @@ struct PaxCounterLog: View {
|
|||
titleVisibility: .visible
|
||||
) {
|
||||
Button("Delete all pax data?", role: .destructive) {
|
||||
if clearPax(destNum: node.num, context: context) {
|
||||
Logger.services.info("Cleared Pax Counter for \(node.num, privacy: .public)")
|
||||
} else {
|
||||
Logger.services.error("Clear Pax Counter Log Failed")
|
||||
Task {
|
||||
if await MeshPackets.shared.clearPax(destNum: node.num) {
|
||||
Logger.services.info("Cleared Pax Counter for \(node.num, privacy: .public)")
|
||||
} else {
|
||||
Logger.services.error("Clear Pax Counter Log Failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -131,10 +131,12 @@ struct PositionLog: View {
|
|||
titleVisibility: .visible
|
||||
) {
|
||||
Button("Delete all positions?", role: .destructive) {
|
||||
if clearPositions(destNum: node.num, context: context) {
|
||||
Logger.services.info("Successfully Cleared Position Log")
|
||||
} else {
|
||||
Logger.services.error("Clear Position Log Failed")
|
||||
Task {
|
||||
if await MeshPackets.shared.clearPositions(destNum: node.num) {
|
||||
Logger.services.info("Successfully Cleared Position Log")
|
||||
} else {
|
||||
Logger.services.error("Clear Position Log Failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -242,10 +242,12 @@ struct PowerMetricsLog: View {
|
|||
titleVisibility: .visible
|
||||
) {
|
||||
Button("Delete Power metrics?", role: .destructive) {
|
||||
if clearTelemetry(destNum: node.num, metricsType: 2, context: context) {
|
||||
Logger.data.notice("Cleared Power Metrics for \(node.num, privacy: .public)")
|
||||
} else {
|
||||
Logger.data.error("Clear Power Metrics Log Failed")
|
||||
Task {
|
||||
if await MeshPackets.shared.clearTelemetry(destNum: node.num, metricsType: 2) {
|
||||
Logger.data.notice("Cleared Power Metrics for \(node.num, privacy: .public)")
|
||||
} else {
|
||||
Logger.data.error("Clear Power Metrics Log Failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -138,30 +138,31 @@ struct AppSettings: View {
|
|||
Button("Erase all app data?", role: .destructive) {
|
||||
Task {
|
||||
try await accessoryManager.disconnect()
|
||||
}
|
||||
/// Delete any database backups too
|
||||
if var url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
|
||||
url = url.appendingPathComponent("backup").appendingPathComponent(String(UserDefaults.preferredPeripheralNum))
|
||||
do {
|
||||
try FileManager.default.removeItem(at: url.appendingPathComponent("Meshtastic.sqlite"))
|
||||
/// Delete -shm file
|
||||
|
||||
/// Delete any database backups too
|
||||
if var url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
|
||||
url = url.appendingPathComponent("backup").appendingPathComponent(String(UserDefaults.preferredPeripheralNum))
|
||||
do {
|
||||
try FileManager.default.removeItem(at: url.appendingPathComponent("Meshtastic.sqlite-wal"))
|
||||
try FileManager.default.removeItem(at: url.appendingPathComponent("Meshtastic.sqlite"))
|
||||
/// Delete -shm file
|
||||
do {
|
||||
try FileManager.default.removeItem(at: url.appendingPathComponent("Meshtastic.sqlite-shm"))
|
||||
try FileManager.default.removeItem(at: url.appendingPathComponent("Meshtastic.sqlite-wal"))
|
||||
do {
|
||||
try FileManager.default.removeItem(at: url.appendingPathComponent("Meshtastic.sqlite-shm"))
|
||||
} catch {
|
||||
Logger.services.error("🗄 Error Deleting Meshtastic.sqlite-shm file \(error, privacy: .public)")
|
||||
}
|
||||
} catch {
|
||||
Logger.services.error("🗄 Error Deleting Meshtastic.sqlite-shm file \(error, privacy: .public)")
|
||||
Logger.services.error("🗄 Error Deleting Meshtastic.sqlite-wal file \(error, privacy: .public)")
|
||||
}
|
||||
} catch {
|
||||
Logger.services.error("🗄 Error Deleting Meshtastic.sqlite-wal file \(error, privacy: .public)")
|
||||
Logger.services.error("🗄 Error Deleting Meshtastic.sqlite file \(error, privacy: .public)")
|
||||
}
|
||||
} catch {
|
||||
Logger.services.error("🗄 Error Deleting Meshtastic.sqlite file \(error, privacy: .public)")
|
||||
}
|
||||
await MeshPackets.shared.clearCoreDataDatabase(includeRoutes: true)
|
||||
clearNotifications()
|
||||
context.refreshAllObjects()
|
||||
}
|
||||
clearCoreDataDatabase(context: context, includeRoutes: true)
|
||||
clearNotifications()
|
||||
context.refreshAllObjects()
|
||||
}
|
||||
}
|
||||
Button {
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@ struct DeviceConfig: View {
|
|||
try await accessoryManager.sendNodeDBReset(fromUser: node!.user!, toUser: node!.user!)
|
||||
try await Task.sleep(for: .seconds(1))
|
||||
try await accessoryManager.disconnect()
|
||||
clearCoreDataDatabase(context: context, includeRoutes: false)
|
||||
await MeshPackets.shared.clearCoreDataDatabase(context: context, includeRoutes: false)
|
||||
clearNotifications()
|
||||
} catch {
|
||||
Logger.mesh.error("NodeDB Reset Failed")
|
||||
|
|
@ -200,7 +200,7 @@ struct DeviceConfig: View {
|
|||
try await accessoryManager.sendFactoryReset(fromUser: node!.user!, toUser: node!.user!)
|
||||
try await Task.sleep(for: .seconds(1))
|
||||
try await accessoryManager.disconnect()
|
||||
clearCoreDataDatabase(context: context, includeRoutes: false)
|
||||
await MeshPackets.shared.clearCoreDataDatabase(context: context, includeRoutes: false)
|
||||
clearNotifications()
|
||||
} catch {
|
||||
Logger.mesh.error("Factory Reset Failed")
|
||||
|
|
@ -213,7 +213,7 @@ struct DeviceConfig: View {
|
|||
try await accessoryManager.sendFactoryReset(fromUser: node!.user!, toUser: node!.user!, resetDevice: true)
|
||||
try? await Task.sleep(for: .seconds(1))
|
||||
try await accessoryManager.disconnect()
|
||||
clearCoreDataDatabase(context: context, includeRoutes: false)
|
||||
await MeshPackets.shared.clearCoreDataDatabase(context: context, includeRoutes: false)
|
||||
clearNotifications()
|
||||
} catch {
|
||||
Logger.mesh.error("Factory Reset Failed")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue