mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Janky waypoint popovers
This commit is contained in:
parent
0b11f8ed7d
commit
a0e8caf6fb
4 changed files with 147 additions and 27 deletions
|
|
@ -68,6 +68,7 @@
|
|||
DD6193772862F90F00E59241 /* CannedMessagesConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6193762862F90F00E59241 /* CannedMessagesConfig.swift */; };
|
||||
DD6193792863875F00E59241 /* SerialConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6193782863875F00E59241 /* SerialConfig.swift */; };
|
||||
DD73FD1128750779000852D6 /* PositionLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD73FD1028750779000852D6 /* PositionLog.swift */; };
|
||||
DD760AAE2ABAC706002C022E /* WaypointPopover.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD760AAD2ABAC706002C022E /* WaypointPopover.swift */; };
|
||||
DD769E0328D18BF1001A3F05 /* DeviceMetricsLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD769E0228D18BF0001A3F05 /* DeviceMetricsLog.swift */; };
|
||||
DD77093B2AA1ABB8007A8BF0 /* BluetoothTips.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD77093A2AA1ABB8007A8BF0 /* BluetoothTips.swift */; };
|
||||
DD77093D2AA1AFA3007A8BF0 /* ChannelTips.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD77093C2AA1AFA3007A8BF0 /* ChannelTips.swift */; };
|
||||
|
|
@ -166,7 +167,6 @@
|
|||
DDDE5A1329AFEAB900490C6C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DDDE5A1229AFEAB900490C6C /* Assets.xcassets */; };
|
||||
DDDE5A1429AFEAB900490C6C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DDDE5A1229AFEAB900490C6C /* Assets.xcassets */; };
|
||||
DDE0F7C5295F77B700B8AAB3 /* AppSettingsEnums.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE0F7C4295F77B700B8AAB3 /* AppSettingsEnums.swift */; };
|
||||
DDE179302ABA2482005777A8 /* LocationDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE1792F2ABA2482005777A8 /* LocationDataManager.swift */; };
|
||||
DDF6B2482A9AEBF500BA6931 /* StoreForward.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF6B2472A9AEBF500BA6931 /* StoreForward.swift */; };
|
||||
DDF924CA26FBB953009FE055 /* ConnectedDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF924C926FBB953009FE055 /* ConnectedDevice.swift */; };
|
||||
DDFEB3BB29900C1200EE7472 /* CurrentConditionsCompact.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDFEB3BA29900C1200EE7472 /* CurrentConditionsCompact.swift */; };
|
||||
|
|
@ -277,6 +277,7 @@
|
|||
DD6193762862F90F00E59241 /* CannedMessagesConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CannedMessagesConfig.swift; sourceTree = "<group>"; };
|
||||
DD6193782863875F00E59241 /* SerialConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SerialConfig.swift; sourceTree = "<group>"; };
|
||||
DD73FD1028750779000852D6 /* PositionLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionLog.swift; sourceTree = "<group>"; };
|
||||
DD760AAD2ABAC706002C022E /* WaypointPopover.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaypointPopover.swift; sourceTree = "<group>"; };
|
||||
DD769E0228D18BF0001A3F05 /* DeviceMetricsLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceMetricsLog.swift; sourceTree = "<group>"; };
|
||||
DD77093A2AA1ABB8007A8BF0 /* BluetoothTips.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BluetoothTips.swift; sourceTree = "<group>"; };
|
||||
DD77093C2AA1AFA3007A8BF0 /* ChannelTips.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelTips.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -390,7 +391,6 @@
|
|||
DDDE5A1229AFEAB900490C6C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
DDDEE5E229DBE43E00A8E078 /* MeshtasticDataModelV11.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV11.xcdatamodel; sourceTree = "<group>"; };
|
||||
DDE0F7C4295F77B700B8AAB3 /* AppSettingsEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettingsEnums.swift; sourceTree = "<group>"; };
|
||||
DDE1792F2ABA2482005777A8 /* LocationDataManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationDataManager.swift; sourceTree = "<group>"; };
|
||||
DDEE03EC29544A1000FCAD57 /* MeshtasticDataModelV4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV4.xcdatamodel; sourceTree = "<group>"; };
|
||||
DDF6B2462A9AEB9E00BA6931 /* MeshtasticDataModelV17.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV17.xcdatamodel; sourceTree = "<group>"; };
|
||||
DDF6B2472A9AEBF500BA6931 /* StoreForward.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreForward.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -793,7 +793,6 @@
|
|||
DDA6B2E828419CF2003E8C16 /* MeshPackets.swift */,
|
||||
DD964FBC296E6B01007C176F /* EmojiOnlyTextField.swift */,
|
||||
DDDB443C29F6592F00EE2349 /* NetworkManager.swift */,
|
||||
DDE1792F2ABA2482005777A8 /* LocationDataManager.swift */,
|
||||
);
|
||||
path = Helpers;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -825,6 +824,7 @@
|
|||
DDDB26472AACD6D1003AFCB7 /* NodeMapControl.swift */,
|
||||
DDB6CCFA2AAF805100945AF6 /* NodeMapSwiftUI.swift */,
|
||||
DD13AA482AB73BF400BA0C98 /* PositionPopover.swift */,
|
||||
DD760AAD2ABAC706002C022E /* WaypointPopover.swift */,
|
||||
);
|
||||
path = Helpers;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -1075,6 +1075,7 @@
|
|||
DDDB443629F6287000EE2349 /* MapButtons.swift in Sources */,
|
||||
DD5D0A9C2931B9F200F7EA61 /* EthernetModes.swift in Sources */,
|
||||
6DEDA55A2A957B8E00321D2E /* DetectionSensorLog.swift in Sources */,
|
||||
DD760AAE2ABAC706002C022E /* WaypointPopover.swift in Sources */,
|
||||
DD5E5203298EE33B00D21B61 /* config.pb.swift in Sources */,
|
||||
DD798B072915928D005217CD /* ChannelMessageList.swift in Sources */,
|
||||
DDA6B2EB28420A7B003E8C16 /* NodeAnnotation.swift in Sources */,
|
||||
|
|
@ -1183,7 +1184,6 @@
|
|||
DD5E520E298EE33B00D21B61 /* mqtt.pb.swift in Sources */,
|
||||
DD994B69295F88B60013760A /* IntervalEnums.swift in Sources */,
|
||||
DD415828285859C4009B0E59 /* TelemetryConfig.swift in Sources */,
|
||||
DDE179302ABA2482005777A8 /* LocationDataManager.swift in Sources */,
|
||||
DDDB443D29F6592F00EE2349 /* NetworkManager.swift in Sources */,
|
||||
DDB6CCFB2AAF805100945AF6 /* NodeMapSwiftUI.swift in Sources */,
|
||||
DD73FD1128750779000852D6 /* PositionLog.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -841,22 +841,9 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
if fromNodeNum <= 0 || LocationHelper.currentLocation.distance(from: LocationHelper.DefaultLocation) == 0.0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// if smartPosition {
|
||||
// if lastPosition != nil {
|
||||
// let connectedNode = getNodeInfo(id: connectedPeripheral?.num ?? 0, context: context!)
|
||||
// if connectedNode?.positionConfig?.smartPositionEnabled ?? false {
|
||||
// if lastPosition!.distance(from: LocationHelper.currentLocation) < Double(connectedNode?.positionConfig?.broadcastSmartMinimumDistance ?? 50) {
|
||||
// return false
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// lastPosition = LocationHelper.currentLocation
|
||||
// var locationHelper = LocationHelper()
|
||||
var positionPacket = Position()
|
||||
positionPacket.latitudeI = Int32((locationFetcher.lastKnownLocation?.latitude ?? 0) * 1e7)
|
||||
positionPacket.longitudeI = Int32((locationFetcher.manager.location?.coordinate.longitude ?? 0) * 1e7)
|
||||
positionPacket.latitudeI = Int32(LocationHelper.currentLocation.latitude * 1e7)
|
||||
positionPacket.longitudeI = Int32(LocationHelper.currentLocation.longitude * 1e7)
|
||||
positionPacket.time = UInt32(LocationHelper.currentTimestamp.timeIntervalSince1970)
|
||||
positionPacket.timestamp = UInt32(LocationHelper.currentTimestamp.timeIntervalSince1970)
|
||||
positionPacket.altitude = Int32(LocationHelper.currentAltitude)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import MapKit
|
|||
import WeatherKit
|
||||
|
||||
@available(iOS 17.0, macOS 14.0, *)
|
||||
|
||||
struct NodeMapSwiftUI: View {
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
|
@ -19,7 +18,7 @@ struct NodeMapSwiftUI: View {
|
|||
@ObservedObject var node: NodeInfoEntity
|
||||
@State var showUserLocation: Bool = false
|
||||
@State var positions: [PositionEntity] = []
|
||||
@State var waypoints: [WaypointEntity] = []
|
||||
//@State var waypoints: [WaypointEntity] = []
|
||||
/// Map State User Defaults
|
||||
@AppStorage("meshMapShowNodeHistory") private var showNodeHistory = false
|
||||
@AppStorage("meshMapShowRouteLines") private var showRouteLines = false
|
||||
|
|
@ -35,7 +34,18 @@ struct NodeMapSwiftUI: View {
|
|||
@State private var isLookingAround = false
|
||||
@State private var isEditingSettings = false
|
||||
@State private var selected: PositionEntity?
|
||||
@State private var selectedWaypoint: WaypointEntity?
|
||||
@State private var selectedWaypointRect: CGRect = .zero
|
||||
@State private var selectedWaypointPoint: CGPoint = .zero
|
||||
@State private var showingPositionPopover = false
|
||||
@State private var showingWaypointPopover = false
|
||||
|
||||
@FetchRequest(sortDescriptors: [NSSortDescriptor(key: "name", ascending: false)],
|
||||
predicate: NSPredicate(
|
||||
format: "expire == nil || expire >= %@", Date() as NSDate
|
||||
), animation: .none)
|
||||
private var waypoints: FetchedResults<WaypointEntity>
|
||||
@State var waypoiintSelectionRect: CGRect = .zero
|
||||
|
||||
var body: some View {
|
||||
let positionArray = node.positions?.array as? [PositionEntity] ?? []
|
||||
|
|
@ -44,6 +54,7 @@ struct NodeMapSwiftUI: View {
|
|||
return position.nodeCoordinate ?? LocationHelper.DefaultLocation
|
||||
})
|
||||
|
||||
|
||||
if node.hasPositions {
|
||||
ZStack {
|
||||
Map(position: $position, bounds: MapCameraBounds(minimumDistance: 1, maximumDistance: .infinity), scope: mapScope) {
|
||||
|
|
@ -71,13 +82,29 @@ struct NodeMapSwiftUI: View {
|
|||
.stroke(Color(nodeColor.darker()), lineWidth: 5)
|
||||
.foregroundStyle(Color(nodeColor).opacity(0.4))
|
||||
}
|
||||
/// Waypoint Annotations
|
||||
ForEach(Array(waypoints), id: \.id) { waypoint in
|
||||
Annotation(waypoint.name ?? "?", coordinate: waypoint.coordinate) {
|
||||
ZStack {
|
||||
CircleText(text: String(UnicodeScalar(Int(waypoint.icon)) ?? "📍"), color: Color.orange, circleSize: 35)
|
||||
.onTapGesture(coordinateSpace: .global) { location in
|
||||
print("Tapped at \(location)")
|
||||
let size = CGSize(width: 1, height: 1)
|
||||
let rect = CGRect(origin: location, size: size)
|
||||
selectedWaypointRect = rect
|
||||
selectedWaypointPoint = location
|
||||
showingWaypointPopover = true
|
||||
selectedWaypoint = (selectedWaypoint == waypoint ? nil : waypoint)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Node Annotations
|
||||
ForEach(positionArray.reversed(), id: \.id) { position in
|
||||
ForEach(positionArray, id: \.id) { position in
|
||||
let pf = PositionFlags(rawValue: Int(position.nodePosition?.metadata?.positionFlags ?? 3))
|
||||
let formatter = MeasurementFormatter()
|
||||
let speedText = formatter.string(from: Measurement(value: Double(position.speed), unit: UnitSpeed.kilometersPerHour))
|
||||
let headingDegrees = Angle.degrees(Double(position.heading))
|
||||
Annotation(position.latest ? node.user?.shortName ?? "?" : (pf.contains(.Speed) && position.speed > 2) ? speedText : "", coordinate: position.coordinate) {
|
||||
Annotation(position.latest ? node.user?.shortName ?? "?": "", coordinate: position.coordinate) {
|
||||
ZStack {
|
||||
if position.latest {
|
||||
Circle()
|
||||
|
|
@ -95,7 +122,7 @@ struct NodeMapSwiftUI: View {
|
|||
showingPositionPopover = true
|
||||
selected = (selected == position ? nil : position) // <-- here
|
||||
}
|
||||
.popover(isPresented: $showingPositionPopover, arrowEdge: .bottom) {
|
||||
.popover(isPresented: $showingPositionPopover) {
|
||||
PositionPopover(position: position)
|
||||
.padding()
|
||||
.opacity(0.8)
|
||||
|
|
@ -114,6 +141,7 @@ struct NodeMapSwiftUI: View {
|
|||
}
|
||||
.popover(isPresented: $showingPositionPopover, arrowEdge: .bottom) {
|
||||
PositionPopover(position: position)
|
||||
.tag(position.id)
|
||||
.padding()
|
||||
.opacity(0.8)
|
||||
.presentationCompactAdaptation(.popover)
|
||||
|
|
@ -167,6 +195,13 @@ struct NodeMapSwiftUI: View {
|
|||
.padding(.horizontal, 20)
|
||||
}
|
||||
}
|
||||
.popover(item: $selectedWaypoint, attachmentAnchor: .rect(.rect(selectedWaypointRect)), arrowEdge: .bottom) { selection in
|
||||
//.popover(isPresented: $showingWaypointPopover, arrowEdge: .bottom) {
|
||||
WaypointPopover(waypoint: selection)
|
||||
.padding()
|
||||
.opacity(0.8)
|
||||
.presentationCompactAdaptation(.popover)
|
||||
}
|
||||
.sheet(isPresented: $isEditingSettings) {
|
||||
VStack {
|
||||
Form {
|
||||
|
|
@ -250,8 +285,8 @@ struct NodeMapSwiftUI: View {
|
|||
.padding()
|
||||
#endif
|
||||
}
|
||||
//.presentationDetents([.fraction(0.5)])
|
||||
.presentationDetents([.medium])
|
||||
.presentationDetents([.fraction(0.46)])
|
||||
//.presentationDetents([.medium])
|
||||
.presentationDragIndicator(.visible)
|
||||
}
|
||||
.onChange(of: node) {
|
||||
|
|
|
|||
98
Meshtastic/Views/Nodes/Helpers/WaypointPopover.swift
Normal file
98
Meshtastic/Views/Nodes/Helpers/WaypointPopover.swift
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
//
|
||||
// WaypointPopover.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Copyright(c) Garth Vander Houwen on 9/19/23.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import MapKit
|
||||
|
||||
struct WaypointPopover: View {
|
||||
var waypoint: WaypointEntity
|
||||
let distanceFormatter = MKDistanceFormatter()
|
||||
var body: some View {
|
||||
VStack {
|
||||
HStack {
|
||||
CircleText(text: String(UnicodeScalar(Int(waypoint.icon)) ?? "📍"), color: Color.blue)
|
||||
Text(waypoint.name ?? "?")
|
||||
.font(.title3)
|
||||
if waypoint.locked > 0 {
|
||||
Image(systemName: "lock.fill" )
|
||||
.font(.title2)
|
||||
} else {
|
||||
// Edit Button
|
||||
}
|
||||
}
|
||||
Divider()
|
||||
VStack (alignment: .leading) {
|
||||
// Description
|
||||
if (waypoint.longDescription ?? "").count > 0 {
|
||||
Label {
|
||||
Text(waypoint.longDescription ?? "")
|
||||
.foregroundColor(.primary)
|
||||
.font(.footnote)
|
||||
.multilineTextAlignment(.leading)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
} icon: {
|
||||
Image(systemName: "doc.plaintext")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
.frame(width: 35)
|
||||
}
|
||||
.padding(.bottom, 5)
|
||||
}
|
||||
/// Created
|
||||
Label {
|
||||
Text("Created: \(waypoint.created?.formatted() ?? "?")")
|
||||
.foregroundColor(.primary)
|
||||
.font(.footnote)
|
||||
} icon: {
|
||||
Image(systemName: "clock.badge.checkmark")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
.frame(width: 35)
|
||||
}
|
||||
.padding(.bottom, 5)
|
||||
/// Updated
|
||||
if waypoint.lastUpdated != nil {
|
||||
Label {
|
||||
Text("Updated: \(waypoint.lastUpdated?.formatted() ?? "?")")
|
||||
.foregroundColor(.primary)
|
||||
.font(.footnote)
|
||||
} icon: {
|
||||
Image(systemName: "clock.arrow.circlepath")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
.frame(width: 35)
|
||||
}
|
||||
.padding(.bottom, 5)
|
||||
}
|
||||
/// Updated
|
||||
if waypoint.expire != nil {
|
||||
Label {
|
||||
Text("Expires: \(waypoint.expire?.formatted() ?? "?")")
|
||||
.foregroundColor(.primary)
|
||||
.font(.footnote)
|
||||
} icon: {
|
||||
Image(systemName: "clock.badge.xmark")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
.frame(width: 35)
|
||||
}
|
||||
.padding(.bottom, 5)
|
||||
}
|
||||
/// Distance
|
||||
if LocationHelper.currentLocation.distance(from: LocationHelper.DefaultLocation) > 0.0 {
|
||||
let metersAway = waypoint.coordinate.distance(from: LocationHelper.currentLocation)
|
||||
Label {
|
||||
Text("distance".localized + ": \(distanceFormatter.string(fromDistance: Double(metersAway)))")
|
||||
.foregroundColor(.primary)
|
||||
.font(.footnote)
|
||||
} icon: {
|
||||
Image(systemName: "lines.measurement.horizontal")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
.frame(width: 35)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.tag(waypoint.id)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue