From c09291e1b2f0c2b548c8bdc3ce2fca25d41abec3 Mon Sep 17 00:00:00 2001 From: Benjamin Faershtein <119711889+RCGV1@users.noreply.github.com> Date: Wed, 4 Jun 2025 09:25:30 -0700 Subject: [PATCH 1/8] Added a button to change waypoint to your location --- .../CoreData/WaypointEntityExtension.swift | 28 ++++++++++++------- .../Nodes/Helpers/Map/WaypointForm.swift | 8 ++++++ 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/Meshtastic/Extensions/CoreData/WaypointEntityExtension.swift b/Meshtastic/Extensions/CoreData/WaypointEntityExtension.swift index 2f538b62..240e6d76 100644 --- a/Meshtastic/Extensions/CoreData/WaypointEntityExtension.swift +++ b/Meshtastic/Extensions/CoreData/WaypointEntityExtension.swift @@ -14,9 +14,6 @@ extension WaypointEntity { static func allWaypointssFetchRequest() -> NSFetchRequest { let request: NSFetchRequest = WaypointEntity.fetchRequest() request.fetchLimit = 50 - // request.fetchBatchSize = 1 - // request.returnsObjectsAsFaults = false - // request.includesSubentities = true request.returnsDistinctResults = true request.sortDescriptors = [NSSortDescriptor(key: "name", ascending: false)] request.predicate = NSPredicate(format: "expire == nil || expire >= %@", Date() as NSDate) @@ -24,7 +21,6 @@ extension WaypointEntity { } var latitude: Double? { - let d = Double(latitudeI) if d == 0 { return 0 @@ -33,7 +29,6 @@ extension WaypointEntity { } var longitude: Double? { - let d = Double(longitudeI) if d == 0 { return 0 @@ -46,7 +41,7 @@ extension WaypointEntity { let coord = CLLocationCoordinate2D(latitude: latitude!, longitude: longitude!) return coord } else { - return nil + return nil } } @@ -60,16 +55,29 @@ extension WaypointEntity { } extension WaypointEntity: MKAnnotation { - public var coordinate: CLLocationCoordinate2D { waypointCoordinate ?? LocationsHandler.DefaultLocation } - public var title: String? { name ?? "Dropped Pin" } + @MainActor + public var coordinate: CLLocationCoordinate2D { + get { + waypointCoordinate ?? LocationsHandler.currentLocation + } + set { + latitudeI = Int32(newValue.latitude * 1e7) + longitudeI = Int32(newValue.longitude * 1e7) + } + } + + public var title: String? { + name ?? "Dropped Pin" + } + public var subtitle: String? { (longDescription ?? "") + String(expire != nil ? "\nāŒ› Expires \(String(describing: expire?.formatted()))" : "") + - String(locked > 0 ? "\nšŸ”’ Locked" : "") } + String(locked > 0 ? "\nšŸ”’ Locked" : "") + } } struct WaypointCoordinate: Identifiable { - let id: UUID let coordinate: CLLocationCoordinate2D? let waypointId: Int64 diff --git a/Meshtastic/Views/Nodes/Helpers/Map/WaypointForm.swift b/Meshtastic/Views/Nodes/Helpers/Map/WaypointForm.swift index ad342cbc..57320259 100644 --- a/Meshtastic/Views/Nodes/Helpers/Map/WaypointForm.swift +++ b/Meshtastic/Views/Nodes/Helpers/Map/WaypointForm.swift @@ -47,6 +47,14 @@ struct WaypointForm: View { .textSelection(.enabled) .foregroundColor(.secondary) .font(.caption) + + Button { + let currentLoc = LocationsHandler.currentLocation + waypoint.coordinate.longitude = currentLoc.longitude + waypoint.coordinate.latitude = currentLoc.latitude + } label: { + Image(systemName: "location") + } } HStack { if waypoint.coordinate.latitude != 0 && waypoint.coordinate.longitude != 0 { From 8d65985521ebf2c81584f4c1903b4281eed86c64 Mon Sep 17 00:00:00 2001 From: Benjamin Faershtein <119711889+RCGV1@users.noreply.github.com> Date: Wed, 4 Jun 2025 15:56:17 -0700 Subject: [PATCH 2/8] Updated send waypoint intent --- Localizable.xcstrings | 16 +++++++ .../AppIntents/SendWaypointIntent.swift | 48 ++++++++++++++----- 2 files changed, 51 insertions(+), 13 deletions(-) diff --git a/Localizable.xcstrings b/Localizable.xcstrings index eec7ee0d..7f283f03 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -11388,6 +11388,9 @@ } } } + }, + "Expiration" : { + }, "Expire" : { "localizations" : { @@ -15747,6 +15750,12 @@ } } } + }, + "Latitude in degrees (e.g., 37.7749)" : { + + }, + "Latitude must be between -90 and 90 degrees" : { + }, "LED Heartbeat" : { "localizations" : { @@ -16055,6 +16064,7 @@ } }, "Location" : { + "extractionState" : "stale", "localizations" : { "de" : { "stringUnit" : { @@ -16493,6 +16503,12 @@ } } } + }, + "Longitude in degrees (e.g., -122.4194)" : { + + }, + "Longitude must be between -180 and 180 degrees" : { + }, "LoRa" : { "localizations" : { diff --git a/Meshtastic/AppIntents/SendWaypointIntent.swift b/Meshtastic/AppIntents/SendWaypointIntent.swift index 4352c548..e41732db 100644 --- a/Meshtastic/AppIntents/SendWaypointIntent.swift +++ b/Meshtastic/AppIntents/SendWaypointIntent.swift @@ -11,6 +11,8 @@ import AppIntents import MeshtasticProtobufs struct SendWaypointIntent: AppIntent { + + var defaultDate = Date.now.addingTimeInterval(60 * 480) static var title = LocalizedStringResource("Send a Waypoint") @@ -23,13 +25,24 @@ struct SendWaypointIntent: AppIntent { @Parameter(title: "Emoji", default: "šŸ“") var emojiParameter: String? - @Parameter(title: "Location") - var locationParameter: CLPlacemark + // Replace CLPlacemark with latitude and longitude parameters + @Parameter(title: "Latitude", description: "Latitude in degrees (e.g., 37.7749)") + var latitudeParameter: Double + + @Parameter(title: "Longitude", description: "Longitude in degrees (e.g., -122.4194)") + var longitudeParameter: Double + + @Parameter(title: "Locked", default: false) + var isLocked: Bool + + @Parameter(title: "Expiration") + var expiration: Date? func perform() async throws -> some IntentResult { if !BLEManager.shared.isConnected { throw AppIntentErrors.AppIntentError.notConnected } + // Provide default values if parameters are nil let name = nameParameter ?? "Dropped Pin" let description = descriptionParameter ?? "" @@ -50,24 +63,35 @@ struct SendWaypointIntent: AppIntent { throw $emojiParameter.needsValueError("Must be a single emoji") } + // Validate latitude and longitude + guard abs(latitudeParameter) <= 90 else { + throw $latitudeParameter.needsValueError("Latitude must be between -90 and 90 degrees") + } + guard abs(longitudeParameter) <= 180 else { + throw $longitudeParameter.needsValueError("Longitude must be between -180 and 180 degrees") + } + var newWaypoint = Waypoint() - if let latitude = locationParameter.location?.coordinate.latitude { - newWaypoint.latitudeI = Int32(latitude * 10_000_000) - } - - if let longitude = locationParameter.location?.coordinate.longitude { - newWaypoint.longitudeI = Int32(longitude * 10_000_000) - } + // Set latitude and longitude directly + newWaypoint.latitudeI = Int32(latitudeParameter * 10_000_000) + newWaypoint.longitudeI = Int32(longitudeParameter * 10_000_000) newWaypoint.id = UInt32.random(in: UInt32(UInt8.max).. Bool { - // This regex pattern is for matching a single emoji let emojiPattern = "^([\\p{So}\\p{Cn}])$" let regex = try? NSRegularExpression(pattern: emojiPattern, options: []) let matches = regex?.matches(in: emoji, options: [], range: NSRange(location: 0, length: emoji.utf16.count)) - return matches?.count == 1 } } From bd666a742c5c6c75d0f796496b32359f7d24e27a Mon Sep 17 00:00:00 2001 From: Benjamin Faershtein <119711889+RCGV1@users.noreply.github.com> Date: Thu, 5 Jun 2025 08:24:17 -0700 Subject: [PATCH 3/8] More graceful failures --- Localizable.xcstrings | 3 +++ .../CoreData/WaypointEntityExtension.swift | 2 +- Meshtastic/Helpers/BLEManager.swift | 2 +- .../Nodes/Helpers/Map/WaypointForm.swift | 19 ++++++++++++++++--- 4 files changed, 21 insertions(+), 5 deletions(-) diff --git a/Localizable.xcstrings b/Localizable.xcstrings index 7f283f03..e6a7809f 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -34355,6 +34355,9 @@ } } } + }, + "Waypoiny Failed to Send" : { + }, "Weather Conditions" : { "localizations" : { diff --git a/Meshtastic/Extensions/CoreData/WaypointEntityExtension.swift b/Meshtastic/Extensions/CoreData/WaypointEntityExtension.swift index 240e6d76..4f2923eb 100644 --- a/Meshtastic/Extensions/CoreData/WaypointEntityExtension.swift +++ b/Meshtastic/Extensions/CoreData/WaypointEntityExtension.swift @@ -58,7 +58,7 @@ extension WaypointEntity: MKAnnotation { @MainActor public var coordinate: CLLocationCoordinate2D { get { - waypointCoordinate ?? LocationsHandler.currentLocation + waypointCoordinate ?? LocationsHandler.DefaultLocation } set { latitudeI = Int32(newValue.latitude * 1e7) diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 050958e0..59571549 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -1183,7 +1183,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate } public func sendWaypoint(waypoint: Waypoint) -> Bool { - if waypoint.latitudeI == 373346000 && waypoint.longitudeI == -1220090000 { + if waypoint.latitudeI == 0 && waypoint.longitudeI == 0 { return false } var success = false diff --git a/Meshtastic/Views/Nodes/Helpers/Map/WaypointForm.swift b/Meshtastic/Views/Nodes/Helpers/Map/WaypointForm.swift index 57320259..e813b854 100644 --- a/Meshtastic/Views/Nodes/Helpers/Map/WaypointForm.swift +++ b/Meshtastic/Views/Nodes/Helpers/Map/WaypointForm.swift @@ -30,6 +30,7 @@ struct WaypointForm: View { @State private var lockedTo: Int64 = 0 @State private var detents: Set = [.medium, .fraction(0.85)] @State private var selectedDetent: PresentationDetent = .medium + @State private var waypointFailedAlert: Bool = false var body: some View { NavigationStack { @@ -47,7 +48,7 @@ struct WaypointForm: View { .textSelection(.enabled) .foregroundColor(.secondary) .font(.caption) - + Button { let currentLoc = LocationsHandler.currentLocation waypoint.coordinate.longitude = currentLoc.longitude @@ -80,6 +81,7 @@ struct WaypointForm: View { name = String(name.dropLast()) totalBytes = name.utf8.count } + waypoint.name = name.count > 0 ? name : "Dropped Pin" } } HStack { @@ -175,8 +177,8 @@ struct WaypointForm: View { if bleManager.sendWaypoint(waypoint: newWaypoint) { dismiss() } else { - dismiss() Logger.mesh.warning("Send waypoint failed") + waypointFailedAlert = true } } else { Logger.mesh.warning("Send waypoint failed, node not connected") @@ -241,8 +243,8 @@ struct WaypointForm: View { } dismiss() } else { - dismiss() Logger.mesh.warning("Send waypoint failed") + waypointFailedAlert = true } }) } @@ -376,6 +378,17 @@ struct WaypointForm: View { } } } + .alert("Waypoiny Failed to Send", isPresented: $waypointFailedAlert) { + Button("OK", role: .cancel) { + bleManager.context.delete(waypoint) + do { + try bleManager.context.save() + } catch { + bleManager.context.rollback() + } + dismiss() + } + } .onDisappear { if waypoint.id == 0 { // New, unsent waypoint created by the user: delete it From 8ef6d0cea68497425e5933a8951247bb915a2edd Mon Sep 17 00:00:00 2001 From: Benjamin Faershtein <119711889+RCGV1@users.noreply.github.com> Date: Thu, 5 Jun 2025 08:28:04 -0700 Subject: [PATCH 4/8] fix --- Meshtastic/Views/Nodes/Helpers/Map/WaypointForm.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Meshtastic/Views/Nodes/Helpers/Map/WaypointForm.swift b/Meshtastic/Views/Nodes/Helpers/Map/WaypointForm.swift index e813b854..caee990f 100644 --- a/Meshtastic/Views/Nodes/Helpers/Map/WaypointForm.swift +++ b/Meshtastic/Views/Nodes/Helpers/Map/WaypointForm.swift @@ -378,7 +378,7 @@ struct WaypointForm: View { } } } - .alert("Waypoiny Failed to Send", isPresented: $waypointFailedAlert) { + .alert("Waypoint Failed to Send", isPresented: $waypointFailedAlert) { Button("OK", role: .cancel) { bleManager.context.delete(waypoint) do { From 7e0c0b834791533d29c48cfb07b2b3159c85fe47 Mon Sep 17 00:00:00 2001 From: Benjamin Faershtein <119711889+RCGV1@users.noreply.github.com> Date: Thu, 5 Jun 2025 08:28:28 -0700 Subject: [PATCH 5/8] Fix waypoiny --- Localizable.xcstrings | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Localizable.xcstrings b/Localizable.xcstrings index e6a7809f..ffeaff93 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -34356,7 +34356,7 @@ } } }, - "Waypoiny Failed to Send" : { + "Waypoint Failed to Send" : { }, "Weather Conditions" : { @@ -35340,4 +35340,4 @@ } }, "version" : "1.0" -} \ No newline at end of file +} From e7055a2768285c6c0996760066a490a4f3076360 Mon Sep 17 00:00:00 2001 From: Benjamin Faershtein <119711889+RCGV1@users.noreply.github.com> Date: Thu, 5 Jun 2025 08:29:03 -0700 Subject: [PATCH 6/8] Update Meshtastic/AppIntents/SendWaypointIntent.swift Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Meshtastic/AppIntents/SendWaypointIntent.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Meshtastic/AppIntents/SendWaypointIntent.swift b/Meshtastic/AppIntents/SendWaypointIntent.swift index e41732db..fb0f97c3 100644 --- a/Meshtastic/AppIntents/SendWaypointIntent.swift +++ b/Meshtastic/AppIntents/SendWaypointIntent.swift @@ -89,7 +89,11 @@ struct SendWaypointIntent: AppIntent { } if isLocked { - newWaypoint.lockedTo = UInt32(BLEManager.shared.connectedPeripheral!.num) + if let connectedPeripheral = BLEManager.shared.connectedPeripheral { + newWaypoint.lockedTo = UInt32(connectedPeripheral.num) + } else { + throw AppIntentErrors.AppIntentError.notConnected + } } if !BLEManager.shared.sendWaypoint(waypoint: newWaypoint) { From a16c7ade27f97fac491659ac44b6d84f6c1a97bb Mon Sep 17 00:00:00 2001 From: Benjamin Faershtein <119711889+RCGV1@users.noreply.github.com> Date: Thu, 5 Jun 2025 08:30:15 -0700 Subject: [PATCH 7/8] Accessibility Label --- Meshtastic/Views/Nodes/Helpers/Map/WaypointForm.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Meshtastic/Views/Nodes/Helpers/Map/WaypointForm.swift b/Meshtastic/Views/Nodes/Helpers/Map/WaypointForm.swift index caee990f..61bc8b7a 100644 --- a/Meshtastic/Views/Nodes/Helpers/Map/WaypointForm.swift +++ b/Meshtastic/Views/Nodes/Helpers/Map/WaypointForm.swift @@ -56,6 +56,7 @@ struct WaypointForm: View { } label: { Image(systemName: "location") } + .accessibilityLabel("Set to current location") } HStack { if waypoint.coordinate.latitude != 0 && waypoint.coordinate.longitude != 0 { From 784d3ab7f48eb240754218141442b015813b764b Mon Sep 17 00:00:00 2001 From: Benjamin Faershtein <119711889+RCGV1@users.noreply.github.com> Date: Thu, 5 Jun 2025 09:23:51 -0700 Subject: [PATCH 8/8] Fixed lock waypoint logic --- Localizable.xcstrings | 11 +++++++---- Meshtastic/Views/Nodes/Helpers/Map/WaypointForm.swift | 4 ++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Localizable.xcstrings b/Localizable.xcstrings index ffeaff93..b5d0c59d 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -27888,6 +27888,9 @@ } } } + }, + "Set to current location" : { + }, "Sets the maximum number of hops, default is 3. Increasing hops also increases congestion and should be used carefully. O hop broadcast messages will not get ACKs." : { "localizations" : { @@ -34269,6 +34272,9 @@ } } } + }, + "Waypoint Failed to Send" : { + }, "Waypoint Options" : { "localizations" : { @@ -34355,9 +34361,6 @@ } } } - }, - "Waypoint Failed to Send" : { - }, "Weather Conditions" : { "localizations" : { @@ -35340,4 +35343,4 @@ } }, "version" : "1.0" -} +} \ No newline at end of file diff --git a/Meshtastic/Views/Nodes/Helpers/Map/WaypointForm.swift b/Meshtastic/Views/Nodes/Helpers/Map/WaypointForm.swift index 61bc8b7a..5279dfe2 100644 --- a/Meshtastic/Views/Nodes/Helpers/Map/WaypointForm.swift +++ b/Meshtastic/Views/Nodes/Helpers/Map/WaypointForm.swift @@ -267,8 +267,8 @@ struct WaypointForm: View { Text(waypoint.name ?? "?") .font(.largeTitle) Spacer() - if waypoint.locked > 0 { - Image(systemName: "lock.fill" ) + if waypoint.locked > 0 && waypoint.locked != UInt32(BLEManager.shared.connectedPeripheral?.num ?? 0) { + Image(systemName: "lock.fill") .font(.largeTitle) } else { Button {