diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 59a50868..39426e2f 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -56,7 +56,6 @@ B399E8A42B6F486400E4488E /* RetryButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B399E8A32B6F486400E4488E /* RetryButton.swift */; }; B3E905B12B71F7F300654D07 /* TextMessageField.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3E905B02B71F7F300654D07 /* TextMessageField.swift */; }; BC47C2EF2CE0017D008245CA /* MessageNodeIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC47C2EE2CE0017D008245CA /* MessageNodeIntent.swift */; }; - BC5EBA3C2D002A2000C442FF /* MessageNodeIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5EBA3B2D002A2000C442FF /* MessageNodeIntent.swift */; }; BC6B45FF2CB2F98900723CEB /* SaveChannelSettingsIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC6B45FE2CB2F98900723CEB /* SaveChannelSettingsIntent.swift */; }; BCB613812C67290800485544 /* SendWaypointIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCB613802C67290800485544 /* SendWaypointIntent.swift */; }; BCB613832C672A2600485544 /* MessageChannelIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCB613822C672A2600485544 /* MessageChannelIntent.swift */; }; @@ -1237,7 +1236,7 @@ attributes = { BuildIndependentTargetsInParallel = YES; LastSwiftUpdateCheck = 1540; - LastUpgradeCheck = 1600; + LastUpgradeCheck = 1630; TargetAttributes = { 25F5D5C62C4375A8008036E3 = { CreatedOnToolsVersion = 15.4; @@ -1482,7 +1481,6 @@ 251926872C3BAE2200249DF5 /* NodeAlertsButton.swift in Sources */, DDA1C48E28DB49D3009933EC /* ChannelRoles.swift in Sources */, DDD5BB092C285DDC007E03CA /* AppLog.swift in Sources */, - BC5EBA3C2D002A2000C442FF /* MessageNodeIntent.swift in Sources */, DD8ED9C8289CE4B900B3B0AB /* RoutingError.swift in Sources */, 233E99C52D84A0B600CC3A77 /* CompactWidget.swift in Sources */, DDC1B81A2AB5377B00C71E39 /* MessagesTips.swift in Sources */, @@ -1604,7 +1602,6 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = GCH7VS5Y9R; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; @@ -1628,7 +1625,6 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = GCH7VS5Y9R; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 17.0; @@ -1678,6 +1674,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = GCH7VS5Y9R; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; ENABLE_USER_SCRIPT_SANDBOXING = NO; @@ -1742,6 +1739,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = GCH7VS5Y9R; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_USER_SCRIPT_SANDBOXING = YES; @@ -1778,12 +1776,11 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"Meshtastic/Preview Content\""; - DEVELOPMENT_TEAM = GCH7VS5Y9R; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = Meshtastic/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = Meshtastic; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; - IPHONEOS_DEPLOYMENT_TARGET = 17.2; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1812,12 +1809,11 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"Meshtastic/Preview Content\""; - DEVELOPMENT_TEAM = GCH7VS5Y9R; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = Meshtastic/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = Meshtastic; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; - IPHONEOS_DEPLOYMENT_TARGET = 17.2; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1843,12 +1839,11 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = GCH7VS5Y9R; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Widgets/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = Widgets; INFOPLIST_KEY_NSHumanReadableCopyright = ""; - IPHONEOS_DEPLOYMENT_TARGET = 17.2; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1876,12 +1871,11 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = GCH7VS5Y9R; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Widgets/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = Widgets; INFOPLIST_KEY_NSHumanReadableCopyright = ""; - IPHONEOS_DEPLOYMENT_TARGET = 17.2; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/Meshtastic.xcodeproj/xcshareddata/xcschemes/Meshtastic.xcscheme b/Meshtastic.xcodeproj/xcshareddata/xcschemes/Meshtastic.xcscheme index 508a6cab..53ea5ca7 100644 --- a/Meshtastic.xcodeproj/xcshareddata/xcschemes/Meshtastic.xcscheme +++ b/Meshtastic.xcodeproj/xcshareddata/xcschemes/Meshtastic.xcscheme @@ -1,6 +1,6 @@ some IntentResult & ProvidesDialog { if !BLEManager.shared.isConnected { throw AppIntentErrors.AppIntentError.notConnected } - + let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest(entityName: "NodeInfoEntity") fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum)) - + do { guard let fetchedNode = try PersistenceController.shared.container.viewContext.fetch(fetchNodeInfoRequest) as? [NodeInfoEntity], fetchedNode.count == 1 else { throw $nodeNum.needsValueError("Could not find node") } - + let nodeInfo = fetchedNode[0] if let latitude = nodeInfo.latestPosition?.coordinate.latitude, let longitude = nodeInfo.latestPosition?.coordinate.longitude { - + let url = URL(string: "maps://?saddr=&daddr=\(latitude),\(longitude)") - + if let mapURL = url, UIApplication.shared.canOpenURL(mapURL) { // Request to continue in foreground before opening the app try await requestToContinueInForeground() - + // Open Apple Maps for navigation UIApplication.shared.open(mapURL, options: [:], completionHandler: nil) return .result(dialog: "Navigating to node location.") diff --git a/Meshtastic/AppIntents/SaveChannelSettingsIntent.swift b/Meshtastic/AppIntents/SaveChannelSettingsIntent.swift index 902a66cc..095112ff 100644 --- a/Meshtastic/AppIntents/SaveChannelSettingsIntent.swift +++ b/Meshtastic/AppIntents/SaveChannelSettingsIntent.swift @@ -29,12 +29,9 @@ struct SaveChannelSettingsIntent: AppIntent { if channelUrl.absoluteString.lowercased().contains("meshtastic.org/e/#") { // Split the URL to get the portion after "#" let components = channelUrl.absoluteString.components(separatedBy: "#") - // Add channels flag based on the URL query parameter (if present) let addChannels = Bool(channelUrl["add"] ?? "false") ?? false - var channelSettings: String? - // Extract the Base64 encoded channel settings (after "#") if let lastComponent = components.last { channelSettings = lastComponent.components(separatedBy: "?").first // Ignore any query parameters @@ -44,7 +41,6 @@ struct SaveChannelSettingsIntent: AppIntent { if let channelSettings = channelSettings { // Call the BLEManager to save the channel settings let saveResult = BLEManager.shared.saveChannelSet(base64UrlString: channelSettings, addChannels: addChannels) - if !saveResult { throw AppIntentErrors.AppIntentError.message("Failed to save the channel settings.") } diff --git a/Meshtastic/Export/CsvDocument.swift b/Meshtastic/Export/CsvDocument.swift index f4bb04ac..7133633e 100644 --- a/Meshtastic/Export/CsvDocument.swift +++ b/Meshtastic/Export/CsvDocument.swift @@ -20,13 +20,10 @@ struct CsvDocument: FileDocument { } init(configuration: ReadConfiguration) throws { - if let data = configuration.file.regularFileContents { - - csvData = String(decoding: data, as: UTF8.self) + csvData = String(data: data, encoding: .utf8) ?? "" } else { - throw CocoaError(.fileReadCorruptFile) } } diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index b9f2a7d8..b46e6a46 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -579,7 +579,6 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) { if let error { - Logger.services.error("🚫 [BLE] didUpdateValueFor Characteristic error \(error.localizedDescription, privacy: .public)") let errorCode = (error as NSError).code if errorCode == 5 || errorCode == 15 { @@ -633,14 +632,9 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate } catch { Logger.services.error("πŸ’₯ \(error.localizedDescription, privacy: .public) \(characteristic.value!, privacy: .public)") } - // Publish mqttClientProxyMessages received on the from radio if decodedInfo.payloadVariant == FromRadio.OneOf_PayloadVariant.mqttClientProxyMessage(decodedInfo.mqttClientProxyMessage) { - let message = CocoaMQTTMessage( - topic: decodedInfo.mqttClientProxyMessage.topic, - payload: [UInt8](decodedInfo.mqttClientProxyMessage.data), - retained: decodedInfo.mqttClientProxyMessage.retained - ) + let message = CocoaMQTTMessage(topic: decodedInfo.mqttClientProxyMessage.topic, payload: [UInt8](decodedInfo.mqttClientProxyMessage.data), retained: decodedInfo.mqttClientProxyMessage.retained) mqttManager.mqttClientProxy?.publish(message) } else if decodedInfo.payloadVariant == FromRadio.OneOf_PayloadVariant.clientNotification(decodedInfo.clientNotification) { if decodedInfo.clientNotification.hasReplyID { @@ -782,13 +776,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate adminAppPacket(packet: decodedInfo.packet, context: context) case .replyApp: Logger.mesh.info("πŸ•ΈοΈ MESH PACKET received for Reply App handling as a text message") - textMessageAppPacket( - packet: decodedInfo.packet, - wantRangeTestPackets: wantRangeTestPackets, - connectedNode: (self.connectedPeripheral != nil ? connectedPeripheral.num : 0), - context: context, - appState: appState - ) + textMessageAppPacket(packet: decodedInfo.packet, wantRangeTestPackets: wantRangeTestPackets, connectedNode: (self.connectedPeripheral != nil ? connectedPeripheral.num : 0), context: context, appState: appState) case .ipTunnelApp: Logger.mesh.info("πŸ•ΈοΈ MESH PACKET received for IP Tunnel App UNHANDLED UNHANDLED") case .serialApp: diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index 83f0b8ef..4b0aa7b4 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -686,20 +686,15 @@ func routingPacket (packet: MeshPacket, connectedNodeNum: Int64, context: NSMana func telemetryPacket(packet: MeshPacket, connectedNode: Int64, context: NSManagedObjectContext) { if let telemetryMessage = try? Telemetry(serializedBytes: packet.decoded.payload) { - let logString = String.localizedStringWithFormat("mesh.log.telemetry.received %@".localized, String(packet.from)) Logger.mesh.info("πŸ“ˆ \(logString, privacy: .public)") - if telemetryMessage.variant != Telemetry.OneOf_Variant.deviceMetrics(telemetryMessage.deviceMetrics) && telemetryMessage.variant != Telemetry.OneOf_Variant.environmentMetrics(telemetryMessage.environmentMetrics) && telemetryMessage.variant != Telemetry.OneOf_Variant.localStats(telemetryMessage.localStats) && telemetryMessage.variant != Telemetry.OneOf_Variant.powerMetrics(telemetryMessage.powerMetrics) { /// Other unhandled telemetry packets return } - let telemetry = TelemetryEntity(context: context) - let fetchNodeTelemetryRequest = NodeInfoEntity.fetchRequest() fetchNodeTelemetryRequest.predicate = NSPredicate(format: "num == %lld", Int64(packet.from)) - do { let fetchedNode = try context.fetch(fetchNodeTelemetryRequest) if fetchedNode.count == 1 { @@ -756,7 +751,6 @@ func telemetryPacket(packet: MeshPacket, connectedNode: Int64, context: NSManage Logger.statistics.info("πŸ“ˆ [Mesh Statistics] Channel Utilization: \(telemetryMessage.localStats.channelUtilization, privacy: .public) Airtime: \(telemetryMessage.localStats.airUtilTx, privacy: .public) Packets Sent: \(telemetryMessage.localStats.numPacketsTx, privacy: .public) Packets Received: \(telemetryMessage.localStats.numPacketsRx, privacy: .public) Bad Packets Received: \(telemetryMessage.localStats.numPacketsRxBad, privacy: .public) Nodes Online: \(telemetryMessage.localStats.numOnlineNodes, privacy: .public) of \(telemetryMessage.localStats.numTotalNodes, privacy: .public) nodes for Node: \(packet.from.toHex(), privacy: .public)") } else if telemetryMessage.variant == Telemetry.OneOf_Variant.powerMetrics(telemetryMessage.powerMetrics) { Logger.data.info("πŸ“ˆ [Power Metrics] Received for Node: \(packet.from.toHex(), privacy: .public)") - telemetry.powerCh1Voltage = telemetryMessage.powerMetrics.hasCh1Voltage.then(telemetryMessage.powerMetrics.ch1Voltage) telemetry.powerCh1Current = telemetryMessage.powerMetrics.hasCh1Current.then(telemetryMessage.powerMetrics.ch1Current) telemetry.powerCh2Voltage = telemetryMessage.powerMetrics.hasCh2Voltage.then(telemetryMessage.powerMetrics.ch2Voltage) @@ -764,7 +758,6 @@ func telemetryPacket(packet: MeshPacket, connectedNode: Int64, context: NSManage telemetry.powerCh3Voltage = telemetryMessage.powerMetrics.hasCh3Voltage.then(telemetryMessage.powerMetrics.ch3Voltage) telemetry.powerCh3Current = telemetryMessage.powerMetrics.hasCh3Current.then(telemetryMessage.powerMetrics.ch3Current) telemetry.metricsType = 2 - } telemetry.snr = packet.rxSnr telemetry.rssi = packet.rxRssi @@ -781,7 +774,6 @@ func telemetryPacket(packet: MeshPacket, connectedNode: Int64, context: NSManage fetchedNode[0].telemetries = mutableTelemetries.copy() as? NSOrderedSet } try context.save() - Logger.data.info("πŸ’Ύ [TelemetryEntity] of type \(MetricsTypes(rawValue: Int(telemetry.metricsType))?.name ?? "Unknown Metrics Type", privacy: .public) Saved for Node: \(packet.from.toHex(), privacy: .public)") if telemetry.metricsType == 0 { // Connected Device Metrics diff --git a/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift b/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift index 6dc1854b..435e162b 100644 --- a/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift +++ b/Meshtastic/Helpers/Mqtt/MqttClientProxyManager.swift @@ -43,18 +43,13 @@ class MqttClientProxyManager { let port = defaultServerPort let username = node.mqttConfig?.username let password = node.mqttConfig?.password - // if host == defaultServerAddress { - //username = ProcessInfo.processInfo.environment["PUBLIC_MQTT_USERNAME"] - //password = ProcessInfo.processInfo.environment["PUBLIC_MQTT_PASSWORD"] - // } let root = node.mqttConfig?.root?.count ?? 0 > 0 ? node.mqttConfig?.root : "msh" let prefix = root! topic = prefix + "/2/e" + "/#" - let qos = CocoaMQTTQoS(rawValue: UInt8(1))! - connect(host: host, port: port, useSsl: useSsl, username: username, password: password, topic: topic, qos: qos, cleanSession: true) + connect(host: host, port: port, useSsl: useSsl, username: username, password: password, topic: topic) } } - func connect(host: String, port: Int, useSsl: Bool, username: String?, password: String?, topic: String?, qos: CocoaMQTTQoS, cleanSession: Bool) { + func connect(host: String, port: Int, useSsl: Bool, username: String?, password: String?, topic: String?) { guard !host.isEmpty else { delegate?.onMqttDisconnected() return @@ -67,7 +62,7 @@ class MqttClientProxyManager { mqttClient.username = username mqttClient.password = password mqttClient.keepAlive = 60 - mqttClient.cleanSession = cleanSession + mqttClient.cleanSession = true if debugLog { mqttClient.logLevel = .debug } diff --git a/Meshtastic/Model/Metrics Visualization/MetricsChartSeries.swift b/Meshtastic/Model/Metrics Visualization/MetricsChartSeries.swift index ab68c4cc..42e35ce0 100644 --- a/Meshtastic/Model/Metrics Visualization/MetricsChartSeries.swift +++ b/Meshtastic/Model/Metrics Visualization/MetricsChartSeries.swift @@ -41,7 +41,6 @@ class MetricsChartSeries: ObservableObject { // Used for scaling the Y-axis let initialYAxisRange: ClosedRange? let minumumYAxisSpan: Float? - // Main initializer init( id: String, diff --git a/Meshtastic/Model/Metrics Visualization/MetricsSeriesList.swift b/Meshtastic/Model/Metrics Visualization/MetricsSeriesList.swift index fb68af92..1def5f47 100644 --- a/Meshtastic/Model/Metrics Visualization/MetricsSeriesList.swift +++ b/Meshtastic/Model/Metrics Visualization/MetricsSeriesList.swift @@ -67,12 +67,12 @@ class MetricsSeriesList: ObservableObject, RandomAccessCollection, RangeReplacea for aSeries in self.visible { var seriesUpper = range[aSeries]?.upperBound ?? -.infinity var seriesLower = range[aSeries]?.lowerBound ?? .infinity - + if let value = aSeries.valueFor(te) { // Update the global bounds if value > globalUpper {globalUpper = value} if value < globalLower {globalLower = value} - + // Update the series bounds if necessary if value > seriesUpper || value < seriesLower { if value > seriesUpper { diff --git a/Meshtastic/Views/Helpers/CircleText.swift b/Meshtastic/Views/Helpers/CircleText.swift index c9ee41c6..f85decf4 100644 --- a/Meshtastic/Views/Helpers/CircleText.swift +++ b/Meshtastic/Views/Helpers/CircleText.swift @@ -10,11 +10,11 @@ struct CircleText: View { var text: String var color: Color var circleSize: CGFloat = 45 - var node: NodeInfoEntity? = nil - + var node: NodeInfoEntity? + var body: some View { if let node = node { - NavigationStack{ + NavigationStack { NavigationLink(destination: NodeDetail(node: node)) { circleContent } diff --git a/Meshtastic/Views/Helpers/Compact Widgets/CompactWidget.swift b/Meshtastic/Views/Helpers/Compact Widgets/CompactWidget.swift index 42411cc1..a999dae0 100644 --- a/Meshtastic/Views/Helpers/Compact Widgets/CompactWidget.swift +++ b/Meshtastic/Views/Helpers/Compact Widgets/CompactWidget.swift @@ -31,7 +31,7 @@ import SwiftUI WeightCompactWidget(weight: "123", unit: "kg") SoilTemperatureCompactWidget(temperature: "23", unit: "Β°C") SoilMoistureCompactWidget(moisture: "23", unit: "%") - + let rain: Float = 10.1 let locale = NSLocale.current as NSLocale let usesMetricSystem = locale.usesMetricSystem // Returns true for metric (mm), false for imperial (inches) diff --git a/Meshtastic/Views/Helpers/Compact Widgets/RadiationCompactWidget.swift b/Meshtastic/Views/Helpers/Compact Widgets/RadiationCompactWidget.swift index b5cd7232..582a1c26 100644 --- a/Meshtastic/Views/Helpers/Compact Widgets/RadiationCompactWidget.swift +++ b/Meshtastic/Views/Helpers/Compact Widgets/RadiationCompactWidget.swift @@ -16,7 +16,7 @@ struct RadiationCompactWidget: View { HStack(alignment: .firstTextBaseline) { Text(verbatim: "☒") .font(.system(size: 30, design: .monospaced)) - .foregroundColor(.accentColor) + .tint(.accentColor) Text("Radiation") .textCase(.uppercase) .font(.callout) diff --git a/Meshtastic/Views/Helpers/Compact Widgets/WeatherConditionsCompactWidget.swift b/Meshtastic/Views/Helpers/Compact Widgets/WeatherConditionsCompactWidget.swift index 4918c7b6..3cac48bf 100644 --- a/Meshtastic/Views/Helpers/Compact Widgets/WeatherConditionsCompactWidget.swift +++ b/Meshtastic/Views/Helpers/Compact Widgets/WeatherConditionsCompactWidget.swift @@ -39,4 +39,3 @@ struct WeatherConditionsCompactWidget: View { } } } - diff --git a/Meshtastic/Views/Messages/ChannelMessageList.swift b/Meshtastic/Views/Messages/ChannelMessageList.swift index 0696e9d8..f787f4f1 100644 --- a/Meshtastic/Views/Messages/ChannelMessageList.swift +++ b/Meshtastic/Views/Messages/ChannelMessageList.swift @@ -14,19 +14,16 @@ struct ChannelMessageList: View { @EnvironmentObject var appState: AppState @Environment(\.managedObjectContext) var context @EnvironmentObject var bleManager: BLEManager - // Keyboard State @FocusState var messageFieldFocused: Bool - @ObservedObject var myInfo: MyInfoEntity @ObservedObject var channel: ChannelEntity @State private var replyMessageId: Int64 = 0 @AppStorage("preferredPeripheralNum") private var preferredPeripheralNum = -1 - // Scroll state - @State private var showScrollToBottomButton = false - @State private var hasReachedBottom = false - @State private var gotFirstUnreadMessage: Bool = false + @State private var showScrollToBottomButton = false + @State private var hasReachedBottom = false + @State private var gotFirstUnreadMessage: Bool = false var body: some View { VStack { @@ -118,7 +115,7 @@ struct ChannelMessageList: View { .frame(maxWidth: .infinity) .id(message.messageId) .onAppear { - if gotFirstUnreadMessage{ + if gotFirstUnreadMessage { if !message.read { message.read = true do { @@ -184,7 +181,6 @@ struct ChannelMessageList: View { showScrollToBottomButton = true } } - // Scroll to bottom button if showScrollToBottomButton { Button { diff --git a/Meshtastic/Views/Messages/MessageText.swift b/Meshtastic/Views/Messages/MessageText.swift index 002d2c4c..c8a994c3 100644 --- a/Meshtastic/Views/Messages/MessageText.swift +++ b/Meshtastic/Views/Messages/MessageText.swift @@ -73,7 +73,6 @@ struct MessageText: View { } else { EmptyView() } - } .contextMenu { MessageContextMenuItems( diff --git a/Meshtastic/Views/Messages/UserMessageList.swift b/Meshtastic/Views/Messages/UserMessageList.swift index 6f995756..9824f621 100644 --- a/Meshtastic/Views/Messages/UserMessageList.swift +++ b/Meshtastic/Views/Messages/UserMessageList.swift @@ -20,7 +20,6 @@ struct UserMessageList: View { // View State Items @ObservedObject var user: UserEntity @State private var replyMessageId: Int64 = 0 - // Scroll state @State private var showScrollToBottomButton = false @State private var hasReachedBottom = false @@ -171,7 +170,6 @@ struct UserMessageList: View { showScrollToBottomButton = true } } - // Scroll to bottom button if showScrollToBottomButton { Button { diff --git a/Meshtastic/Views/Nodes/Helpers/Actions/NavigateToButton.swift b/Meshtastic/Views/Nodes/Helpers/Actions/NavigateToButton.swift index 78917082..403d1b98 100644 --- a/Meshtastic/Views/Nodes/Helpers/Actions/NavigateToButton.swift +++ b/Meshtastic/Views/Nodes/Helpers/Actions/NavigateToButton.swift @@ -14,45 +14,43 @@ struct NavigateToButton: View { var node: NodeInfoEntity var body: some View { - Button { - guard let userNum = node.user?.num else { - Logger.services.error("NavigateToAction: Selected node does not exist") + Button { + guard let userNum = node.user?.num else { + Logger.services.error("NavigateToAction: Selected node does not exist") + return + } + Logger.services.info("Fetching NodeInfoEntity for userNum: \(userNum, privacy: .public)") + + let fetchRequest: NSFetchRequest = NSFetchRequest(entityName: "NodeInfoEntity") + fetchRequest.predicate = NSPredicate(format: "num == %lld", Int64(userNum)) + + do { + let fetchedNodes = try PersistenceController.shared.container.viewContext.fetch(fetchRequest) + guard let nodeInfo = fetchedNodes.first else { + Logger.services.error("NavigateToAction: Node with userNum \(userNum, privacy: .public) not found in Core Data") return } - - Logger.services.info("Fetching NodeInfoEntity for userNum: \(userNum, privacy: .public)") - - let fetchRequest: NSFetchRequest = NSFetchRequest(entityName: "NodeInfoEntity") - fetchRequest.predicate = NSPredicate(format: "num == %lld", Int64(userNum)) - - do { - let fetchedNodes = try PersistenceController.shared.container.viewContext.fetch(fetchRequest) - - guard let nodeInfo = fetchedNodes.first else { - Logger.services.error("NavigateToAction: Node with userNum \(userNum, privacy: .public) not found in Core Data") - return - } - if let latitude = nodeInfo.latestPosition?.latitude, - let longitude = nodeInfo.latestPosition?.longitude { - if let url = URL(string: "maps://?saddr=&daddr=\(latitude),\(longitude)") { - UIApplication.shared.open(url, options: [:], completionHandler: nil) - } else { - Logger.services.error("Failed to create URL for navigation") - } + if let latitude = nodeInfo.latestPosition?.latitude, + let longitude = nodeInfo.latestPosition?.longitude { + if let url = URL(string: "maps://?saddr=&daddr=\(latitude),\(longitude)") { + UIApplication.shared.open(url, options: [:], completionHandler: nil) } else { - Logger.services.warning("NavigateToAction: Node \(userNum, privacy: .public) has invalid or missing coordinates") + Logger.services.error("Failed to create URL for navigation") } - } catch { - Logger.services.error("NavigateToAction: Failed to fetch node with userNum \(userNum, privacy: .public): \(error.localizedDescription, privacy: .public)") - } - } label: { - Label { - Text("Navigate to node") - } icon: { - Image(systemName: "map") - .symbolRenderingMode(.hierarchical) + } else { + Logger.services.warning("NavigateToAction: Node \(userNum, privacy: .public) has invalid or missing coordinates") } + } catch { + Logger.services.error("NavigateToAction: Failed to fetch node with userNum \(userNum, privacy: .public): \(error.localizedDescription, privacy: .public)") + } + } label: { + Label { + Text("Navigate to node") + } icon: { + Image(systemName: "map") + .symbolRenderingMode(.hierarchical) } } + } } diff --git a/Meshtastic/Views/Nodes/Helpers/Map/PositionPopover.swift b/Meshtastic/Views/Nodes/Helpers/Map/PositionPopover.swift index ce7a10ef..97df2327 100644 --- a/Meshtastic/Views/Nodes/Helpers/Map/PositionPopover.swift +++ b/Meshtastic/Views/Nodes/Helpers/Map/PositionPopover.swift @@ -23,7 +23,7 @@ struct PositionPopover: View { var body: some View { // Node Color from node.num let nodeColor = UIColor(hex: UInt32(position.nodePosition?.num ?? 0)) - NavigationStack{ + NavigationStack { VStack { HStack { ZStack { @@ -105,7 +105,6 @@ struct PositionPopover: View { .foregroundColor(.primary) .font(idiom == .phone ? .callout : .body) } - } icon: { Image(systemName: "mountain.2.fill") .symbolRenderingMode(.hierarchical) @@ -180,7 +179,6 @@ struct PositionPopover: View { } .padding(.bottom, 5) if position.nodePosition?.viaMqtt ?? false { - Label { Text("MQTT") .font(idiom == .phone ? .callout : .body) diff --git a/Meshtastic/Views/Nodes/Helpers/Metrics Columns/EnvironmentDefaultColumns.swift b/Meshtastic/Views/Nodes/Helpers/Metrics Columns/EnvironmentDefaultColumns.swift index ffd86987..42cf8663 100644 --- a/Meshtastic/Views/Nodes/Helpers/Metrics Columns/EnvironmentDefaultColumns.swift +++ b/Meshtastic/Views/Nodes/Helpers/Metrics Columns/EnvironmentDefaultColumns.swift @@ -220,7 +220,6 @@ extension MetricsColumnList { ) } ?? Text(Constants.nilValueIndicator) }), - // Rainfall 24-hour MetricsTableColumn( id: "rainfall24H", diff --git a/Meshtastic/Views/Nodes/Helpers/Metrics Columns/EnvironmentDefaultSeries.swift b/Meshtastic/Views/Nodes/Helpers/Metrics Columns/EnvironmentDefaultSeries.swift index d3762845..b2590b51 100644 --- a/Meshtastic/Views/Nodes/Helpers/Metrics Columns/EnvironmentDefaultSeries.swift +++ b/Meshtastic/Views/Nodes/Helpers/Metrics Columns/EnvironmentDefaultSeries.swift @@ -80,7 +80,7 @@ extension MetricsSeriesList { .alignsMarkStylesWithPlotArea() } }), - + // Barometric Pressure Series Configuration MetricsChartSeries( id: "barometricPressure", @@ -106,7 +106,7 @@ extension MetricsSeriesList { .alignsMarkStylesWithPlotArea() } }), - + // Indoor Air Quality Series Configuration MetricsChartSeries( id: "iaq", @@ -134,7 +134,7 @@ extension MetricsSeriesList { .alignsMarkStylesWithPlotArea() } }), - + // Lux MetricsChartSeries( id: "lux", @@ -460,7 +460,7 @@ extension MetricsSeriesList { .lineStyle(StrokeStyle(lineWidth: 4)) .alignsMarkStylesWithPlotArea() } - }), + }) ]) } } diff --git a/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift b/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift index cabfabc6..958906aa 100644 --- a/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift +++ b/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift @@ -77,7 +77,7 @@ struct NodeListItem: View { imageColor: .green, text: "connected".localized) } - if node.lastHeard?.timeIntervalSince1970 ?? 0 > 0 && node.lastHeard! < Calendar.current.date(byAdding: .year, value: 1, to: Date())!{ + if node.lastHeard?.timeIntervalSince1970 ?? 0 > 0 && node.lastHeard! < Calendar.current.date(byAdding: .year, value: 1, to: Date())! { IconAndText(systemName: node.isOnline ? "checkmark.circle.fill" : "moon.circle.fill", imageColor: node.isOnline ? .green : .orange, text: node.lastHeard?.formatted() ?? "unknown.age".localized) diff --git a/Meshtastic/Views/Nodes/NodeList.swift b/Meshtastic/Views/Nodes/NodeList.swift index 49b2d3a1..de1b522a 100644 --- a/Meshtastic/Views/Nodes/NodeList.swift +++ b/Meshtastic/Views/Nodes/NodeList.swift @@ -65,10 +65,7 @@ struct NodeList: View { var nodes: FetchedResults var connectedNode: NodeInfoEntity? { - getNodeInfo( - id: bleManager.connectedPeripheral?.num ?? 0, - context: context - ) + getNodeInfo(id: bleManager.connectedPeripheral?.num ?? 0, context: context) } @ViewBuilder @@ -78,19 +75,11 @@ struct NodeList: View { ) -> some View { /// Allow users to mute notifications for a node even if they are not connected if let user = node.user { - NodeAlertsButton( - context: context, - node: node, - user: user - ) + NodeAlertsButton(context: context, node: node, user: user) } if let connectedNode { /// Favoriting a node requires being connected - FavoriteNodeButton( - bleManager: bleManager, - context: context, - node: node - ) + FavoriteNodeButton(bleManager: bleManager, context: context, node: node) /// Don't show message, trace route, position exchange or delete context menu items for the connected node if connectedNode.num != node.num { if !node.viaMqtt || node.viaMqtt && node.hopsAway == 0 { diff --git a/Meshtastic/Views/Nodes/PositionLog.swift b/Meshtastic/Views/Nodes/PositionLog.swift index 6c78d1c3..5af381c9 100644 --- a/Meshtastic/Views/Nodes/PositionLog.swift +++ b/Meshtastic/Views/Nodes/PositionLog.swift @@ -65,8 +65,6 @@ struct PositionLog: View { .width(min: 180) } .textSelection(.enabled) - - } else { ScrollView { // Use a grid on iOS as a table only shows a single column diff --git a/Meshtastic/Views/Settings/About.swift b/Meshtastic/Views/Settings/About.swift index 84fa4b83..f6854bcb 100644 --- a/Meshtastic/Views/Settings/About.swift +++ b/Meshtastic/Views/Settings/About.swift @@ -14,7 +14,6 @@ struct AboutMeshtastic: View { var body: some View { VStack { - List { Section(header: Text("What is Meshtastic?")) { Text("An open source, off-grid, decentralized, mesh network that runs on affordable, low-power radios.") @@ -44,7 +43,7 @@ struct AboutMeshtastic: View { Button("Review the app") { if let scene = UIApplication.shared.connectedScenes .first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene { - SKStoreReviewController.requestReview(in: scene) + AppStore.requestReview(in: scene) } } .font(.title2) diff --git a/Meshtastic/Views/Settings/AppLog.swift b/Meshtastic/Views/Settings/AppLog.swift index 53a32e07..88f8ab55 100644 --- a/Meshtastic/Views/Settings/AppLog.swift +++ b/Meshtastic/Views/Settings/AppLog.swift @@ -270,4 +270,4 @@ extension AppLog { } } -extension OSLogEntry: Identifiable { } +extension OSLogEntry: @retroactive Identifiable { } diff --git a/Meshtastic/Views/Settings/Config/DeviceConfig.swift b/Meshtastic/Views/Settings/Config/DeviceConfig.swift index 3fdc73b9..d8d0296d 100644 --- a/Meshtastic/Views/Settings/Config/DeviceConfig.swift +++ b/Meshtastic/Views/Settings/Config/DeviceConfig.swift @@ -30,11 +30,9 @@ struct DeviceConfig: View { @State var ledHeartbeatEnabled = true @State var tripleClickAsAdHocPing = true @State var tzdef = "" - @State private var showRouterWarning = false @State private var confirmWarning = false - var body: some View { VStack { Form { diff --git a/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift b/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift index 21297a16..f9f9e499 100644 --- a/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift @@ -388,7 +388,6 @@ struct MQTTConfig: View { } if let placemarks = placemarks, let placemark = placemarks.first { - let cc = locale.region?.identifier ?? "UNK" /// Country Topic unless your region is a country if !(region?.isCountry ?? false) { let countryTopic = defaultTopic + "/" + (placemark.isoCountryCode ?? "") diff --git a/Meshtastic/Views/Settings/Config/PositionConfig.swift b/Meshtastic/Views/Settings/Config/PositionConfig.swift index d8e6ad9f..b22e0dfd 100644 --- a/Meshtastic/Views/Settings/Config/PositionConfig.swift +++ b/Meshtastic/Views/Settings/Config/PositionConfig.swift @@ -139,7 +139,6 @@ struct PositionConfig: View { ForEach(GpsMode.allCases, id: \.self) { at in Text(at.description) .tag(at.id) - } } .pickerStyle(SegmentedPickerStyle()) @@ -314,7 +313,6 @@ struct PositionConfig: View { .font(.caption) } } - var saveButton: some View { SaveConfigButton(node: node, hasChanges: $hasChanges) { if fixedPosition && !supportedVersion { @@ -399,11 +397,7 @@ struct PositionConfig: View { .navigationTitle("position.config") .navigationBarItems( trailing: ZStack { - ConnectedDevice( - bluetoothOn: bleManager.isSwitchedOn, - deviceConnected: bleManager.connectedPeripheral != nil, - name: bleManager.connectedPeripheral?.shortName ?? "?" - ) + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: bleManager.connectedPeripheral?.shortName ?? "?") } ) .onFirstAppear { diff --git a/Widgets/WidgetsLiveActivity.swift b/Widgets/WidgetsLiveActivity.swift index 3923fee7..656cc8a1 100644 --- a/Widgets/WidgetsLiveActivity.swift +++ b/Widgets/WidgetsLiveActivity.swift @@ -224,7 +224,7 @@ struct NodeInfoView: View { .foregroundStyle(.secondary) .opacity(isLuminanceReduced ? 0.8 : 1.0) .fixedSize() - + let now = Date() Text("Last Heard: \(now.formatted())") .font(.caption)