From 7eb6659c0c81d79e39374f78e9c55f87419f4a9e Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Mon, 25 Mar 2024 15:21:38 -0700 Subject: [PATCH] Move fetch requests to mapcontent --- Meshtastic/Enums/AppSettingsEnums.swift | 2 +- .../CoreData/WaypointEntityExtension.swift | 4 +- Meshtastic/Helpers/MeshPackets.swift | 6 +-- .../Map/MapContent/MeshMapContent.swift | 26 +++++++---- .../Map/MapContent/NodeMapContent.swift | 2 - .../Nodes/Helpers/Map/MapSettingsForm.swift | 21 +++------ .../Nodes/Helpers/Map/NodeMapSwiftUI.swift | 21 --------- Meshtastic/Views/Nodes/MeshMap.swift | 46 ++++++++----------- Meshtastic/Views/Nodes/NodeList.swift | 21 ++++++++- .../Settings/Config/PositionConfig.swift | 21 +++++++-- Meshtastic/Views/Settings/Firmware.swift | 2 +- 11 files changed, 88 insertions(+), 84 deletions(-) diff --git a/Meshtastic/Enums/AppSettingsEnums.swift b/Meshtastic/Enums/AppSettingsEnums.swift index fd4fe088..4169444e 100644 --- a/Meshtastic/Enums/AppSettingsEnums.swift +++ b/Meshtastic/Enums/AppSettingsEnums.swift @@ -60,7 +60,7 @@ enum MeshMapDistances: Double, CaseIterable, Identifiable { var id: Double { self.rawValue } var description: String { let distanceFormatter = MKDistanceFormatter() - return "\(distanceFormatter.string(fromDistance: Double(self.rawValue))) away" + return "up to \(distanceFormatter.string(fromDistance: Double(self.rawValue))) away" } } diff --git a/Meshtastic/Extensions/CoreData/WaypointEntityExtension.swift b/Meshtastic/Extensions/CoreData/WaypointEntityExtension.swift index 76fe8f8e..0c3d6854 100644 --- a/Meshtastic/Extensions/CoreData/WaypointEntityExtension.swift +++ b/Meshtastic/Extensions/CoreData/WaypointEntityExtension.swift @@ -13,8 +13,10 @@ extension WaypointEntity { static func allWaypointssFetchRequest() -> NSFetchRequest { let request: NSFetchRequest = WaypointEntity.fetchRequest() - //request.fetchLimit = 100 + 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) diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index 2e1e31cf..0655b89f 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -28,9 +28,9 @@ func generateMessageMarkdown (message: String) -> String { let phone = messageWithMarkdown[range] messageWithMarkdown = messageWithMarkdown.replacingOccurrences(of: phone, with: "[\(phone)](tel:\(phone))") } else if match.resultType == .link { - let start = match.range.lowerBound - let stop = match.range.upperBound - if stop > start { + if (match.range.location != NSNotFound) { + let start = match.range.lowerBound + let stop = match.range.upperBound let url = message[start ..< stop] let absoluteUrl = match.url?.absoluteString ?? "" let markdownUrl = "[\(url)](\(absoluteUrl))" diff --git a/Meshtastic/Views/Nodes/Helpers/Map/MapContent/MeshMapContent.swift b/Meshtastic/Views/Nodes/Helpers/Map/MapContent/MeshMapContent.swift index ae1f8a0c..7a96c3d1 100644 --- a/Meshtastic/Views/Nodes/Helpers/Map/MapContent/MeshMapContent.swift +++ b/Meshtastic/Views/Nodes/Helpers/Map/MapContent/MeshMapContent.swift @@ -14,8 +14,7 @@ import MapKit @available(iOS 17.0, macOS 14.0, *) struct MeshMapContent: MapContent { - @State var positions: [PositionEntity] = [] - @State var waypoints: [WaypointEntity] = [] + //@State var waypoints: [WaypointEntity] = [] @State var routes: [RouteEntity] = [] /// Parameters @Binding var showUserLocation: Bool @@ -29,13 +28,19 @@ struct MeshMapContent: MapContent { @Binding var selectedPosition: PositionEntity? @Binding var showWaypoints: Bool @Binding var selectedWaypoint: WaypointEntity? + + @FetchRequest(fetchRequest: PositionEntity.allPositionsFetchRequest(), animation: .easeIn) + var positions: FetchedResults + + @FetchRequest(fetchRequest: WaypointEntity.allWaypointssFetchRequest(), animation: .none) + var waypoints: FetchedResults var delay: Double = 0 @State private var scale: CGFloat = 0.5 @MapContentBuilder var meshMap: some MapContent { - let lineCoords = positions.compactMap({(position) -> CLLocationCoordinate2D in + let lineCoords = Array(positions).compactMap({(position) -> CLLocationCoordinate2D in return position.nodeCoordinate ?? LocationsHandler.DefaultLocation }) /// Convex Hull @@ -148,7 +153,7 @@ struct MeshMapContent: MapContent { } } /// Routes - ForEach(Array(routes), id: \.id) { route in + ForEach(Array(routes)) { route in let routeLocations = Array(route.locations!) as! [LocationEntity] let routeCoords = routeLocations.compactMap({(loc) -> CLLocationCoordinate2D in return loc.locationCoordinate ?? LocationHelper.DefaultLocation @@ -183,13 +188,15 @@ struct MeshMapContent: MapContent { /// Waypoint Annotations if waypoints.count > 0 && showWaypoints { - ForEach(Array(waypoints), id: \.id) { waypoint in + ForEach(Array(waypoints) as! [WaypointEntity], id: \.self) { waypoint in Annotation(waypoint.name ?? "?", coordinate: waypoint.coordinate) { LazyVStack { - CircleText(text: String(UnicodeScalar(Int(waypoint.icon)) ?? "📍"), color: Color.orange, circleSize: 40) - .onTapGesture(perform: { location in - selectedWaypoint = (selectedWaypoint == waypoint ? nil : waypoint) - }) + ZStack { + CircleText(text: String(UnicodeScalar(Int(waypoint.icon)) ?? "📍"), color: Color.orange, circleSize: 40) + .onTapGesture(perform: { location in + selectedWaypoint = (selectedWaypoint == waypoint ? nil : waypoint) + }) + } } } } @@ -199,5 +206,6 @@ struct MeshMapContent: MapContent { @MapContentBuilder var body: some MapContent { meshMap + } } diff --git a/Meshtastic/Views/Nodes/Helpers/Map/MapContent/NodeMapContent.swift b/Meshtastic/Views/Nodes/Helpers/Map/MapContent/NodeMapContent.swift index 2c15e9de..8e8d0b26 100644 --- a/Meshtastic/Views/Nodes/Helpers/Map/MapContent/NodeMapContent.swift +++ b/Meshtastic/Views/Nodes/Helpers/Map/MapContent/NodeMapContent.swift @@ -29,8 +29,6 @@ struct NodeMapContent: MapContent { @State var isShowingAltitude = false @State var isEditingSettings = false @State var selectedPosition: PositionEntity? - @State var showWaypoints = false - @State var selectedWaypoint: WaypointEntity? @State var isMeshMap = false //let region: MKCoordinateRegion diff --git a/Meshtastic/Views/Nodes/Helpers/Map/MapSettingsForm.swift b/Meshtastic/Views/Nodes/Helpers/Map/MapSettingsForm.swift index fa1ce751..6a65c356 100644 --- a/Meshtastic/Views/Nodes/Helpers/Map/MapSettingsForm.swift +++ b/Meshtastic/Views/Nodes/Helpers/Map/MapSettingsForm.swift @@ -41,22 +41,15 @@ struct MapSettingsForm: View { UserDefaults.mapLayer = newMapLayer } if meshMap { - VStack { - HStack { - Label("Show nodes", systemImage: "lines.measurement.horizontal") - Picker("", selection: $meshMapDistance) { - ForEach(MeshMapDistances.allCases) { di in - Text(di.description) - .tag(di.id) - } + HStack { + Label("Show nodes", systemImage: "lines.measurement.horizontal") + Picker("", selection: $meshMapDistance) { + ForEach(MeshMapDistances.allCases) { di in + Text(di.description) + .tag(di.id) } - .pickerStyle(DefaultPickerStyle()) } - .listRowSeparator(.hidden) - Text("You will need to close and re-open the app for this to take effect.") - .font(.callout) - .foregroundColor(.gray) - .listRowSeparator(/*@START_MENU_TOKEN@*/.visible/*@END_MENU_TOKEN@*/) + .pickerStyle(DefaultPickerStyle()) } .onChange(of: meshMapDistance) { newMeshMapDistance in UserDefaults.meshMapDistance = newMeshMapDistance diff --git a/Meshtastic/Views/Nodes/Helpers/Map/NodeMapSwiftUI.swift b/Meshtastic/Views/Nodes/Helpers/Map/NodeMapSwiftUI.swift index 3f2fb0fb..282a5a8c 100644 --- a/Meshtastic/Views/Nodes/Helpers/Map/NodeMapSwiftUI.swift +++ b/Meshtastic/Views/Nodes/Helpers/Map/NodeMapSwiftUI.swift @@ -36,8 +36,6 @@ struct NodeMapSwiftUI: View { @State var isShowingAltitude = false @State var isEditingSettings = false @State var selectedPosition: PositionEntity? - @State var showWaypoints = false - @State var selectedWaypoint: WaypointEntity? @State var isMeshMap = false @State private var mapRegion = MKCoordinateRegion.init() @@ -88,10 +86,6 @@ struct NodeMapSwiftUI: View { .padding(.horizontal, 20) } } - .sheet(item: $selectedWaypoint) { selection in - WaypointForm(waypoint: selection) - .padding() - } .sheet(isPresented: $isEditingSettings) { MapSettingsForm(nodeHistory: $showNodeHistory, routeLines: $showRouteLines, convexHull: $showConvexHull, traffic: $showTraffic, pointsOfInterest: $showPointsOfInterest, mapLayer: $selectedMapLayer, meshMapDistance: $meshMapDistance, meshMap: $isMeshMap) .onChange(of: (selectedMapLayer)) { newMapLayer in @@ -162,21 +156,6 @@ struct NodeMapSwiftUI: View { .tint(Color(UIColor.secondarySystemBackground)) .foregroundColor(.accentColor) .buttonStyle(.borderedProminent) - /// Show / Hide Waypoints Button - if waypoints.count > 0 { - - Button(action: { - withAnimation { - showWaypoints = !showWaypoints - } - }) { - Image(systemName: showWaypoints ? "signpost.right.and.left.fill" : "signpost.right.and.left") - .padding(.vertical, 5) - } - .tint(Color(UIColor.secondarySystemBackground)) - .foregroundColor(.accentColor) - .buttonStyle(.borderedProminent) - } /// Look Around Button if self.scene != nil { Button(action: { diff --git a/Meshtastic/Views/Nodes/MeshMap.swift b/Meshtastic/Views/Nodes/MeshMap.swift index 780ebde2..b992635f 100644 --- a/Meshtastic/Views/Nodes/MeshMap.swift +++ b/Meshtastic/Views/Nodes/MeshMap.swift @@ -37,17 +37,12 @@ struct MeshMap: View { @State var position = MapCameraPosition.automatic @State var isEditingSettings = false @State var selectedPosition: PositionEntity? - @State var showWaypoints = false + @State var showWaypoints = true @State var editingWaypoint: WaypointEntity? @State var selectedWaypoint: WaypointEntity? @State var newWaypointCoord: CLLocationCoordinate2D? @State var isMeshMap = true - - @FetchRequest(fetchRequest: PositionEntity.allPositionsFetchRequest(), animation: .none) - var positions: FetchedResults - - @FetchRequest(fetchRequest: WaypointEntity.allWaypointssFetchRequest(), animation: .none) - var waypoints: FetchedResults + @FetchRequest(sortDescriptors: [NSSortDescriptor(key: "name", ascending: true)], predicate: NSPredicate(format: "enabled == true", ""), animation: .none) @@ -59,7 +54,7 @@ struct MeshMap: View { ZStack { MapReader { reader in Map(position: $position, bounds: MapCameraBounds(minimumDistance: 1, maximumDistance: .infinity), scope: mapScope) { - MeshMapContent(positions: Array(positions), waypoints: Array(waypoints), routes: Array(routes), showUserLocation: $showUserLocation, showNodeHistory: $showNodeHistory, showRouteLines: $showRouteLines, showConvexHull: $showConvexHull, showTraffic: $showTraffic, showPointsOfInterest: $showPointsOfInterest, selectedMapLayer: $selectedMapLayer, selectedPosition: $selectedPosition, showWaypoints: $showWaypoints, selectedWaypoint: $selectedWaypoint) + MeshMapContent(routes: Array(routes), showUserLocation: $showUserLocation, showNodeHistory: $showNodeHistory, showRouteLines: $showRouteLines, showConvexHull: $showConvexHull, showTraffic: $showTraffic, showPointsOfInterest: $showPointsOfInterest, selectedMapLayer: $selectedMapLayer, selectedPosition: $selectedPosition, showWaypoints: $showWaypoints, selectedWaypoint: $selectedWaypoint) } .mapScope(mapScope) @@ -142,12 +137,12 @@ struct MeshMap: View { print("Waypoint id not found") return } - guard let waypoint = waypoints.first(where: { $0.id == Int64(waypointId) }) else { - print("Waypoint not found") - return - } +// guard let waypoint = waypoints.first(where: { $0.id == Int64(waypointId) }) else { +// print("Waypoint not found") +// return +// } showWaypoints = true - position = .camera(MapCamera(centerCoordinate: waypoint.coordinate, distance: 1000, heading: 0, pitch: 60)) + //position = .camera(MapCamera(centerCoordinate: waypoint.coordinate, distance: 1000, heading: 0, pitch: 60)) } } .onChange(of: (selectedMapLayer)) { newMapLayer in @@ -179,26 +174,25 @@ struct MeshMap: View { .foregroundColor(.accentColor) .buttonStyle(.borderedProminent) /// Show / Hide Waypoints Button - if waypoints.count > 0 { - - Button(action: { - withAnimation { - showWaypoints = !showWaypoints - } - }) { - Image(systemName: showWaypoints ? "signpost.right.and.left.fill" : "signpost.right.and.left") - .padding(.vertical, 5) + + Button(action: { + withAnimation { + showWaypoints = !showWaypoints } - .tint(Color(UIColor.secondarySystemBackground)) - .foregroundColor(.accentColor) - .buttonStyle(.borderedProminent) + }) { + Image(systemName: showWaypoints ? "signpost.right.and.left.fill" : "signpost.right.and.left") + .padding(.vertical, 5) } + .tint(Color(UIColor.secondarySystemBackground)) + .foregroundColor(.accentColor) + .buttonStyle(.borderedProminent) + } .controlSize(.regular) .padding(5) } } - .navigationTitle("\(positions.count) Nodes") + .navigationTitle("Mesh Map") .navigationBarItems(leading: MeshtasticLogo(), trailing: ZStack { ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?") }) diff --git a/Meshtastic/Views/Nodes/NodeList.swift b/Meshtastic/Views/Nodes/NodeList.swift index c2532ad7..902d7df5 100644 --- a/Meshtastic/Views/Nodes/NodeList.swift +++ b/Meshtastic/Views/Nodes/NodeList.swift @@ -159,6 +159,25 @@ struct NodeList: View { Text("Any missed messages will be delivered again.") } } + .safeAreaInset(edge: .bottom, alignment: .trailing) { + HStack { + Button(action: { + withAnimation { + //isEditingSettings = !isEditingSettings + } + }) { + Image(systemName: true ? "line.3.horizontal.decrease.circle" : "line.3.horizontal.decrease.circle.fill") + .padding(.vertical, 5) + } + .tint(Color(UIColor.secondarySystemBackground)) + .foregroundColor(.accentColor) + .buttonStyle(.borderedProminent) + + } + .controlSize(.regular) + .padding(5) + } + .padding(.bottom, 5) .searchable(text: $searchState.searchText, placement: nodes.count > 10 ? .navigationBarDrawer(displayMode: .always) : .automatic, prompt: "Find a node") .disableAutocorrection(true) .scrollDismissesKeyboard(.immediately) @@ -220,7 +239,7 @@ struct NodeList: View { name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?", phoneOnly: true) }) } - .padding(.bottom, 5) + } else { if #available (iOS 17, *) { ContentUnavailableView("select.node", systemImage: "flipphone") diff --git a/Meshtastic/Views/Settings/Config/PositionConfig.swift b/Meshtastic/Views/Settings/Config/PositionConfig.swift index 9f96ef8b..b18ff5e6 100644 --- a/Meshtastic/Views/Settings/Config/PositionConfig.swift +++ b/Meshtastic/Views/Settings/Config/PositionConfig.swift @@ -72,6 +72,10 @@ struct PositionConfig: View { /// walking speeds are likely to be error prone like the compass @State var includeHeading = false + /// Minimum Version for fixed postion admin messages + @State var minimumVersion = "2.3.3" + @State private var supportedVersion = true + var body: some View { VStack { Form { @@ -159,6 +163,15 @@ struct PositionConfig: View { Text("If enabled your current phone location will be sent to the device and will broadcast over the mesh on the position interval. Fixed position will always use the most recent position the device has.") } .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + + } + .onChange(of: fixedPosition) { newFixed in + if node != nil && node!.positionConfig != nil { + if newFixed != node!.positionConfig!.fixedPosition { hasChanges = true } + } + if supportedVersion && hasChanges && !newFixed { + // Send Admin message to remove the fixed position + } } } } @@ -316,6 +329,9 @@ struct PositionConfig: View { self.bleManager.context = context } setPositionValues() + supportedVersion = bleManager.connectedVersion == "0.0.0" || self.minimumVersion.compare(bleManager.connectedVersion, options: .numeric) == .orderedAscending || minimumVersion.compare(bleManager.connectedVersion, options: .numeric) == .orderedSame + + // Need to request a PositionConfig from the remote node before allowing changes if bleManager.connectedPeripheral != nil && node?.positionConfig == nil { print("empty position config") @@ -355,11 +371,6 @@ struct PositionConfig: View { if newSmartPositionEnabled != node!.positionConfig!.smartPositionEnabled { hasChanges = true } } } - .onChange(of: fixedPosition) { newFixed in - if node != nil && node!.positionConfig != nil { - if newFixed != node!.positionConfig!.fixedPosition { hasChanges = true } - } - } .onChange(of: positionBroadcastSeconds) { newPositionBroadcastSeconds in if node != nil && node!.positionConfig != nil { if newPositionBroadcastSeconds != node!.positionConfig!.positionBroadcastSeconds { hasChanges = true } diff --git a/Meshtastic/Views/Settings/Firmware.swift b/Meshtastic/Views/Settings/Firmware.swift index 98b29957..3558da1f 100644 --- a/Meshtastic/Views/Settings/Firmware.swift +++ b/Meshtastic/Views/Settings/Firmware.swift @@ -12,7 +12,7 @@ struct Firmware: View { @Environment(\.managedObjectContext) var context @EnvironmentObject var bleManager: BLEManager var node: NodeInfoEntity? - @State var minimumVersion = "2.3.0" + @State var minimumVersion = "2.3.2" @State var version = "" @State private var currentDevice: DeviceHardware? @State private var latestStable: FirmwareRelease?