mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Move fetch requests to mapcontent
This commit is contained in:
parent
b0101ab4ce
commit
7eb6659c0c
11 changed files with 88 additions and 84 deletions
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,8 +13,10 @@ extension WaypointEntity {
|
|||
|
||||
static func allWaypointssFetchRequest() -> NSFetchRequest<WaypointEntity> {
|
||||
let request: NSFetchRequest<WaypointEntity> = 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)
|
||||
|
|
|
|||
|
|
@ -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))"
|
||||
|
|
|
|||
|
|
@ -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<PositionEntity>
|
||||
|
||||
@FetchRequest(fetchRequest: WaypointEntity.allWaypointssFetchRequest(), animation: .none)
|
||||
var waypoints: FetchedResults<WaypointEntity>
|
||||
|
||||
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
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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<PositionEntity>
|
||||
|
||||
@FetchRequest(fetchRequest: WaypointEntity.allWaypointssFetchRequest(), animation: .none)
|
||||
var waypoints: FetchedResults<WaypointEntity>
|
||||
|
||||
|
||||
@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 : "?")
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue