From 90dd5b2141490f958e381bdbdd6479f5437aa291 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Fri, 26 Sep 2025 19:17:52 -0700 Subject: [PATCH] Make current location nilable, remove log spam --- Localizable.xcstrings | 21 ++----- .../CoreData/PositionEntityExtension.swift | 36 +++++------ Meshtastic/Helpers/LocationsHandler.swift | 36 +---------- .../Helpers/Weather/NodeWeatherForecast.swift | 11 ++-- Meshtastic/Views/Messages/UserList.swift | 27 +++++---- .../Nodes/Helpers/Map/WaypointForm.swift | 59 ++++++++++--------- Meshtastic/Views/Nodes/NodeList.swift | 30 +++++----- 7 files changed, 91 insertions(+), 129 deletions(-) diff --git a/Localizable.xcstrings b/Localizable.xcstrings index 854b9bc0..a9e3685e 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -15976,22 +15976,6 @@ } } }, - "Generated from your private key and sent to other nodes on the mesh so they can compute a shared secret key." : { - "localizations" : { - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "秘密キーから生成され、メッシュ上の他のノードに送信されて、共有秘密キーの計算を可能にします。" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Генерисано из твог приватног кључа и послато другим чворовима на мрежи како би им омогућило да израчунају заједнички тајни кључ.\n" - } - } - } - }, "Get custom waterproof solar and detection sensor router nodes, aluminium desktop nodes and rugged handsets." : { "localizations" : { "it" : { @@ -42335,6 +42319,9 @@ } } } + }, + "Your public key is generated from your private key and sent to other nodes on the mesh so they can compute a shared secret key with you." : { + }, "Your region has a %lld%% duty cycle. MQTT is not advised when you are duty cycle restricted, the extra traffic will quickly overwhelm your LoRa mesh." : { "localizations" : { @@ -42428,4 +42415,4 @@ } }, "version" : "1.1" -} +} \ No newline at end of file diff --git a/Meshtastic/Extensions/CoreData/PositionEntityExtension.swift b/Meshtastic/Extensions/CoreData/PositionEntityExtension.swift index 4e05bcd6..3c7ec96c 100644 --- a/Meshtastic/Extensions/CoreData/PositionEntityExtension.swift +++ b/Meshtastic/Extensions/CoreData/PositionEntityExtension.swift @@ -21,27 +21,27 @@ extension PositionEntity { request.returnsDistinctResults = true request.sortDescriptors = [NSSortDescriptor(key: "time", ascending: false)] let positionPredicate = NSPredicate(format: "nodePosition != nil && (nodePosition.user.shortName != nil || nodePosition.user.shortName != '') && latest == true") - - let pointOfInterest = LocationsHandler.currentLocation - - if pointOfInterest.latitude != LocationsHandler.DefaultLocation.latitude && pointOfInterest.longitude != LocationsHandler.DefaultLocation.longitude { - let d: Double = UserDefaults.meshMapDistance * 1.1 - let r: Double = 6371009 - let meanLatitidue = pointOfInterest.latitude * .pi / 180 - let deltaLatitude = d / r * 180 / .pi - let deltaLongitude = d / (r * cos(meanLatitidue)) * 180 / .pi - let minLatitude: Double = pointOfInterest.latitude - deltaLatitude - let maxLatitude: Double = pointOfInterest.latitude + deltaLatitude - let minLongitude: Double = pointOfInterest.longitude - deltaLongitude - let maxLongitude: Double = pointOfInterest.longitude + deltaLongitude - let distancePredicate = NSPredicate(format: "(%lf <= (longitudeI / 1e7)) AND ((longitudeI / 1e7) <= %lf) AND (%lf <= (latitudeI / 1e7)) AND ((latitudeI / 1e7) <= %lf)", minLongitude, maxLongitude, minLatitude, maxLatitude) - request.predicate = NSCompoundPredicate(type: .and, subpredicates: [positionPredicate, distancePredicate]) - } else { - request.predicate = positionPredicate + + if let pointOfInterest = LocationsHandler.currentLocation { + + if pointOfInterest.latitude != LocationsHandler.DefaultLocation.latitude && pointOfInterest.longitude != LocationsHandler.DefaultLocation.longitude { + let d: Double = UserDefaults.meshMapDistance * 1.1 + let r: Double = 6371009 + let meanLatitidue = pointOfInterest.latitude * .pi / 180 + let deltaLatitude = d / r * 180 / .pi + let deltaLongitude = d / (r * cos(meanLatitidue)) * 180 / .pi + let minLatitude: Double = pointOfInterest.latitude - deltaLatitude + let maxLatitude: Double = pointOfInterest.latitude + deltaLatitude + let minLongitude: Double = pointOfInterest.longitude - deltaLongitude + let maxLongitude: Double = pointOfInterest.longitude + deltaLongitude + let distancePredicate = NSPredicate(format: "(%lf <= (longitudeI / 1e7)) AND ((longitudeI / 1e7) <= %lf) AND (%lf <= (latitudeI / 1e7)) AND ((latitudeI / 1e7) <= %lf)", minLongitude, maxLongitude, minLatitude, maxLatitude) + request.predicate = NSCompoundPredicate(type: .and, subpredicates: [positionPredicate, distancePredicate]) + } else { + request.predicate = positionPredicate + } } return request } - var latitude: Double? { let d = Double(latitudeI) diff --git a/Meshtastic/Helpers/LocationsHandler.swift b/Meshtastic/Helpers/LocationsHandler.swift index 4c1552a4..b174970a 100644 --- a/Meshtastic/Helpers/LocationsHandler.swift +++ b/Meshtastic/Helpers/LocationsHandler.swift @@ -220,50 +220,18 @@ import OSLog // If not recording, only keep the latest location. locationsArray = [location] } - // Store the last known location in UserDefaults for persistence. - UserDefaults.standard.set(location.coordinate.latitude, forKey: "lastKnownLatitude") - UserDefaults.standard.set(location.coordinate.longitude, forKey: "lastKnownLongitude") - UserDefaults.standard.set(Date().timeIntervalSince1970, forKey: "lastKnownLocationTimestamp") return true } // Default location (Apple Park) used as a fallback. // nonisolated because it is never mutated nonisolated static let DefaultLocation = CLLocationCoordinate2D(latitude: 37.3346, longitude: -122.0090) /// Provides the current location, falling back to last known or a default if necessary. - static var currentLocation: CLLocationCoordinate2D { + static var currentLocation: CLLocationCoordinate2D? { // Attempt to get the most recent location from the manager. if let location = shared.manager.location { return location.coordinate } else { - // If manager.location is nil, check authorization status and potentially request. - let status = shared.manager.authorizationStatus - switch status { - case .notDetermined: - Logger.services.info("📍 [App] Location permission not determined, requesting authorization (WhenInUse)") - // Requesting WhenInUse authorization here. For "Always" authorization, - // `requestLocationAlwaysPermissions()` should be called explicitly, - // typically from a user action or app setup. - shared.manager.requestWhenInUseAuthorization() - case .denied, .restricted: - Logger.services.warning("📍 [App] Location access denied or restricted. Please enable location services in Settings to get accurate positioning!") - // Requesting WhenInUse authorization again, though user interaction is needed for denied/restricted. - shared.manager.requestWhenInUseAuthorization() - default: - break // For .authorizedAlways, .authorizedWhenInUse, .limited - } - // Fallback 1: Last known location from UserDefaults if it's recent (within 4 hours). - if let lat = UserDefaults.standard.object(forKey: "lastKnownLatitude") as? Double, - let lon = UserDefaults.standard.object(forKey: "lastKnownLongitude") as? Double, - let timestamp = UserDefaults.standard.object(forKey: "lastKnownLocationTimestamp") as? Double, - lat >= -90 && lat <= 90, // Validate latitude - lon >= -180 && lon <= 180, // Validate longitude - Date().timeIntervalSince1970 - timestamp <= 14_400 { // 4 hours in seconds - Logger.services.info("📍 [App] Falling back to last known location (age: \(Int(Date().timeIntervalSince1970 - timestamp)) seconds)") - return CLLocationCoordinate2D(latitude: lat, longitude: lon) - } - // Fallback 2: Default location if no other location is available. - Logger.services.warning("📍 [App] No Location and no last known location, check your location settings. Falling back to default location.") - return DefaultLocation + return nil } } /// Estimates the number of satellites in view based on horizontal and vertical accuracy. diff --git a/Meshtastic/Views/Helpers/Weather/NodeWeatherForecast.swift b/Meshtastic/Views/Helpers/Weather/NodeWeatherForecast.swift index 53f19a0f..74b7a409 100644 --- a/Meshtastic/Views/Helpers/Weather/NodeWeatherForecast.swift +++ b/Meshtastic/Views/Helpers/Weather/NodeWeatherForecast.swift @@ -211,9 +211,12 @@ struct NodeWeatherForecast { struct NodeWeatherForecastView_Previews: PreviewProvider { static var previews: some View { - NodeWeatherForecastView(location: CLLocation(latitude: LocationsHandler.currentLocation.latitude, longitude: LocationsHandler.currentLocation.longitude) ) - .aspectRatio(2, contentMode: .fit) - .padding() - .previewLayout(.sizeThatFits) + + if let cl = LocationsHandler.currentLocation { + NodeWeatherForecastView(location: CLLocation(latitude: cl.latitude, longitude: cl.longitude) ) + .aspectRatio(2, contentMode: .fit) + .padding() + .previewLayout(.sizeThatFits) + } } } diff --git a/Meshtastic/Views/Messages/UserList.swift b/Meshtastic/Views/Messages/UserList.swift index 260f0b45..2199e997 100644 --- a/Meshtastic/Views/Messages/UserList.swift +++ b/Meshtastic/Views/Messages/UserList.swift @@ -290,19 +290,20 @@ fileprivate extension NodeFilterParameters { } // Distance if distanceFilter { - let pointOfInterest = LocationsHandler.currentLocation - if pointOfInterest.latitude != LocationsHandler.DefaultLocation.latitude && pointOfInterest.longitude != LocationsHandler.DefaultLocation.longitude { - let d: Double = maxDistance * 1.1 - let r: Double = 6371009 - let meanLatitidue = pointOfInterest.latitude * .pi / 180 - let deltaLatitude = d / r * 180 / .pi - let deltaLongitude = d / (r * cos(meanLatitidue)) * 180 / .pi - let minLatitude: Double = pointOfInterest.latitude - deltaLatitude - let maxLatitude: Double = pointOfInterest.latitude + deltaLatitude - let minLongitude: Double = pointOfInterest.longitude - deltaLongitude - let maxLongitude: Double = pointOfInterest.longitude + deltaLongitude - let distancePredicate = NSPredicate(format: "(SUBQUERY(userNode.positions, $position, $position.latest == TRUE && (%lf <= ($position.longitudeI / 1e7)) AND (($position.longitudeI / 1e7) <= %lf) AND (%lf <= ($position.latitudeI / 1e7)) AND (($position.latitudeI / 1e7) <= %lf))).@count > 0", minLongitude, maxLongitude, minLatitude, maxLatitude) - predicates.append(distancePredicate) + if let pointOfInterest = LocationsHandler.currentLocation { + if pointOfInterest.latitude != LocationsHandler.DefaultLocation.latitude && pointOfInterest.longitude != LocationsHandler.DefaultLocation.longitude { + let d: Double = maxDistance * 1.1 + let r: Double = 6371009 + let meanLatitidue = pointOfInterest.latitude * .pi / 180 + let deltaLatitude = d / r * 180 / .pi + let deltaLongitude = d / (r * cos(meanLatitidue)) * 180 / .pi + let minLatitude: Double = pointOfInterest.latitude - deltaLatitude + let maxLatitude: Double = pointOfInterest.latitude + deltaLatitude + let minLongitude: Double = pointOfInterest.longitude - deltaLongitude + let maxLongitude: Double = pointOfInterest.longitude + deltaLongitude + let distancePredicate = NSPredicate(format: "(SUBQUERY(userNode.positions, $position, $position.latest == TRUE && (%lf <= ($position.longitudeI / 1e7)) AND (($position.longitudeI / 1e7) <= %lf) AND (%lf <= ($position.latitudeI / 1e7)) AND (($position.latitudeI / 1e7) <= %lf))).@count > 0", minLongitude, maxLongitude, minLatitude, maxLatitude) + predicates.append(distancePredicate) + } } } // Always apply unmessagable and connected node filters diff --git a/Meshtastic/Views/Nodes/Helpers/Map/WaypointForm.swift b/Meshtastic/Views/Nodes/Helpers/Map/WaypointForm.swift index ea66a340..ca3c9d0f 100644 --- a/Meshtastic/Views/Nodes/Helpers/Map/WaypointForm.swift +++ b/Meshtastic/Views/Nodes/Helpers/Map/WaypointForm.swift @@ -40,21 +40,21 @@ struct WaypointForm: View { .font(.largeTitle) Divider() Form { - let distance = CLLocation(latitude: LocationsHandler.currentLocation.latitude, longitude: LocationsHandler.currentLocation.longitude).distance(from: CLLocation(latitude: waypoint.coordinate.latitude, longitude: waypoint.coordinate.longitude )) - Section(header: Text("Coordinate") ) { - HStack { - Text("Location:") - .foregroundColor(.secondary) - Text("\(String(format: "%.5f", waypoint.coordinate.latitude) + "," + String(format: "%.5f", waypoint.coordinate.longitude))") - .textSelection(.enabled) - .foregroundColor(.secondary) - .font(.caption) - - } + if let cl = LocationsHandler.currentLocation { + let distance = CLLocation(latitude: cl.latitude, longitude: cl.longitude).distance(from: CLLocation(latitude: waypoint.coordinate.latitude, longitude: waypoint.coordinate.longitude )) + Section(header: Text("Coordinate") ) { + HStack { + Text("Location:") + .foregroundColor(.secondary) + Text("\(String(format: "%.5f", waypoint.coordinate.latitude) + "," + String(format: "%.5f", waypoint.coordinate.longitude))") + .textSelection(.enabled) + .foregroundColor(.secondary) + .font(.caption) + + } Button { - let currentLoc = LocationsHandler.currentLocation - waypoint.coordinate.longitude = currentLoc.longitude - waypoint.coordinate.latitude = currentLoc.latitude + waypoint.coordinate.longitude = cl.longitude + waypoint.coordinate.latitude = cl.latitude } label: { HStack { Text("Use my Location") @@ -62,10 +62,11 @@ struct WaypointForm: View { } } .accessibilityLabel("Set to current location") - HStack { - if waypoint.coordinate.latitude != 0 && waypoint.coordinate.longitude != 0 { - DistanceText(meters: distance) - .foregroundColor(Color.gray) + HStack { + if waypoint.coordinate.latitude != 0 && waypoint.coordinate.longitude != 0 { + DistanceText(meters: distance) + .foregroundColor(Color.gray) + } } } } @@ -374,17 +375,19 @@ struct WaypointForm: View { .padding(.bottom, 5) } /// Distance - if LocationsHandler.currentLocation.distance(from: LocationsHandler.DefaultLocation) > 0.0 { - let metersAway = waypoint.coordinate.distance(from: LocationsHandler.currentLocation) - Label { - Text("Distance".localized + ": \(distanceFormatter.string(fromDistance: Double(metersAway)))") - .foregroundColor(.primary) - } icon: { - Image(systemName: "lines.measurement.horizontal") - .symbolRenderingMode(.hierarchical) - .frame(width: 35) + if let cl = LocationsHandler.currentLocation { + if cl.distance(from: cl) > 0.0 { + let metersAway = waypoint.coordinate.distance(from: cl) + Label { + Text("Distance".localized + ": \(distanceFormatter.string(fromDistance: Double(metersAway)))") + .foregroundColor(.primary) + } icon: { + Image(systemName: "lines.measurement.horizontal") + .symbolRenderingMode(.hierarchical) + .frame(width: 35) + } + .padding(.bottom, 5) } - .padding(.bottom, 5) } } .padding(.top) diff --git a/Meshtastic/Views/Nodes/NodeList.swift b/Meshtastic/Views/Nodes/NodeList.swift index f781a411..34393253 100644 --- a/Meshtastic/Views/Nodes/NodeList.swift +++ b/Meshtastic/Views/Nodes/NodeList.swift @@ -341,23 +341,23 @@ fileprivate extension NodeFilterParameters { // Distance filter if distanceFilter { - let pointOfInterest = LocationsHandler.currentLocation - - if pointOfInterest.latitude != LocationsHandler.DefaultLocation.latitude && pointOfInterest.longitude != LocationsHandler.DefaultLocation.longitude { - let d: Double = maxDistance * 1.1 - let r: Double = 6371009 - let meanLatitidue = pointOfInterest.latitude * .pi / 180 - let deltaLatitude = d / r * 180 / .pi - let deltaLongitude = d / (r * cos(meanLatitidue)) * 180 / .pi - let minLatitude: Double = pointOfInterest.latitude - deltaLatitude - let maxLatitude: Double = pointOfInterest.latitude + deltaLatitude - let minLongitude: Double = pointOfInterest.longitude - deltaLongitude - let maxLongitude: Double = pointOfInterest.longitude + deltaLongitude - let distancePredicate = NSPredicate(format: "(SUBQUERY(positions, $position, $position.latest == TRUE && (%lf <= ($position.longitudeI / 1e7)) AND (($position.longitudeI / 1e7) <= %lf) AND (%lf <= ($position.latitudeI / 1e7)) AND (($position.latitudeI / 1e7) <= %lf))).@count > 0", minLongitude, maxLongitude, minLatitude, maxLatitude) - predicates.append(distancePredicate) + if let pointOfInterest = LocationsHandler.currentLocation { + + if pointOfInterest.latitude != LocationsHandler.DefaultLocation.latitude && pointOfInterest.longitude != LocationsHandler.DefaultLocation.longitude { + let d: Double = maxDistance * 1.1 + let r: Double = 6371009 + let meanLatitidue = pointOfInterest.latitude * .pi / 180 + let deltaLatitude = d / r * 180 / .pi + let deltaLongitude = d / (r * cos(meanLatitidue)) * 180 / .pi + let minLatitude: Double = pointOfInterest.latitude - deltaLatitude + let maxLatitude: Double = pointOfInterest.latitude + deltaLatitude + let minLongitude: Double = pointOfInterest.longitude - deltaLongitude + let maxLongitude: Double = pointOfInterest.longitude + deltaLongitude + let distancePredicate = NSPredicate(format: "(SUBQUERY(positions, $position, $position.latest == TRUE && (%lf <= ($position.longitudeI / 1e7)) AND (($position.longitudeI / 1e7) <= %lf) AND (%lf <= ($position.latitudeI / 1e7)) AND (($position.latitudeI / 1e7) <= %lf))).@count > 0", minLongitude, maxLongitude, minLatitude, maxLatitude) + predicates.append(distancePredicate) + } } } - return predicates.isEmpty ? nil : NSCompoundPredicate(type: .and, subpredicates: predicates) } }