diff --git a/Localizable.xcstrings b/Localizable.xcstrings index 2438475a..687024fb 100644 --- a/Localizable.xcstrings +++ b/Localizable.xcstrings @@ -11481,9 +11481,6 @@ }, "Map Tile Data" : { - }, - "Map Type" : { - }, "map.centering" : { "extractionState" : "manual", diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 865f8d23..50e13f28 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -1202,65 +1202,37 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate @MainActor public func getPositionFromPhoneGPS(destNum: Int64, fixedPosition: Bool) -> Position? { var positionPacket = Position() - if #available(iOS 17.0, macOS 14.0, *) { - guard let lastLocation = LocationsHandler.shared.locationsArray.last else { - return nil - } + guard let lastLocation = LocationsHandler.shared.locationsArray.last else { + return nil + } - if lastLocation == CLLocation(latitude: 0, longitude: 0) { - return nil - } - - positionPacket.latitudeI = Int32(lastLocation.coordinate.latitude * 1e7) - positionPacket.longitudeI = Int32(lastLocation.coordinate.longitude * 1e7) - let timestamp = lastLocation.timestamp - positionPacket.time = UInt32(timestamp.timeIntervalSince1970) - positionPacket.timestamp = UInt32(timestamp.timeIntervalSince1970) - positionPacket.altitude = Int32(lastLocation.altitude) - positionPacket.satsInView = UInt32(LocationsHandler.satsInView) - let currentSpeed = lastLocation.speed - if currentSpeed > 0 && (!currentSpeed.isNaN || !currentSpeed.isInfinite) { - positionPacket.groundSpeed = UInt32(currentSpeed) - } - let currentHeading = lastLocation.course - if (currentHeading > 0 && currentHeading <= 360) && (!currentHeading.isNaN || !currentHeading.isInfinite) { - positionPacket.groundTrack = UInt32(currentHeading) - } - /// Set location source for time - if !fixedPosition { - /// From GPS treat time as good - positionPacket.locationSource = Position.LocSource.locExternal - } else { - /// From GPS, but time can be old and have drifted - positionPacket.locationSource = Position.LocSource.locManual - } + if lastLocation == CLLocation(latitude: 0, longitude: 0) { + return nil + } + positionPacket.latitudeI = Int32(lastLocation.coordinate.latitude * 1e7) + positionPacket.longitudeI = Int32(lastLocation.coordinate.longitude * 1e7) + let timestamp = lastLocation.timestamp + positionPacket.time = UInt32(timestamp.timeIntervalSince1970) + positionPacket.timestamp = UInt32(timestamp.timeIntervalSince1970) + positionPacket.altitude = Int32(lastLocation.altitude) + positionPacket.satsInView = UInt32(LocationsHandler.satsInView) + let currentSpeed = lastLocation.speed + if currentSpeed > 0 && (!currentSpeed.isNaN || !currentSpeed.isInfinite) { + positionPacket.groundSpeed = UInt32(currentSpeed) + } + let currentHeading = lastLocation.course + if (currentHeading > 0 && currentHeading <= 360) && (!currentHeading.isNaN || !currentHeading.isInfinite) { + positionPacket.groundTrack = UInt32(currentHeading) + } + /// Set location source for time + if !fixedPosition { + /// From GPS treat time as good + positionPacket.locationSource = Position.LocSource.locExternal } else { - - positionPacket.latitudeI = Int32(LocationHelper.currentLocation.latitude * 1e7) - positionPacket.longitudeI = Int32(LocationHelper.currentLocation.longitude * 1e7) - let timestamp = LocationHelper.shared.locationManager.location?.timestamp ?? Date() - positionPacket.time = UInt32(timestamp.timeIntervalSince1970) - positionPacket.timestamp = UInt32(timestamp.timeIntervalSince1970) - positionPacket.altitude = Int32(LocationHelper.shared.locationManager.location?.altitude ?? 0) - positionPacket.satsInView = UInt32(LocationHelper.satsInView) - let currentSpeed = LocationHelper.shared.locationManager.location?.speed ?? 0 - if currentSpeed > 0 && (!currentSpeed.isNaN || !currentSpeed.isInfinite) { - positionPacket.groundSpeed = UInt32(currentSpeed) - } - let currentHeading = LocationHelper.shared.locationManager.location?.course ?? 0 - if (currentHeading > 0 && currentHeading <= 360) && (!currentHeading.isNaN || !currentHeading.isInfinite) { - positionPacket.groundTrack = UInt32(currentHeading) - } - /// Set location source for time - if !fixedPosition { - /// From GPS treat time as good - positionPacket.locationSource = Position.LocSource.locExternal - } else { - /// From GPS, but time can be old and have drifted - positionPacket.locationSource = Position.LocSource.locManual - } + /// From GPS, but time can be old and have drifted + positionPacket.locationSource = Position.LocSource.locManual } return positionPacket } diff --git a/Meshtastic/MeshtasticApp.swift b/Meshtastic/MeshtasticApp.swift index 047a8cc9..2c5a55ee 100644 --- a/Meshtastic/MeshtasticApp.swift +++ b/Meshtastic/MeshtasticApp.swift @@ -5,7 +5,6 @@ import CoreData import OSLog import TipKit -@available(iOS 17.0, *) @main struct MeshtasticAppleApp: App { @@ -109,23 +108,21 @@ struct MeshtasticAppleApp: App { } }) .task { - if #available(iOS 17.0, macOS 14.0, *) { - #if DEBUG - /// Optionally, call `Tips.resetDatastore()` before `Tips.configure()` to reset the state of all tips. This will allow tips to re-appear even after they have been dismissed by the user. - /// This is for testing only, and should not be enabled in release builds. - try? Tips.resetDatastore() - #endif + #if DEBUG + /// Optionally, call `Tips.resetDatastore()` before `Tips.configure()` to reset the state of all tips. This will allow tips to re-appear even after they have been dismissed by the user. + /// This is for testing only, and should not be enabled in release builds. + try? Tips.resetDatastore() + #endif - try? Tips.configure( - [ - // Reset which tips have been shown and what parameters have been tracked, useful during testing and for this sample project - .datastoreLocation(.applicationDefault), - // When should the tips be presented? If you use .immediate, they'll all be presented whenever a screen with a tip appears. - // You can adjust this on per tip level as well - .displayFrequency(.immediate) - ] - ) - } + try? Tips.configure( + [ + // Reset which tips have been shown and what parameters have been tracked, useful during testing and for this sample project + .datastoreLocation(.applicationDefault), + // When should the tips be presented? If you use .immediate, they'll all be presented whenever a screen with a tip appears. + // You can adjust this on per tip level as well + .displayFrequency(.immediate) + ] + ) } } .onChange(of: scenePhase) { (newScenePhase) in diff --git a/Meshtastic/MeshtasticAppDelegate.swift b/Meshtastic/MeshtasticAppDelegate.swift index d38557bc..87226052 100644 --- a/Meshtastic/MeshtasticAppDelegate.swift +++ b/Meshtastic/MeshtasticAppDelegate.swift @@ -9,9 +9,9 @@ import SwiftUI import OSLog class MeshtasticAppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate, ObservableObject { - + var router: Router? - + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { Logger.services.info("🚀 [App] Meshtstic Apple App launched!") // Default User Default Values @@ -19,14 +19,11 @@ class MeshtasticAppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificat UserDefaults.standard.register(defaults: ["meshMapShowNodeHistory": true]) UserDefaults.standard.register(defaults: ["meshMapShowRouteLines": true]) UNUserNotificationCenter.current().delegate = self - if #available(iOS 17.0, macOS 14.0, *) { - let locationsHandler = LocationsHandler.shared - locationsHandler.startLocationUpdates() - - // If a background activity session was previously active, reinstantiate it after the background launch. - if locationsHandler.backgroundActivity { - locationsHandler.backgroundActivity = true - } + let locationsHandler = LocationsHandler.shared + locationsHandler.startLocationUpdates() + // If a background activity session was previously active, reinstantiate it after the background launch. + if locationsHandler.backgroundActivity { + locationsHandler.backgroundActivity = true } return true } @@ -38,7 +35,7 @@ class MeshtasticAppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificat ) { completionHandler([.list, .banner, .sound]) } - + // This method is called when a user clicks on the notification func userNotificationCenter( _ center: UNUserNotificationCenter, @@ -46,11 +43,10 @@ class MeshtasticAppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificat withCompletionHandler completionHandler: @escaping () -> Void ) { let userInfo = response.notification.request.content.userInfo - + switch response.actionIdentifier { case UNNotificationDefaultActionIdentifier: break - case "messageNotification.thumbsUpAction": if let channel = userInfo["channel"] as? Int32, let replyID = userInfo["messageId"] as? Int64 { @@ -66,7 +62,6 @@ class MeshtasticAppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificat Logger.services.error("Failed to retrieve channel or messageId from userInfo") } break - case "messageNotification.thumbsDownAction": if let channel = userInfo["channel"] as? Int32, let replyID = userInfo["messageId"] as? Int64 { @@ -82,7 +77,6 @@ class MeshtasticAppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificat Logger.services.error("Failed to retrieve channel or messageId from userInfo") } break - case "messageNotification.replyInputAction": if let userInput = (response as? UNTextInputNotificationResponse)?.userText, let channel = userInfo["channel"] as? Int32, @@ -99,11 +93,10 @@ class MeshtasticAppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificat Logger.services.error("Failed to retrieve user input, channel, or messageId from userInfo") } break - default: break } - + if let targetValue = userInfo["target"] as? String, let deepLink = userInfo["path"] as? String, let url = URL(string: deepLink) { @@ -112,7 +105,6 @@ class MeshtasticAppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificat } else { Logger.services.error("Failed to handle notification response: \(userInfo)") } - completionHandler() } } diff --git a/Meshtastic/Views/Bluetooth/Connect.swift b/Meshtastic/Views/Bluetooth/Connect.swift index 90ec1de9..5bda12df 100644 --- a/Meshtastic/Views/Bluetooth/Connect.swift +++ b/Meshtastic/Views/Bluetooth/Connect.swift @@ -47,9 +47,7 @@ struct Connect: View { if bleManager.isSwitchedOn { Section(header: Text("connected.radio").font(.title)) { if let connectedPeripheral = bleManager.connectedPeripheral, connectedPeripheral.peripheral.state == .connected { - if #available(iOS 17.0, macOS 14.0, *) { - TipView(BluetoothConnectionTip(), arrowEdge: .bottom) - } + TipView(BluetoothConnectionTip(), arrowEdge: .bottom) VStack(alignment: .leading) { HStack { VStack(alignment: .center) { @@ -76,12 +74,10 @@ struct Connect: View { .foregroundColor(.green) } else { HStack { - if #available(iOS 17.0, macOS 14.0, *) { - Image(systemName: "square.stack.3d.down.forward") - .symbolRenderingMode(.multicolor) - .symbolEffect(.variableColor.reversing.cumulative, options: .repeat(20).speed(3)) - .foregroundColor(.orange) - } + Image(systemName: "square.stack.3d.down.forward") + .symbolRenderingMode(.multicolor) + .symbolEffect(.variableColor.reversing.cumulative, options: .repeat(20).speed(3)) + .foregroundColor(.orange) Text("communicating").font(.callout) .foregroundColor(.orange) } diff --git a/Meshtastic/Views/ContentView.swift b/Meshtastic/Views/ContentView.swift index 23b25f0c..cb5032ee 100644 --- a/Meshtastic/Views/ContentView.swift +++ b/Meshtastic/Views/ContentView.swift @@ -4,7 +4,6 @@ import SwiftUI -@available(iOS 17.0, *) struct ContentView: View { @ObservedObject var appState: AppState @@ -39,20 +38,12 @@ struct ContentView: View { } .tag(NavigationState.Tab.nodes) - if #available(iOS 17.0, macOS 14.0, *), !UserDefaults.mapUseLegacy { - MeshMap(router: appState.router) - .tabItem { - Label("map", systemImage: "map") - } - .tag(NavigationState.Tab.map) - } else { - NodeMap(router: appState.router) - .tabItem { - Label("map", systemImage: "map") - } - .tag(NavigationState.Tab.map) - } - + MeshMap(router: appState.router) + .tabItem { + Label("map", systemImage: "map") + } + .tag(NavigationState.Tab.map) + Settings( router: appState.router ) diff --git a/Meshtastic/Views/MapKitMap/NodeMapMapkit.swift b/Meshtastic/Views/MapKitMap/NodeMapMapkit.swift index 59c0e9cd..f9593585 100644 --- a/Meshtastic/Views/MapKitMap/NodeMapMapkit.swift +++ b/Meshtastic/Views/MapKitMap/NodeMapMapkit.swift @@ -1,164 +1,164 @@ +//// +//// NodeMapControl.swift +//// Meshtastic +//// +//// Created by Garth Vander Houwen on 9/9/23. +//// +//import SwiftUI +//import CoreLocation +//import MapKit +//import WeatherKit +//import OSLog // -// NodeMapControl.swift -// Meshtastic +//struct NodeMapMapkit: View { // -// Created by Garth Vander Houwen on 9/9/23. +// @Environment(\.managedObjectContext) var context +// @EnvironmentObject var bleManager: BLEManager +// /// Weather +// /// The current weather condition for the city. +// @State private var condition: WeatherCondition? +// @State private var temperature: Measurement? +// @State private var humidity: Int? +// @State private var symbolName: String = "cloud.fill" +// @State private var attributionLink: URL? +// @State private var attributionLogo: URL? // -import SwiftUI -import CoreLocation -import MapKit -import WeatherKit -import OSLog - -struct NodeMapMapkit: View { - - @Environment(\.managedObjectContext) var context - @EnvironmentObject var bleManager: BLEManager - /// Weather - /// The current weather condition for the city. - @State private var condition: WeatherCondition? - @State private var temperature: Measurement? - @State private var humidity: Int? - @State private var symbolName: String = "cloud.fill" - @State private var attributionLink: URL? - @State private var attributionLogo: URL? - - @Environment(\.colorScheme) var colorScheme: ColorScheme - @AppStorage("meshMapType") private var meshMapType = 0 - @AppStorage("meshMapShowNodeHistory") private var meshMapShowNodeHistory = false - @AppStorage("meshMapShowRouteLines") private var meshMapShowRouteLines = false - @State private var selectedMapLayer: MapLayer = .standard - @State var waypointCoordinate: WaypointCoordinate? - @State var editingWaypoint: Int = 0 - @State private var customMapOverlay: MapViewSwiftUI.CustomMapOverlay? = MapViewSwiftUI.CustomMapOverlay( - mapName: "offlinemap", - tileType: "png", - canReplaceMapContent: true - ) - @FetchRequest(sortDescriptors: [NSSortDescriptor(key: "name", ascending: false)], - predicate: NSPredicate( - format: "expire == nil || expire >= %@", Date() as NSDate - ), animation: .none) - private var waypoints: FetchedResults - @ObservedObject var node: NodeInfoEntity - - var body: some View { - - NavigationStack { - GeometryReader { bounds in - VStack { - if node.hasPositions { - ZStack { - let positionArray = node.positions?.array as? [PositionEntity] ?? [] - let lastTenThousand = Array(positionArray.prefix(10000)) - // let todaysPositions = positionArray.filter { $0.time! >= Calendar.current.startOfDay(for: Date()) } - ZStack { - MapViewSwiftUI(onLongPress: { coord in - waypointCoordinate = WaypointCoordinate(id: .init(), coordinate: coord, waypointId: 0) - }, onWaypointEdit: { wpId in - if wpId > 0 { - waypointCoordinate = WaypointCoordinate(id: .init(), coordinate: nil, waypointId: Int64(wpId)) - } - }, - selectedMapLayer: selectedMapLayer, - positions: lastTenThousand, - waypoints: Array(waypoints), - userTrackingMode: MKUserTrackingMode.none, - showNodeHistory: meshMapShowNodeHistory, - showRouteLines: meshMapShowRouteLines, - customMapOverlay: self.customMapOverlay - ) - VStack(alignment: .leading) { - Spacer() - HStack(alignment: .bottom, spacing: 1) { - Picker("Map Type", selection: $selectedMapLayer) { - ForEach(MapLayer.allCases, id: \.self) { layer in - if layer == MapLayer.offline && UserDefaults.enableOfflineMaps { - Text(layer.localized) - } else if layer != MapLayer.offline { - Text(layer.localized) - } - } - } - .onChange(of: (selectedMapLayer)) { newMapLayer in - UserDefaults.mapLayer = newMapLayer - } - .background(.thinMaterial, in: RoundedRectangle(cornerRadius: 12, style: .continuous)) - .pickerStyle(.menu) - .padding(5) - VStack { - VStack { - Label(temperature?.formatted(.measurement(width: .narrow)) ?? "??", systemImage: symbolName) - .font(.caption) - - Label("\(humidity ?? 0)%", systemImage: "humidity") - .font(.caption2) - - AsyncImage(url: attributionLogo) { image in - image - .resizable() - .scaledToFit() - } placeholder: { - ProgressView() - .controlSize(.mini) - } - .frame(height: 10) - - Link("Other data sources", destination: attributionLink ?? URL(string: "https://weather-data.apple.com/legal-attribution.html")!) - .font(.caption2) - } - .padding(5) - - } - .background(.thinMaterial, in: RoundedRectangle(cornerRadius: 12, style: .continuous)) - .padding(5) - .task { - do { - if node.hasPositions { - let mostRecent = node.positions?.lastObject as? PositionEntity - let weather = try await WeatherService.shared.weather(for: mostRecent?.nodeLocation ?? CLLocation(latitude: LocationHelper.currentLocation.latitude, longitude: LocationHelper.currentLocation.longitude)) - condition = weather.currentWeather.condition - temperature = weather.currentWeather.temperature - humidity = Int(weather.currentWeather.humidity * 100) - symbolName = weather.currentWeather.symbolName - let attribution = try await WeatherService.shared.attribution - attributionLink = attribution.legalPageURL - attributionLogo = colorScheme == .light ? attribution.combinedMarkLightURL : attribution.combinedMarkDarkURL - } - } catch { - Logger.services.error("Could not gather weather information: \(error.localizedDescription)") - condition = .clear - symbolName = "cloud.fill" - } - } - } - } - } - .ignoresSafeArea(.all, edges: [.top, .leading, .trailing]) - .frame(idealWidth: bounds.size.width, minHeight: bounds.size.height / 1.65) - } - } else { - HStack { - } - .padding([.top], 20) - } - } - .edgesIgnoringSafeArea([.leading, .trailing]) - .sheet(item: $waypointCoordinate, content: { wpc in - WaypointFormMapKit(coordinate: wpc) - .presentationDetents([.medium, .large]) - .presentationDragIndicator(.automatic) - }) - .navigationBarTitle(String(node.user?.longName ?? "unknown".localized), displayMode: .inline) - .navigationBarItems(trailing: - ZStack { - ConnectedDevice( - bluetoothOn: bleManager.isSwitchedOn, - deviceConnected: bleManager.connectedPeripheral != nil, - name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?") - }) - } - .padding(.bottom, 2) - } - } -} +// @Environment(\.colorScheme) var colorScheme: ColorScheme +// @AppStorage("meshMapType") private var meshMapType = 0 +// @AppStorage("meshMapShowNodeHistory") private var meshMapShowNodeHistory = false +// @AppStorage("meshMapShowRouteLines") private var meshMapShowRouteLines = false +// @State private var selectedMapLayer: MapLayer = .standard +// @State var waypointCoordinate: WaypointCoordinate? +// @State var editingWaypoint: Int = 0 +// @State private var customMapOverlay: MapViewSwiftUI.CustomMapOverlay? = MapViewSwiftUI.CustomMapOverlay( +// mapName: "offlinemap", +// tileType: "png", +// canReplaceMapContent: true +// ) +// @FetchRequest(sortDescriptors: [NSSortDescriptor(key: "name", ascending: false)], +// predicate: NSPredicate( +// format: "expire == nil || expire >= %@", Date() as NSDate +// ), animation: .none) +// private var waypoints: FetchedResults +// @ObservedObject var node: NodeInfoEntity +// +// var body: some View { +// +// NavigationStack { +// GeometryReader { bounds in +// VStack { +// if node.hasPositions { +// ZStack { +// let positionArray = node.positions?.array as? [PositionEntity] ?? [] +// let lastTenThousand = Array(positionArray.prefix(10000)) +// // let todaysPositions = positionArray.filter { $0.time! >= Calendar.current.startOfDay(for: Date()) } +// ZStack { +// MapViewSwiftUI(onLongPress: { coord in +// waypointCoordinate = WaypointCoordinate(id: .init(), coordinate: coord, waypointId: 0) +// }, onWaypointEdit: { wpId in +// if wpId > 0 { +// waypointCoordinate = WaypointCoordinate(id: .init(), coordinate: nil, waypointId: Int64(wpId)) +// } +// }, +// selectedMapLayer: selectedMapLayer, +// positions: lastTenThousand, +// waypoints: Array(waypoints), +// userTrackingMode: MKUserTrackingMode.none, +// showNodeHistory: meshMapShowNodeHistory, +// showRouteLines: meshMapShowRouteLines, +// customMapOverlay: self.customMapOverlay +// ) +// VStack(alignment: .leading) { +// Spacer() +// HStack(alignment: .bottom, spacing: 1) { +// Picker("Map Type", selection: $selectedMapLayer) { +// ForEach(MapLayer.allCases, id: \.self) { layer in +// if layer == MapLayer.offline && UserDefaults.enableOfflineMaps { +// Text(layer.localized) +// } else if layer != MapLayer.offline { +// Text(layer.localized) +// } +// } +// } +// .onChange(of: (selectedMapLayer)) { newMapLayer in +// UserDefaults.mapLayer = newMapLayer +// } +// .background(.thinMaterial, in: RoundedRectangle(cornerRadius: 12, style: .continuous)) +// .pickerStyle(.menu) +// .padding(5) +// VStack { +// VStack { +// Label(temperature?.formatted(.measurement(width: .narrow)) ?? "??", systemImage: symbolName) +// .font(.caption) +// +// Label("\(humidity ?? 0)%", systemImage: "humidity") +// .font(.caption2) +// +// AsyncImage(url: attributionLogo) { image in +// image +// .resizable() +// .scaledToFit() +// } placeholder: { +// ProgressView() +// .controlSize(.mini) +// } +// .frame(height: 10) +// +// Link("Other data sources", destination: attributionLink ?? URL(string: "https://weather-data.apple.com/legal-attribution.html")!) +// .font(.caption2) +// } +// .padding(5) +// +// } +// .background(.thinMaterial, in: RoundedRectangle(cornerRadius: 12, style: .continuous)) +// .padding(5) +// .task { +// do { +// if node.hasPositions { +// let mostRecent = node.positions?.lastObject as? PositionEntity +// let weather = try await WeatherService.shared.weather(for: mostRecent?.nodeLocation ?? CLLocation(latitude: LocationHelper.currentLocation.latitude, longitude: LocationHelper.currentLocation.longitude)) +// condition = weather.currentWeather.condition +// temperature = weather.currentWeather.temperature +// humidity = Int(weather.currentWeather.humidity * 100) +// symbolName = weather.currentWeather.symbolName +// let attribution = try await WeatherService.shared.attribution +// attributionLink = attribution.legalPageURL +// attributionLogo = colorScheme == .light ? attribution.combinedMarkLightURL : attribution.combinedMarkDarkURL +// } +// } catch { +// Logger.services.error("Could not gather weather information: \(error.localizedDescription)") +// condition = .clear +// symbolName = "cloud.fill" +// } +// } +// } +// } +// } +// .ignoresSafeArea(.all, edges: [.top, .leading, .trailing]) +// .frame(idealWidth: bounds.size.width, minHeight: bounds.size.height / 1.65) +// } +// } else { +// HStack { +// } +// .padding([.top], 20) +// } +// } +// .edgesIgnoringSafeArea([.leading, .trailing]) +// .sheet(item: $waypointCoordinate, content: { wpc in +// WaypointFormMapKit(coordinate: wpc) +// .presentationDetents([.medium, .large]) +// .presentationDragIndicator(.automatic) +// }) +// .navigationBarTitle(String(node.user?.longName ?? "unknown".localized), displayMode: .inline) +// .navigationBarItems(trailing: +// ZStack { +// ConnectedDevice( +// bluetoothOn: bleManager.isSwitchedOn, +// deviceConnected: bleManager.connectedPeripheral != nil, +// name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?") +// }) +// } +// .padding(.bottom, 2) +// } +// } +//} diff --git a/Meshtastic/Views/Messages/MessageText.swift b/Meshtastic/Views/Messages/MessageText.swift index 411511fd..df5b1f3d 100644 --- a/Meshtastic/Views/Messages/MessageText.swift +++ b/Meshtastic/Views/Messages/MessageText.swift @@ -47,23 +47,14 @@ struct MessageText: View { let isDetectionSensorMessage = message.portNum == Int32(PortNum.detectionSensorApp.rawValue) if tapBackDestination.overlaySensorMessage { VStack { - if #available(iOS 17.0, macOS 14.0, *) { - isDetectionSensorMessage ? Image(systemName: "sensor.fill") - .padding() - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topTrailing) - .foregroundStyle(Color.orange) - .symbolRenderingMode(.multicolor) - .symbolEffect(.variableColor.reversing.cumulative, options: .repeat(20).speed(3)) - .offset(x: 20, y: -20) - : nil - } else { - isDetectionSensorMessage ? Image(systemName: "sensor.fill") - .padding() - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topTrailing) - .foregroundStyle(Color.orange) - .offset(x: 20, y: -20) - : nil - } + isDetectionSensorMessage ? Image(systemName: "sensor.fill") + .padding() + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topTrailing) + .foregroundStyle(Color.orange) + .symbolRenderingMode(.multicolor) + .symbolEffect(.variableColor.reversing.cumulative, options: .repeat(20).speed(3)) + .offset(x: 20, y: -20) + : nil } } else { EmptyView() diff --git a/Meshtastic/Views/Messages/Messages.swift b/Meshtastic/Views/Messages/Messages.swift index 4b347ca0..9785c7bf 100644 --- a/Meshtastic/Views/Messages/Messages.swift +++ b/Meshtastic/Views/Messages/Messages.swift @@ -63,9 +63,7 @@ struct Messages: View { } } - if #available(iOS 17.0, macOS 14.0, *) { - TipView(MessagesTip(), arrowEdge: .top) - } + TipView(MessagesTip(), arrowEdge: .top) } .navigationTitle("messages") .navigationBarTitleDisplayMode(.large) diff --git a/Meshtastic/Views/Nodes/DeviceMetricsLog.swift b/Meshtastic/Views/Nodes/DeviceMetricsLog.swift index 63e17424..9f30efc4 100644 --- a/Meshtastic/Views/Nodes/DeviceMetricsLog.swift +++ b/Meshtastic/Views/Nodes/DeviceMetricsLog.swift @@ -36,32 +36,31 @@ struct DeviceMetricsLog: View { .sorted { $0.time! < $1.time! } if chartData.count > 0 { GroupBox(label: Label("\(deviceMetrics.count) Readings Total", systemImage: "chart.xyaxis.line")) { - if #available(iOS 17.0, macOS 14.0, *) { - Chart { - ForEach(chartData, id: \.self) { point in - Plot { - LineMark( - x: .value("x", point.time!), - y: .value("y", point.batteryLevel) - ) - } - .accessibilityLabel("Line Series") - .accessibilityValue("X: \(point.time!), Y: \(point.batteryLevel)") - .foregroundStyle(batteryChartColor) - .interpolationMethod(.linear) - Plot { - PointMark( - x: .value("x", point.time!), - y: .value("y", point.channelUtilization) - ) - .symbolSize(25) - } - .accessibilityLabel("Line Series") - .accessibilityValue("X: \(point.time!), Y: \(point.channelUtilization)") - .foregroundStyle(channelUtilizationChartColor) - if let chartSelection { - RuleMark(x: .value("Second", chartSelection, unit: .second)) - .foregroundStyle(.tertiary.opacity(0.5)) + Chart { + ForEach(chartData, id: \.self) { point in + Plot { + LineMark( + x: .value("x", point.time!), + y: .value("y", point.batteryLevel) + ) + } + .accessibilityLabel("Line Series") + .accessibilityValue("X: \(point.time!), Y: \(point.batteryLevel)") + .foregroundStyle(batteryChartColor) + .interpolationMethod(.linear) + Plot { + PointMark( + x: .value("x", point.time!), + y: .value("y", point.channelUtilization) + ) + .symbolSize(25) + } + .accessibilityLabel("Line Series") + .accessibilityValue("X: \(point.time!), Y: \(point.channelUtilization)") + .foregroundStyle(channelUtilizationChartColor) + if let chartSelection { + RuleMark(x: .value("Second", chartSelection, unit: .second)) + .foregroundStyle(.tertiary.opacity(0.5)) // .annotation( // position: .automatic, // overflowResolution: .init(x: .fit, y: .disabled) @@ -75,91 +74,37 @@ struct DeviceMetricsLog: View { // .foregroundStyle(Color.accentColor.opacity(0.2)) // } // } - } - RuleMark(y: .value("Network Status Orange", 25)) - .lineStyle(StrokeStyle(lineWidth: 1, dash: [5, 10])) - .foregroundStyle(.orange) - RuleMark(y: .value("Network Status Red", 50)) - .lineStyle(StrokeStyle(lineWidth: 1, dash: [5, 10])) - .foregroundStyle(.red) - Plot { - PointMark( - x: .value("x", point.time!), - y: .value("y", point.airUtilTx) - ) - .symbolSize(25) - } - .accessibilityLabel("Line Series") - .accessibilityValue("X: \(point.time!), Y: \(point.airUtilTx)") - .foregroundStyle(airtimeChartColor) } - } - .chartXAxis(content: { - AxisMarks(position: .top) - }) - .chartXAxis(.automatic) - .chartXSelection(value: $chartSelection) - .chartYScale(domain: 0...100) - .chartForegroundStyleScale([ - idiom == .phone ? "Battery" : "Battery Level": batteryChartColor, - "Channel Utilization": channelUtilizationChartColor, - "Airtime": airtimeChartColor - ]) - .chartLegend(position: .automatic, alignment: .bottom) - } else { - // Fallback on earlier versions - Chart { - ForEach(chartData, id: \.self) { point in - Plot { - LineMark( - x: .value("x", point.time!), - y: .value("y", point.batteryLevel) - ) - } - .accessibilityLabel("Line Series") - .accessibilityValue("X: \(point.time!), Y: \(point.batteryLevel)") - .foregroundStyle(batteryChartColor) - .interpolationMethod(.linear) - Plot { - PointMark( - x: .value("x", point.time!), - y: .value("y", point.channelUtilization) - ) - .symbolSize(25) - } - .accessibilityLabel("Line Series") - .accessibilityValue("X: \(point.time!), Y: \(point.channelUtilization)") - .foregroundStyle(channelUtilizationChartColor) - RuleMark(y: .value("Network Status Orange", 25)) - .lineStyle(StrokeStyle(lineWidth: 1, dash: [5, 10])) - .foregroundStyle(.orange) - RuleMark(y: .value("Network Status Red", 50)) - .lineStyle(StrokeStyle(lineWidth: 1, dash: [5, 10])) - .foregroundStyle(.red) - Plot { - PointMark( - x: .value("x", point.time!), - y: .value("y", point.airUtilTx) - ) - .symbolSize(25) - } - .accessibilityLabel("Line Series") - .accessibilityValue("X: \(point.time!), Y: \(point.airUtilTx)") - .foregroundStyle(airtimeChartColor) + RuleMark(y: .value("Network Status Orange", 25)) + .lineStyle(StrokeStyle(lineWidth: 1, dash: [5, 10])) + .foregroundStyle(.orange) + RuleMark(y: .value("Network Status Red", 50)) + .lineStyle(StrokeStyle(lineWidth: 1, dash: [5, 10])) + .foregroundStyle(.red) + Plot { + PointMark( + x: .value("x", point.time!), + y: .value("y", point.airUtilTx) + ) + .symbolSize(25) } + .accessibilityLabel("Line Series") + .accessibilityValue("X: \(point.time!), Y: \(point.airUtilTx)") + .foregroundStyle(airtimeChartColor) } - .chartXAxis(content: { - AxisMarks(position: .top) - }) - .chartXAxis(.automatic) - .chartYScale(domain: 0...100) - .chartForegroundStyleScale([ - idiom == .phone ? "Battery" : "Battery Level": batteryChartColor, - "Channel Utilization": channelUtilizationChartColor, - "Airtime": airtimeChartColor - ]) - .chartLegend(position: .automatic, alignment: .bottom) } + .chartXAxis(content: { + AxisMarks(position: .top) + }) + .chartXAxis(.automatic) + .chartXSelection(value: $chartSelection) + .chartYScale(domain: 0...100) + .chartForegroundStyleScale([ + idiom == .phone ? "Battery" : "Battery Level": batteryChartColor, + "Channel Utilization": channelUtilizationChartColor, + "Airtime": airtimeChartColor + ]) + .chartLegend(position: .automatic, alignment: .bottom) } .frame(minHeight: 240) } @@ -269,11 +214,7 @@ struct DeviceMetricsLog: View { chartSelection = metrics.time } } else { - if #available (iOS 17, *) { - ContentUnavailableView("No Device Metrics", systemImage: "slash.circle") - } else { - Text("No Device Metrics") - } + ContentUnavailableView("No Device Metrics", systemImage: "slash.circle") } } .navigationTitle("device.metrics.log") diff --git a/Meshtastic/Views/Nodes/EnvironmentMetricsLog.swift b/Meshtastic/Views/Nodes/EnvironmentMetricsLog.swift index 0cd35248..56b0c82c 100644 --- a/Meshtastic/Views/Nodes/EnvironmentMetricsLog.swift +++ b/Meshtastic/Views/Nodes/EnvironmentMetricsLog.swift @@ -193,11 +193,7 @@ struct EnvironmentMetricsLog: View { } } else { - if #available (iOS 17, *) { - ContentUnavailableView("No Environment Metrics", systemImage: "slash.circle") - } else { - Text("No Environment Metrics") - } + ContentUnavailableView("No Environment Metrics", systemImage: "slash.circle") } } diff --git a/Meshtastic/Views/Nodes/Helpers/Map/MapContent/MeshMapContent.swift b/Meshtastic/Views/Nodes/Helpers/Map/MapContent/MeshMapContent.swift index edfec3be..dd49484a 100644 --- a/Meshtastic/Views/Nodes/Helpers/Map/MapContent/MeshMapContent.swift +++ b/Meshtastic/Views/Nodes/Helpers/Map/MapContent/MeshMapContent.swift @@ -8,7 +8,6 @@ import SwiftUI import MapKit -@available(iOS 17.0, macOS 14.0, *) struct MeshMapContent: MapContent { /// Parameters diff --git a/Meshtastic/Views/Nodes/Helpers/Map/PositionPopover.swift b/Meshtastic/Views/Nodes/Helpers/Map/PositionPopover.swift index 35406707..88f61d76 100644 --- a/Meshtastic/Views/Nodes/Helpers/Map/PositionPopover.swift +++ b/Meshtastic/Views/Nodes/Helpers/Map/PositionPopover.swift @@ -214,20 +214,12 @@ struct PositionPopover: View { .padding(.bottom) } if position.nodePosition?.hasDetectionSensorMetrics ?? false { - if #available(iOS 17.0, macOS 14.0, *) { - Image(systemName: "sensor.fill") - .symbolEffect(.variableColor.reversing.cumulative, options: .repeat(20).speed(3)) - .symbolRenderingMode(.hierarchical) - .foregroundColor(.accentColor) - .font(.largeTitle) - .padding(.bottom) - } else { - Image(systemName: "sensor.fill") - .symbolRenderingMode(.hierarchical) - .foregroundColor(.accentColor) - .font(.largeTitle) - .padding(.bottom) - } + Image(systemName: "sensor.fill") + .symbolEffect(.variableColor.reversing.cumulative, options: .repeat(20).speed(3)) + .symbolRenderingMode(.hierarchical) + .foregroundColor(.accentColor) + .font(.largeTitle) + .padding(.bottom) } BatteryGauge(node: position.nodePosition!) } diff --git a/Meshtastic/Views/Nodes/Helpers/NodeDetail.swift b/Meshtastic/Views/Nodes/Helpers/NodeDetail.swift index 11defc79..a9f7bcfe 100644 --- a/Meshtastic/Views/Nodes/Helpers/NodeDetail.swift +++ b/Meshtastic/Views/Nodes/Helpers/NodeDetail.swift @@ -221,11 +221,7 @@ struct NodeDetail: View { .disabled(!node.hasDeviceMetrics) NavigationLink { - if #available (iOS 17, macOS 14, *) { - NodeMapSwiftUI(node: node, showUserLocation: connectedNode?.num ?? 0 == node.num) - } else { - NodeMapMapkit(node: node) - } + NodeMapSwiftUI(node: node, showUserLocation: connectedNode?.num ?? 0 == node.num) } label: { Label { Text("Node Map") @@ -260,19 +256,17 @@ struct NodeDetail: View { } .disabled(!node.hasEnvironmentMetrics) - if #available(iOS 17.0, macOS 14.0, *) { - NavigationLink { - TraceRouteLog(node: node) - } label: { - Label { - Text("Trace Route Log") - } icon: { - Image(systemName: "signpost.right.and.left") - .symbolRenderingMode(.multicolor) - } + NavigationLink { + TraceRouteLog(node: node) + } label: { + Label { + Text("Trace Route Log") + } icon: { + Image(systemName: "signpost.right.and.left") + .symbolRenderingMode(.multicolor) } - .disabled(node.traceRoutes?.count ?? 0 == 0) } + .disabled(node.traceRoutes?.count ?? 0 == 0) NavigationLink { DetectionSensorLog(node: node) diff --git a/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift b/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift index 481666a1..66f58a07 100644 --- a/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift +++ b/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift @@ -101,36 +101,9 @@ struct NodeListItem: View { if node.positions?.count ?? 0 > 0 && connectedNode != node.num { HStack { if let lastPostion = node.positions?.lastObject as? PositionEntity { - if #available(iOS 17.0, macOS 14.0, *) { - if let currentLocation = LocationsHandler.shared.locationsArray.last { - let myCoord = CLLocation(latitude: currentLocation.coordinate.latitude, longitude: currentLocation.coordinate.longitude) - if lastPostion.nodeCoordinate != nil && myCoord.coordinate.longitude != LocationsHandler.DefaultLocation.longitude && myCoord.coordinate.latitude != LocationsHandler.DefaultLocation.latitude { - let nodeCoord = CLLocation(latitude: lastPostion.nodeCoordinate!.latitude, longitude: lastPostion.nodeCoordinate!.longitude) - let metersAway = nodeCoord.distance(from: myCoord) - Image(systemName: "lines.measurement.horizontal") - .font(.callout) - .symbolRenderingMode(.multicolor) - .frame(width: 30) - DistanceText(meters: metersAway) - .font(UIDevice.current.userInterfaceIdiom == .phone ? .callout : .caption) - .foregroundColor(.gray) - let trueBearing = getBearingBetweenTwoPoints(point1: myCoord, point2: nodeCoord) - let headingDegrees = Angle.degrees(trueBearing) - Image(systemName: "location.north") - .font(.callout) - .symbolRenderingMode(.multicolor) - .clipShape(Circle()) - .rotationEffect(headingDegrees) - let heading = Measurement(value: trueBearing, unit: UnitAngle.degrees) - Text("\(heading.formatted(.measurement(width: .narrow, numberFormatStyle: .number.precision(.fractionLength(0)))))") - .font(UIDevice.current.userInterfaceIdiom == .phone ? .callout : .caption) - .foregroundColor(.gray) - } - } - } else { - - let myCoord = CLLocation(latitude: LocationHelper.currentLocation.latitude, longitude: LocationHelper.currentLocation.longitude) - if lastPostion.nodeCoordinate != nil && myCoord.coordinate.longitude != LocationHelper.DefaultLocation.longitude && myCoord.coordinate.latitude != LocationHelper.DefaultLocation.latitude { + if let currentLocation = LocationsHandler.shared.locationsArray.last { + let myCoord = CLLocation(latitude: currentLocation.coordinate.latitude, longitude: currentLocation.coordinate.longitude) + if lastPostion.nodeCoordinate != nil && myCoord.coordinate.longitude != LocationsHandler.DefaultLocation.longitude && myCoord.coordinate.latitude != LocationsHandler.DefaultLocation.latitude { let nodeCoord = CLLocation(latitude: lastPostion.nodeCoordinate!.latitude, longitude: lastPostion.nodeCoordinate!.longitude) let metersAway = nodeCoord.distance(from: myCoord) Image(systemName: "lines.measurement.horizontal") @@ -139,7 +112,7 @@ struct NodeListItem: View { .frame(width: 30) DistanceText(meters: metersAway) .font(UIDevice.current.userInterfaceIdiom == .phone ? .callout : .caption) - .foregroundColor(.secondary) + .foregroundColor(.gray) let trueBearing = getBearingBetweenTwoPoints(point1: myCoord, point2: nodeCoord) let headingDegrees = Angle.degrees(trueBearing) Image(systemName: "location.north") @@ -211,13 +184,11 @@ struct NodeListItem: View { .font(.callout) .frame(width: 30) } - if #available(iOS 17.0, macOS 14.0, *) { - if node.hasTraceRoutes { - Image(systemName: "signpost.right.and.left") - .symbolRenderingMode(.hierarchical) - .font(.callout) - .frame(width: 30) - } + if node.hasTraceRoutes { + Image(systemName: "signpost.right.and.left") + .symbolRenderingMode(.hierarchical) + .font(.callout) + .frame(width: 30) } } } diff --git a/Meshtastic/Views/Nodes/NodeList.swift b/Meshtastic/Views/Nodes/NodeList.swift index f81a34cf..65f194cf 100644 --- a/Meshtastic/Views/Nodes/NodeList.swift +++ b/Meshtastic/Views/Nodes/NodeList.swift @@ -274,18 +274,10 @@ struct NodeList: View { ) } } else { - if #available (iOS 17, *) { - ContentUnavailableView("select.node", systemImage: "flipphone") - } else { - Text("select.node") - } + ContentUnavailableView("select.node", systemImage: "flipphone") } } detail: { - if #available (iOS 17, *) { - ContentUnavailableView("", systemImage: "line.3.horizontal") - } else { - Text("Select something to view") - } + ContentUnavailableView("", systemImage: "line.3.horizontal") } .navigationSplitViewStyle(.balanced) .onChange(of: searchText) { _ in diff --git a/Meshtastic/Views/Nodes/PaxCounterLog.swift b/Meshtastic/Views/Nodes/PaxCounterLog.swift index 6d764c63..bb03aa9b 100644 --- a/Meshtastic/Views/Nodes/PaxCounterLog.swift +++ b/Meshtastic/Views/Nodes/PaxCounterLog.swift @@ -196,11 +196,7 @@ struct PaxCounterLog: View { .padding(.trailing) } } else { - if #available (iOS 17, *) { - ContentUnavailableView("paxcounter.content.unavailable", systemImage: "slash.circle") - } else { - Text("paxcounter.content.unavailable") - } + ContentUnavailableView("paxcounter.content.unavailable", systemImage: "slash.circle") } } .navigationTitle("paxcounter.log") diff --git a/Meshtastic/Views/Nodes/PositionLog.swift b/Meshtastic/Views/Nodes/PositionLog.swift index 43354830..3abcb791 100644 --- a/Meshtastic/Views/Nodes/PositionLog.swift +++ b/Meshtastic/Views/Nodes/PositionLog.swift @@ -166,11 +166,7 @@ struct PositionLog: View { ) } else { - if #available (iOS 17, *) { - ContentUnavailableView("No Positions", systemImage: "mappin.slash") - } else { - Text("No Positions") - } + ContentUnavailableView("No Positions", systemImage: "mappin.slash") } } .navigationTitle("Position Log \(node.positions?.count ?? 0) Points") diff --git a/Meshtastic/Views/Settings/AppData.swift b/Meshtastic/Views/Settings/AppData.swift index bed0a948..049c73cf 100644 --- a/Meshtastic/Views/Settings/AppData.swift +++ b/Meshtastic/Views/Settings/AppData.swift @@ -22,9 +22,7 @@ struct AppData: View { VStack { Section(header: Text("phone.gps")) { - if #available(iOS 17.0, macOS 14.0, *) { - GPSStatus() - } + GPSStatus() } Divider() Button(action: { diff --git a/Meshtastic/Views/Settings/Channels.swift b/Meshtastic/Views/Settings/Channels.swift index fbc9f75b..1a28a6be 100644 --- a/Meshtastic/Views/Settings/Channels.swift +++ b/Meshtastic/Views/Settings/Channels.swift @@ -60,9 +60,7 @@ struct Channels: View { VStack { List { - if #available(iOS 17.0, macOS 14.0, *) { - TipView(CreateChannelsTip(), arrowEdge: .bottom) - } + TipView(CreateChannelsTip(), arrowEdge: .bottom) if node != nil && node?.myInfo != nil { ForEach(node?.myInfo?.channels?.array as? [ChannelEntity] ?? [], id: \.self) { (channel: ChannelEntity) in Button(action: { diff --git a/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift b/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift index 6b39b649..c077c92e 100644 --- a/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift @@ -353,52 +353,50 @@ struct MQTTConfig: View { } func setMqttValues() { - if #available(iOS 17.0, macOS 14.0, *) { + nearbyTopics = [] + let geocoder = CLGeocoder() + if LocationsHandler.shared.locationsArray.count > 0 { + let region = RegionCodes(rawValue: Int(node?.loRaConfig?.regionCode ?? 0))?.topic + defaultTopic = "msh/" + (region ?? "UNSET") + geocoder.reverseGeocodeLocation(LocationsHandler.shared.locationsArray.first!, completionHandler: {(placemarks, error) in + if let error { + Logger.services.error("Failed to reverse geocode location: \(error.localizedDescription)") + return + } - nearbyTopics = [] - let geocoder = CLGeocoder() - if LocationsHandler.shared.locationsArray.count > 0 { - let region = RegionCodes(rawValue: Int(node?.loRaConfig?.regionCode ?? 0))?.topic - defaultTopic = "msh/" + (region ?? "UNSET") - geocoder.reverseGeocodeLocation(LocationsHandler.shared.locationsArray.first!, completionHandler: {(placemarks, error) in - if let error { - Logger.services.error("Failed to reverse geocode location: \(error.localizedDescription)") - return + if let placemarks = placemarks, let placemark = placemarks.first { + let cc = locale.region?.identifier ?? "UNK" + /// Country Topic unless you are US + if placemark.isoCountryCode ?? "unknown" != cc { + let countryTopic = defaultTopic + "/" + (placemark.isoCountryCode ?? "") + if !countryTopic.isEmpty { + nearbyTopics.append(countryTopic) + } } - - if let placemarks = placemarks, let placemark = placemarks.first { - let cc = locale.region?.identifier ?? "UNK" - /// Country Topic unless you are US - if placemark.isoCountryCode ?? "unknown" != cc { - let countryTopic = defaultTopic + "/" + (placemark.isoCountryCode ?? "") - if !countryTopic.isEmpty { - nearbyTopics.append(countryTopic) - } - } - let stateTopic = defaultTopic + "/" + (placemark.administrativeArea ?? "") - if !stateTopic.isEmpty { - nearbyTopics.append(stateTopic) - } - let countyTopic = defaultTopic + "/" + (placemark.administrativeArea ?? "") + "/" + (placemark.subAdministrativeArea?.lowercased().replacingOccurrences(of: " ", with: "") ?? "") - if !countyTopic.isEmpty { - nearbyTopics.append(countyTopic) - } - let cityTopic = defaultTopic + "/" + (placemark.administrativeArea ?? "") + "/" + (placemark.locality?.lowercased().replacingOccurrences(of: " ", with: "") ?? "") - if !cityTopic.isEmpty { - nearbyTopics.append(cityTopic) - } - let neightborhoodTopic = defaultTopic + "/" + (placemark.administrativeArea ?? "") + "/" + (placemark.subLocality?.lowercased() - .replacingOccurrences(of: " ", with: "") - .replacingOccurrences(of: "'", with: "") ?? "") - if !neightborhoodTopic.isEmpty { - nearbyTopics.append(neightborhoodTopic) - } - } else { - Logger.services.debug("No Location") + let stateTopic = defaultTopic + "/" + (placemark.administrativeArea ?? "") + if !stateTopic.isEmpty { + nearbyTopics.append(stateTopic) } - }) - } + let countyTopic = defaultTopic + "/" + (placemark.administrativeArea ?? "") + "/" + (placemark.subAdministrativeArea?.lowercased().replacingOccurrences(of: " ", with: "") ?? "") + if !countyTopic.isEmpty { + nearbyTopics.append(countyTopic) + } + let cityTopic = defaultTopic + "/" + (placemark.administrativeArea ?? "") + "/" + (placemark.locality?.lowercased().replacingOccurrences(of: " ", with: "") ?? "") + if !cityTopic.isEmpty { + nearbyTopics.append(cityTopic) + } + let neightborhoodTopic = defaultTopic + "/" + (placemark.administrativeArea ?? "") + "/" + (placemark.subLocality?.lowercased() + .replacingOccurrences(of: " ", with: "") + .replacingOccurrences(of: "'", with: "") ?? "") + if !neightborhoodTopic.isEmpty { + nearbyTopics.append(neightborhoodTopic) + } + } else { + Logger.services.debug("No Location") + } + }) } + self.enabled = node?.mqttConfig?.enabled ?? false self.proxyToClientEnabled = node?.mqttConfig?.proxyToClientEnabled ?? false self.address = node?.mqttConfig?.address ?? "" diff --git a/Meshtastic/Views/Settings/Settings.swift b/Meshtastic/Views/Settings/Settings.swift index 6176512f..52a78b0a 100644 --- a/Meshtastic/Views/Settings/Settings.swift +++ b/Meshtastic/Views/Settings/Settings.swift @@ -154,16 +154,14 @@ struct Settings: View { var moduleConfigurationSection: some View { Section("module.configuration") { - if #available(iOS 17.0, macOS 14.0, *) { - NavigationLink(value: SettingsNavigationState.ambientLighting) { - Label { - Text("ambient.lighting") - } icon: { - Image(systemName: "light.max") - } + NavigationLink(value: SettingsNavigationState.ambientLighting) { + Label { + Text("ambient.lighting") + } icon: { + Image(systemName: "light.max") } } - + NavigationLink(value: SettingsNavigationState.cannedMessages) { Label { Text("canned.messages") @@ -321,25 +319,24 @@ struct Settings: View { Image(systemName: "gearshape") } } - if #available(iOS 17.0, macOS 14.0, *) { - NavigationLink(value: SettingsNavigationState.routes) { - Label { - Text("routes") - } icon: { - Image(systemName: "road.lanes.curved.right") - } - } - - NavigationLink(value: SettingsNavigationState.routeRecorder) { - Label { - Text("route.recorder") - } icon: { - Image(systemName: "record.circle") - .foregroundColor(.red) - } + NavigationLink(value: SettingsNavigationState.routes) { + Label { + Text("routes") + } icon: { + Image(systemName: "road.lanes.curved.right") } } + NavigationLink(value: SettingsNavigationState.routeRecorder) { + Label { + Text("route.recorder") + } icon: { + Image(systemName: "record.circle") + .foregroundColor(.red) + } + } + + if !(node?.deviceConfig?.isManaged ?? false) { if bleManager.connectedPeripheral != nil { Section("Configure") { @@ -403,9 +400,7 @@ struct Settings: View { } } } - if #available(iOS 17.0, macOS 14.0, *) { - TipView(AdminChannelTip(), arrowEdge: .top) - } + TipView(AdminChannelTip(), arrowEdge: .top) } else { if bleManager.connectedPeripheral != nil { Text("Connected Node \(node?.user?.longName ?? "unknown".localized)") @@ -416,9 +411,7 @@ struct Settings: View { radioConfigurationSection deviceConfigurationSection moduleConfigurationSection - if #available (iOS 17.0, *) { - loggingSection - } + loggingSection #if DEBUG developersSection #endif @@ -433,13 +426,9 @@ struct Settings: View { case .appSettings: AppSettings() case .routes: - if #available(iOS 17.0, *) { - Routes() - } + Routes() case .routeRecorder: - if #available(iOS 17.0, *) { - RouteRecorder() - } + RouteRecorder() case .lora: LoRaConfig(node: nodes.first(where: { $0.num == selectedNode })) case .channels: @@ -461,9 +450,7 @@ struct Settings: View { case .power: PowerConfig(node: nodes.first(where: { $0.num == selectedNode })) case .ambientLighting: - if #available(iOS 17.0, macOS 14.0, *) { - AmbientLightingConfig(node: node) - } + AmbientLightingConfig(node: node) case .cannedMessages: CannedMessagesConfig(node: nodes.first(where: { $0.num == selectedNode })) case .detectionSensor: @@ -489,9 +476,7 @@ struct Settings: View { case .meshLog: MeshLog() case .debugLogs: - if #available(iOS 17.0, macOS 14.0, *) { - AppLog() - } + AppLog() case .appFiles: AppData() case .firmwareUpdates: diff --git a/Meshtastic/Views/Settings/ShareChannels.swift b/Meshtastic/Views/Settings/ShareChannels.swift index f4f4167a..e4b293a8 100644 --- a/Meshtastic/Views/Settings/ShareChannels.swift +++ b/Meshtastic/Views/Settings/ShareChannels.swift @@ -52,10 +52,8 @@ struct ShareChannels: View { var body: some View { - if #available(iOS 17.0, macOS 14.0, *) { - VStack { - TipView(ShareChannelsTip(), arrowEdge: .bottom) - } + VStack { + TipView(ShareChannelsTip(), arrowEdge: .bottom) } GeometryReader { bounds in let smallest = min(bounds.size.width, bounds.size.height)