From 80afbcacd3cebb0433fc4ff40f8be9df171442a1 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 24 Sep 2023 18:44:04 -0700 Subject: [PATCH] Log packets being sent to and coming back from the phone --- Meshtastic.xcodeproj/project.pbxproj | 4 +- Meshtastic/Helpers/BLEManager.swift | 4 +- .../Meshtastic.xcdatamodeld/.xccurrentversion | 2 +- .../contents | 4 +- .../contents | 366 +++++++++++++ Meshtastic/Persistence/UpdateCoreData.swift | 2 +- .../Views/Nodes/Helpers/NodeMapSwiftUI.swift | 513 +++++++++--------- 7 files changed, 634 insertions(+), 261 deletions(-) create mode 100644 Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV19.xcdatamodel/contents diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 968271ec..b1d8fa0a 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -230,6 +230,7 @@ DD2553562855B02500E55709 /* LoRaConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoRaConfig.swift; sourceTree = ""; }; DD2553582855B52700E55709 /* PositionConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionConfig.swift; sourceTree = ""; }; DD2AD8A7296D2DF9001FF0E7 /* MapViewSwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapViewSwiftUI.swift; sourceTree = ""; }; + DD2CC2E52ABFE04E00EDFDA7 /* MeshtasticDataModelV19.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV19.xcdatamodel; sourceTree = ""; }; DD2DC2BF29BCD8AB003B383C /* HardwareModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HardwareModels.swift; sourceTree = ""; }; DD2E65252767A01F00E45FC5 /* NodeDetailOld.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeDetailOld.swift; sourceTree = ""; }; DD3501882852FC3B000FC853 /* Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = ""; }; @@ -1718,6 +1719,7 @@ DD3CC6BA28E366DF00FA9159 /* Meshtastic.xcdatamodeld */ = { isa = XCVersionGroup; children = ( + DD2CC2E52ABFE04E00EDFDA7 /* MeshtasticDataModelV19.xcdatamodel */, DDDB26492AAD743E003AFCB7 /* MeshtasticDataModelV18.xcdatamodel */, DDF6B2462A9AEB9E00BA6931 /* MeshtasticDataModelV17.xcdatamodel */, DDC4CA012A8DAA3800CE201C /* MeshtasticDataModelV16.xcdatamodel */, @@ -1737,7 +1739,7 @@ DD5D0A9A2931AD6B00F7EA61 /* MeshtasticDataModelV2.xcdatamodel */, DD3CC6BB28E366DF00FA9159 /* MeshtasticDataModel.xcdatamodel */, ); - currentVersion = DDDB26492AAD743E003AFCB7 /* MeshtasticDataModelV18.xcdatamodel */; + currentVersion = DD2CC2E52ABFE04E00EDFDA7 /* MeshtasticDataModelV19.xcdatamodel */; name = Meshtastic.xcdatamodeld; path = Meshtastic/Meshtastic.xcdatamodeld; sourceTree = ""; diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index cc309864..6d99e61a 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -30,6 +30,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate @Published var mqttProxyConnected: Bool = false @StateObject var appState = AppState.shared + //public var locationHelper = LocationHelper.shared public var minimumVersion = "2.0.0" public var connectedVersion: String public var isConnecting: Bool = false @@ -42,7 +43,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate var lastPosition: CLLocationCoordinate2D? let emptyNodeNum: UInt32 = 4294967295 let mqttManager = MqttClientProxyManager.shared - let locationHelper = LocationHelper.shared + //var locationHelper = LocationHelper.shared var wantRangeTestPackets = false /* Meshtastic Service Details */ var TORADIO_characteristic: CBCharacteristic! @@ -872,6 +873,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse) success = true let logString = String.localizedStringWithFormat("mesh.log.sharelocation %@".localized, String(fromNodeNum)) + print(positionPacket) MeshLogger.log("📍 \(logString)") } return success diff --git a/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion b/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion index 348d9e99..074f016d 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion +++ b/Meshtastic/Meshtastic.xcdatamodeld/.xccurrentversion @@ -3,6 +3,6 @@ _XCCurrentVersionName - MeshtasticDataModelV18.xcdatamodel + MeshtasticDataModelV19.xcdatamodel diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV18.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV18.xcdatamodel/contents index c30e2f4b..ef4d9a8d 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV18.xcdatamodel/contents +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV18.xcdatamodel/contents @@ -1,5 +1,5 @@ - + @@ -345,7 +345,7 @@ - + diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV19.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV19.xcdatamodel/contents new file mode 100644 index 00000000..72bc0d71 --- /dev/null +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV19.xcdatamodel/contents @@ -0,0 +1,366 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Meshtastic/Persistence/UpdateCoreData.swift b/Meshtastic/Persistence/UpdateCoreData.swift index 17310c61..16d253a7 100644 --- a/Meshtastic/Persistence/UpdateCoreData.swift +++ b/Meshtastic/Persistence/UpdateCoreData.swift @@ -218,7 +218,7 @@ func upsertPositionPacket (packet: MeshPacket, context: NSManagedObjectContext) position.latest = false } } - + print("Incoming position message: \n \(positionMessage)") let position = PositionEntity(context: context) position.latest = true position.snr = packet.rxSnr diff --git a/Meshtastic/Views/Nodes/Helpers/NodeMapSwiftUI.swift b/Meshtastic/Views/Nodes/Helpers/NodeMapSwiftUI.swift index ca3ea5eb..ade7d39a 100644 --- a/Meshtastic/Views/Nodes/Helpers/NodeMapSwiftUI.swift +++ b/Meshtastic/Views/Nodes/Helpers/NodeMapSwiftUI.swift @@ -48,232 +48,235 @@ struct NodeMapSwiftUI: View { @State var waypoiintSelectionRect: CGRect = .zero var body: some View { + let positionArray = node.positions?.array as? [PositionEntity] ?? [] let mostRecent = node.positions?.lastObject as? PositionEntity let lineCoords = positionArray.compactMap({(position) -> CLLocationCoordinate2D in return position.nodeCoordinate ?? LocationHelper.DefaultLocation }) - if node.hasPositions { ZStack { - Map(position: $position, bounds: MapCameraBounds(minimumDistance: 1, maximumDistance: .infinity), scope: mapScope) { - /// Node Color from node.num - let nodeColor = UIColor(hex: UInt32(node.num)) - /// Route Lines - if showRouteLines { - if showRouteLines { - let gradient = LinearGradient( - colors: [Color(nodeColor.lighter().lighter().lighter()), Color(nodeColor.lighter()), Color(nodeColor)], - startPoint: .leading, endPoint: .trailing - ) - let dashed = StrokeStyle( - lineWidth: 5, - lineCap: .round, lineJoin: .round, dash: [10, 10] - ) - MapPolyline(coordinates: lineCoords) - .stroke(gradient, style: dashed) + MapReader { reader in + Map(position: $position, bounds: MapCameraBounds(minimumDistance: 1, maximumDistance: .infinity), scope: mapScope) { + /// Node Color from node.num + let nodeColor = UIColor(hex: UInt32(node.num)) + /// Route Lines + if showRouteLines { + if showRouteLines { + let gradient = LinearGradient( + colors: [Color(nodeColor.lighter().lighter().lighter()), Color(nodeColor.lighter()), Color(nodeColor)], + startPoint: .leading, endPoint: .trailing + ) + let dashed = StrokeStyle( + lineWidth: 5, + lineCap: .round, lineJoin: .round, dash: [10, 10] + ) + MapPolyline(coordinates: lineCoords) + .stroke(gradient, style: dashed) + } } - } - /// Convex Hull - if showConvexHull { - let hull = lineCoords.getConvexHull() - MapPolygon(coordinates: hull) - .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) + /// Convex Hull + if showConvexHull { + let hull = lineCoords.getConvexHull() + MapPolygon(coordinates: hull) + .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 pinLocation = reader.convert(location, from: .local) + print(pinLocation) + let size = CGSize(width: 1, height: 50) + let rect = CGRect(origin: location, size: size) + selectedWaypointRect = rect + selectedWaypointPoint = location + showingWaypointPopover = true + selectedWaypoint = (selectedWaypoint == waypoint ? nil : waypoint) + } } } } - } - /// Node Annotations - ForEach(positionArray, id: \.id) { position in - let pf = PositionFlags(rawValue: Int(position.nodePosition?.metadata?.positionFlags ?? 3)) - let formatter = MeasurementFormatter() - let headingDegrees = Angle.degrees(Double(position.heading)) - Annotation(position.latest ? node.user?.shortName ?? "?": "", coordinate: position.coordinate) { - ZStack { - if position.latest { - Circle() - .foregroundStyle(Color(nodeColor.lighter()).opacity(0.4)) - .frame(width: 60, height: 60) - if pf.contains(.Heading) { - Image(systemName: pf.contains(.Speed) && position.speed > 1 ? "location.north" : "hexagon") - .symbolEffect(.pulse.byLayer) - .padding(5) - .foregroundStyle(Color(nodeColor).isLight() ? .black : .white) - .background(Color(UIColor(hex: UInt32(node.num)).darker())) - .clipShape(Circle()) - .rotationEffect(headingDegrees) - .onTapGesture { - showingPositionPopover = true - selected = (selected == position ? nil : position) // <-- here - } - .popover(isPresented: $showingPositionPopover) { - PositionPopover(position: position) - .padding() - .opacity(0.8) - .presentationCompactAdaptation(.popover) - } - } else { - Image(systemName: "flipphone") - .symbolEffect(.pulse.byLayer) - .padding(5) - .foregroundStyle(Color(nodeColor).isLight() ? .black : .white) - .background(Color(UIColor(hex: UInt32(node.num)).darker())) - .clipShape(Circle()) - .onTapGesture { - showingPositionPopover = true - selected = (selected == position ? nil : position) // <-- here - } - .popover(isPresented: $showingPositionPopover, arrowEdge: .bottom) { - PositionPopover(position: position) - .tag(position.id) - .padding() - .opacity(0.8) - .presentationCompactAdaptation(.popover) - } - } - } else { - if showNodeHistory { + /// Node Annotations + ForEach(positionArray, id: \.id) { position in + let pf = PositionFlags(rawValue: Int(position.nodePosition?.metadata?.positionFlags ?? 3)) + let formatter = MeasurementFormatter() + let headingDegrees = Angle.degrees(Double(position.heading)) + Annotation(position.latest ? node.user?.shortName ?? "?": "", coordinate: position.coordinate) { + ZStack { + if position.latest { + Circle() + .foregroundStyle(Color(nodeColor.lighter()).opacity(0.4)) + .frame(width: 60, height: 60) if pf.contains(.Heading) { - Image(systemName: pf.contains(.Speed) && position.speed > 1 ? "location.north.circle" : "hexagon") - .padding(2) - .foregroundStyle(Color(UIColor(hex: UInt32(node.num)).lighter()).isLight() ? .black : .white) - .background(Color(UIColor(hex: UInt32(node.num)).lighter())) + Image(systemName: pf.contains(.Speed) && position.speed > 1 ? "location.north" : "hexagon") + .symbolEffect(.pulse.byLayer) + .padding(5) + .foregroundStyle(Color(nodeColor).isLight() ? .black : .white) + .background(Color(UIColor(hex: UInt32(node.num)).darker())) .clipShape(Circle()) .rotationEffect(headingDegrees) + .onTapGesture { + showingPositionPopover = true + selected = (selected == position ? nil : position) // <-- here + } + .popover(isPresented: $showingPositionPopover) { + PositionPopover(position: position) + .padding() + .opacity(0.8) + .presentationCompactAdaptation(.popover) + } } else { - Image(systemName: "mappin.circle") - .padding(2) - .foregroundStyle(Color(UIColor(hex: UInt32(node.num)).lighter()).isLight() ? .black : .white) - .background(Color(UIColor(hex: UInt32(node.num)).lighter())) + Image(systemName: "flipphone") + .symbolEffect(.pulse.byLayer) + .padding(5) + .foregroundStyle(Color(nodeColor).isLight() ? .black : .white) + .background(Color(UIColor(hex: UInt32(node.num)).darker())) .clipShape(Circle()) + .onTapGesture { + showingPositionPopover = true + selected = (selected == position ? nil : position) // <-- here + } + .popover(isPresented: $showingPositionPopover, arrowEdge: .bottom) { + PositionPopover(position: position) + .tag(position.id) + .padding() + .opacity(0.8) + .presentationCompactAdaptation(.popover) + } + } + } else { + if showNodeHistory { + if pf.contains(.Heading) { + Image(systemName: pf.contains(.Speed) && position.speed > 1 ? "location.north.circle" : "hexagon") + .padding(2) + .foregroundStyle(Color(UIColor(hex: UInt32(node.num)).lighter()).isLight() ? .black : .white) + .background(Color(UIColor(hex: UInt32(node.num)).lighter())) + .clipShape(Circle()) + .rotationEffect(headingDegrees) + } else { + Image(systemName: "mappin.circle") + .padding(2) + .foregroundStyle(Color(UIColor(hex: UInt32(node.num)).lighter()).isLight() ? .black : .white) + .background(Color(UIColor(hex: UInt32(node.num)).lighter())) + .clipShape(Circle()) + } } } } } + .tag(position.time) + .annotationTitles(.automatic) + .annotationSubtitles(.automatic) } - .tag(position.time) - .annotationTitles(.automatic) - .annotationSubtitles(.automatic) } - } - .mapScope(mapScope) - .mapStyle(mapStyle) - .mapControls { - MapScaleView(scope: mapScope) - .mapControlVisibility(.visible) - if showUserLocation { - MapUserLocationButton(scope: mapScope) + .mapScope(mapScope) + .mapStyle(mapStyle) + .mapControls { + MapScaleView(scope: mapScope) + .mapControlVisibility(.visible) + if showUserLocation { + MapUserLocationButton(scope: mapScope) + .mapControlVisibility(.visible) + } + MapPitchToggle(scope: mapScope) + .mapControlVisibility(.visible) + MapCompass(scope: mapScope) .mapControlVisibility(.visible) } - MapPitchToggle(scope: mapScope) - .mapControlVisibility(.visible) - MapCompass(scope: mapScope) - .mapControlVisibility(.visible) - } - .controlSize(.regular) - .overlay(alignment: .bottom) { - if scene != nil && isLookingAround { - LookAroundPreview(initialScene: scene) - .frame(height: UIDevice.current.userInterfaceIdiom == .phone ? 250 : 400) - .clipShape(RoundedRectangle(cornerRadius: 12)) - .padding(.horizontal, 20) + .controlSize(.regular) + .overlay(alignment: .bottom) { + if scene != nil && isLookingAround { + LookAroundPreview(initialScene: scene) + .frame(height: UIDevice.current.userInterfaceIdiom == .phone ? 250 : 400) + .clipShape(RoundedRectangle(cornerRadius: 12)) + .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 { - Section(header: Text("Map Options")) { - Picker(selection: $selectedMapLayer, label: Text("")) { - ForEach(MapLayer.allCases, id: \.self) { layer in - if layer != MapLayer.offline { - Text(layer.localized) + .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 { + Section(header: Text("Map Options")) { + Picker(selection: $selectedMapLayer, label: Text("")) { + ForEach(MapLayer.allCases, id: \.self) { layer in + if layer != MapLayer.offline { + Text(layer.localized) + } } } - } - .pickerStyle(SegmentedPickerStyle()) - .onChange(of: (selectedMapLayer)) { newMapLayer in - switch selectedMapLayer { - case .standard: - UserDefaults.mapLayer = newMapLayer - mapStyle = MapStyle.standard(elevation: .realistic, pointsOfInterest: showPointsOfInterest ? .all : .excludingAll, showsTraffic: showTraffic) - case .hybrid: - UserDefaults.mapLayer = newMapLayer - mapStyle = MapStyle.hybrid(elevation: .realistic, pointsOfInterest: showPointsOfInterest ? .all : .excludingAll, showsTraffic: showTraffic) - case .satellite: - UserDefaults.mapLayer = newMapLayer - mapStyle = MapStyle.imagery(elevation: .realistic) - case .offline: - return + .pickerStyle(SegmentedPickerStyle()) + .onChange(of: (selectedMapLayer)) { newMapLayer in + switch selectedMapLayer { + case .standard: + UserDefaults.mapLayer = newMapLayer + mapStyle = MapStyle.standard(elevation: .realistic, pointsOfInterest: showPointsOfInterest ? .all : .excludingAll, showsTraffic: showTraffic) + case .hybrid: + UserDefaults.mapLayer = newMapLayer + mapStyle = MapStyle.hybrid(elevation: .realistic, pointsOfInterest: showPointsOfInterest ? .all : .excludingAll, showsTraffic: showTraffic) + case .satellite: + UserDefaults.mapLayer = newMapLayer + mapStyle = MapStyle.imagery(elevation: .realistic) + case .offline: + return + } + } + .padding(.top, 5) + .padding(.bottom, 5) + Toggle(isOn: $showNodeHistory) { + Label("Node History", systemImage: "building.columns.fill") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + .onTapGesture { + self.showNodeHistory.toggle() + UserDefaults.enableMapNodeHistoryPins = self.showNodeHistory + } + Toggle(isOn: $showRouteLines) { + Label("Route Lines", systemImage: "road.lanes") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + .onTapGesture { + self.showRouteLines.toggle() + UserDefaults.enableMapRouteLines = self.showRouteLines + } + Toggle(isOn: $showConvexHull) { + Label("Convex Hull", systemImage: "button.angledbottom.horizontal.right") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + .onTapGesture { + self.showConvexHull.toggle() + UserDefaults.enableMapConvexHull = self.showConvexHull + } + Toggle(isOn: $showTraffic) { + Label("Traffic", systemImage: "car") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + .onTapGesture { + self.showTraffic.toggle() + UserDefaults.enableMapTraffic = self.showTraffic + } + Toggle(isOn: $showPointsOfInterest) { + Label("Points of Interest", systemImage: "mappin.and.ellipse") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + .onTapGesture { + self.showPointsOfInterest.toggle() + UserDefaults.enableMapPointsOfInterest = self.showPointsOfInterest } } - .padding(.top, 5) - .padding(.bottom, 5) - Toggle(isOn: $showNodeHistory) { - Label("Node History", systemImage: "building.columns.fill") - } - .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - .onTapGesture { - self.showNodeHistory.toggle() - UserDefaults.enableMapNodeHistoryPins = self.showNodeHistory - } - Toggle(isOn: $showRouteLines) { - Label("Route Lines", systemImage: "road.lanes") - } - .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - .onTapGesture { - self.showRouteLines.toggle() - UserDefaults.enableMapRouteLines = self.showRouteLines - } - Toggle(isOn: $showConvexHull) { - Label("Convex Hull", systemImage: "button.angledbottom.horizontal.right") - } - .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - .onTapGesture { - self.showConvexHull.toggle() - UserDefaults.enableMapConvexHull = self.showConvexHull - } - Toggle(isOn: $showTraffic) { - Label("Traffic", systemImage: "car") - } - .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - .onTapGesture { - self.showTraffic.toggle() - UserDefaults.enableMapTraffic = self.showTraffic - } - Toggle(isOn: $showPointsOfInterest) { - Label("Points of Interest", systemImage: "mappin.and.ellipse") - } - .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - .onTapGesture { - self.showPointsOfInterest.toggle() - UserDefaults.enableMapPointsOfInterest = self.showPointsOfInterest - } } - } - #if targetEnvironment(macCatalyst) +#if targetEnvironment(macCatalyst) Button { isEditingSettings = false } label: { @@ -283,84 +286,84 @@ struct NodeMapSwiftUI: View { .buttonBorderShape(.capsule) .controlSize(.large) .padding() - #endif - } - .presentationDetents([.fraction(0.46)]) - //.presentationDetents([.medium]) - .presentationDragIndicator(.visible) - } - .onChange(of: node) { - let mostRecent = node.positions?.lastObject as? PositionEntity - position = MapCameraPosition.automatic//.camera(MapCamera(centerCoordinate: mostRecent!.coordinate, distance: 1500, heading: 0, pitch: 0)) - if let mostRecent { - Task { - scene = try? await fetchScene(for: mostRecent.coordinate) +#endif } + .presentationDetents([.fraction(0.46)]) + //.presentationDetents([.medium]) + .presentationDragIndicator(.visible) } - } - .onAppear { - UIApplication.shared.isIdleTimerDisabled = true - switch selectedMapLayer { - case .standard: - mapStyle = MapStyle.standard(elevation: .realistic, pointsOfInterest: showPointsOfInterest ? .all : .excludingAll, showsTraffic: showTraffic) - case .hybrid: - mapStyle = MapStyle.hybrid(elevation: .realistic, pointsOfInterest: showPointsOfInterest ? .all : .excludingAll, showsTraffic: showTraffic) - case .satellite: - mapStyle = MapStyle.imagery(elevation: .realistic) - case .offline: - mapStyle = MapStyle.hybrid(elevation: .realistic, pointsOfInterest: showPointsOfInterest ? .all : .excludingAll, showsTraffic: showTraffic) - } - if self.scene == nil { - Task { - scene = try? await fetchScene(for: mostRecent!.coordinate) - } - } - } - .safeAreaInset(edge: .bottom, alignment: UIDevice.current.userInterfaceIdiom == .phone ? .leading : .trailing) { - HStack { - Button(action: { - withAnimation { - isEditingSettings = !isEditingSettings + .onChange(of: node) { + let mostRecent = node.positions?.lastObject as? PositionEntity + position = MapCameraPosition.automatic//.camera(MapCamera(centerCoordinate: mostRecent!.coordinate, distance: 1500, heading: 0, pitch: 0)) + if let mostRecent { + Task { + scene = try? await fetchScene(for: mostRecent.coordinate) } - }) { - Image(systemName: isEditingSettings ? "info.circle.fill" : "info.circle") - .padding(.vertical, 5) } - .tint(Color(UIColor.secondarySystemBackground)) - .foregroundColor(.accentColor) - .buttonStyle(.borderedProminent) - /// Look Around Button - if self.scene != nil { + } + .onAppear { + UIApplication.shared.isIdleTimerDisabled = true + switch selectedMapLayer { + case .standard: + mapStyle = MapStyle.standard(elevation: .realistic, pointsOfInterest: showPointsOfInterest ? .all : .excludingAll, showsTraffic: showTraffic) + case .hybrid: + mapStyle = MapStyle.hybrid(elevation: .realistic, pointsOfInterest: showPointsOfInterest ? .all : .excludingAll, showsTraffic: showTraffic) + case .satellite: + mapStyle = MapStyle.imagery(elevation: .realistic) + case .offline: + mapStyle = MapStyle.hybrid(elevation: .realistic, pointsOfInterest: showPointsOfInterest ? .all : .excludingAll, showsTraffic: showTraffic) + } + if self.scene == nil { + Task { + scene = try? await fetchScene(for: mostRecent!.coordinate) + } + } + } + .safeAreaInset(edge: .bottom, alignment: UIDevice.current.userInterfaceIdiom == .phone ? .leading : .trailing) { + HStack { Button(action: { withAnimation { - isLookingAround = !isLookingAround + isEditingSettings = !isEditingSettings } }) { - Image(systemName: isLookingAround ? "binoculars.fill" : "binoculars") + Image(systemName: isEditingSettings ? "info.circle.fill" : "info.circle") .padding(.vertical, 5) } .tint(Color(UIColor.secondarySystemBackground)) .foregroundColor(.accentColor) .buttonStyle(.borderedProminent) - } - - #if targetEnvironment(macCatalyst) + /// Look Around Button + if self.scene != nil { + Button(action: { + withAnimation { + isLookingAround = !isLookingAround + } + }) { + Image(systemName: isLookingAround ? "binoculars.fill" : "binoculars") + .padding(.vertical, 5) + } + .tint(Color(UIColor.secondarySystemBackground)) + .foregroundColor(.accentColor) + .buttonStyle(.borderedProminent) + } + +#if targetEnvironment(macCatalyst) MapZoomStepper(scope: mapScope) .mapControlVisibility(.visible) MapPitchSlider(scope: mapScope) .mapControlVisibility(.visible) - #endif +#endif + } + .controlSize(.regular) + .padding(5) } - .controlSize(.regular) - .padding(5) - } - .onDisappear { - UIApplication.shared.isIdleTimerDisabled = false - } - } + .onDisappear { + UIApplication.shared.isIdleTimerDisabled = false + } + }} .navigationBarTitle(String((node.user?.shortName ?? "unknown".localized) + (" \(node.positions?.count ?? 0) points")), displayMode: .inline) .navigationBarItems(trailing: - ZStack { + ZStack { ConnectedDevice( bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, @@ -372,7 +375,7 @@ struct NodeMapSwiftUI: View { } private func fetchScene(for coordinate: CLLocationCoordinate2D) async throws -> MKLookAroundScene? { - let lookAroundScene = MKLookAroundSceneRequest(coordinate: coordinate) - return try await lookAroundScene.scene + let lookAroundScene = MKLookAroundSceneRequest(coordinate: coordinate) + return try await lookAroundScene.scene } }