Janky waypoint popovers

This commit is contained in:
Garth Vander Houwen 2023-09-20 22:26:21 -07:00
parent 0b11f8ed7d
commit a0e8caf6fb
4 changed files with 147 additions and 27 deletions

View file

@ -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 */,

View file

@ -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)

View file

@ -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) {

View 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)
}
}