mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
added intent and button
This commit is contained in:
parent
41a252649a
commit
2a68a36d84
4 changed files with 125 additions and 0 deletions
61
Meshtastic/AppIntents/NavigateToNodeIntent.swift
Normal file
61
Meshtastic/AppIntents/NavigateToNodeIntent.swift
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
//
|
||||
// NavigateToNodeIntent.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Created by Benjamin Faershtein on 2/8/25.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import AppIntents
|
||||
import CoreLocation
|
||||
import CoreData
|
||||
import UIKit
|
||||
|
||||
@available(iOS 16.4, *)
|
||||
struct NavigateToNodeIntent: ForegroundContinuableIntent {
|
||||
|
||||
static var title: LocalizedStringResource = "Navigate to Node Position"
|
||||
static var openAppWhenRun: Bool = false
|
||||
|
||||
@Parameter(title: "Node Number")
|
||||
var nodeNum: Int
|
||||
|
||||
@MainActor
|
||||
func perform() async throws -> some IntentResult & ProvidesDialog {
|
||||
if !BLEManager.shared.isConnected {
|
||||
throw AppIntentErrors.AppIntentError.notConnected
|
||||
}
|
||||
|
||||
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "NodeInfoEntity")
|
||||
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
|
||||
|
||||
do {
|
||||
guard let fetchedNode = try PersistenceController.shared.container.viewContext.fetch(fetchNodeInfoRequest) as? [NodeInfoEntity],
|
||||
fetchedNode.count == 1 else {
|
||||
throw $nodeNum.needsValueError("Could not find node")
|
||||
}
|
||||
|
||||
let nodeInfo = fetchedNode[0]
|
||||
if let latitude = nodeInfo.latestPosition?.coordinate.latitude,
|
||||
let longitude = nodeInfo.latestPosition?.coordinate.longitude {
|
||||
|
||||
let url = URL(string: "maps://?saddr=&daddr=\(latitude),\(longitude)")
|
||||
|
||||
if let mapURL = url, UIApplication.shared.canOpenURL(mapURL) {
|
||||
// Request to continue in foreground before opening the app
|
||||
try await requestToContinueInForeground()
|
||||
|
||||
// Open Apple Maps for navigation
|
||||
UIApplication.shared.open(mapURL, options: [:], completionHandler: nil)
|
||||
return .result(dialog: "Navigating to node location.")
|
||||
} else {
|
||||
throw AppIntentErrors.AppIntentError.message("Unable to open Apple Maps.")
|
||||
}
|
||||
} else {
|
||||
throw AppIntentErrors.AppIntentError.message("Node does not have a recorded position.")
|
||||
}
|
||||
} catch {
|
||||
throw AppIntentErrors.AppIntentError.message("Failed to fetch node data.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
//
|
||||
// NavigateToButton.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Created by Benjamin Faershtein on 2/8/25.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import CoreLocation
|
||||
import CoreData
|
||||
import OSLog
|
||||
|
||||
struct NavigateToButton: View {
|
||||
var node: NodeInfoEntity
|
||||
|
||||
var body: some View {
|
||||
Button {
|
||||
guard let userNum = node.user?.num else {
|
||||
Logger.services.error("NavigateToAction: Selected node does not exist")
|
||||
return
|
||||
}
|
||||
|
||||
Logger.services.info("Fetching NodeInfoEntity for userNum: \(userNum)")
|
||||
|
||||
let fetchRequest: NSFetchRequest<NodeInfoEntity> = NSFetchRequest(entityName: "NodeInfoEntity")
|
||||
fetchRequest.predicate = NSPredicate(format: "num == %lld", Int64(userNum))
|
||||
|
||||
do {
|
||||
let fetchedNodes = try PersistenceController.shared.container.viewContext.fetch(fetchRequest)
|
||||
|
||||
guard let nodeInfo = fetchedNodes.first else {
|
||||
Logger.services.error("NavigateToAction: Node with userNum \(userNum) not found in Core Data")
|
||||
return
|
||||
}
|
||||
|
||||
if let latitude = nodeInfo.latestPosition?.latitude,
|
||||
let longitude = nodeInfo.latestPosition?.longitude {
|
||||
if let url = URL(string: "maps://?saddr=&daddr=\(latitude),\(longitude)") {
|
||||
UIApplication.shared.open(url, options: [:], completionHandler: nil)
|
||||
} else {
|
||||
Logger.services.error("Failed to create URL for navigation")
|
||||
}
|
||||
} else {
|
||||
Logger.services.warning("NavigateToAction: Node \(userNum) has invalid or missing coordinates")
|
||||
}
|
||||
} catch {
|
||||
Logger.services.error("NavigateToAction: Failed to fetch node with userNum \(userNum): \(error.localizedDescription)")
|
||||
}
|
||||
} label: {
|
||||
Label {
|
||||
Text("Navigate to node")
|
||||
} icon: {
|
||||
Image(systemName: "map")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -378,6 +378,9 @@ struct NodeDetail: View {
|
|||
node: node
|
||||
)
|
||||
}
|
||||
if node.hasPositions {
|
||||
NavigateToButton(node: node)
|
||||
}
|
||||
IgnoreNodeButton(
|
||||
bleManager: bleManager,
|
||||
context: context,
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ struct PositionLog: View {
|
|||
let degrees = Angle.degrees(Double(position.heading))
|
||||
let heading = Measurement(value: degrees.degrees, unit: UnitAngle.degrees).reciprocal()
|
||||
Text(heading.formatted(.measurement(width: .narrow, numberFormatStyle: .number.precision(.fractionLength(0)))))
|
||||
.textSelection(.enabled)
|
||||
}
|
||||
TableColumn("SNR") { position in
|
||||
Text("\(String(format: "%.2f", position.snr)) dB")
|
||||
|
|
@ -63,6 +64,8 @@ struct PositionLog: View {
|
|||
}
|
||||
.width(min: 180)
|
||||
}
|
||||
.textSelection(.enabled)
|
||||
|
||||
|
||||
} else {
|
||||
ScrollView {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue