diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 658dadb3..0c8fb35f 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -12,6 +12,8 @@ 6DEDA55C2A9592F900321D2E /* MessageEntityExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DEDA55B2A9592F900321D2E /* MessageEntityExtension.swift */; }; C9697F9D279336B700250207 /* LocalMBTileOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9697F9C279336B700250207 /* LocalMBTileOverlay.swift */; }; C9697FA527933B8C00250207 /* SQLite in Frameworks */ = {isa = PBXBuildFile; productRef = C9697FA427933B8C00250207 /* SQLite */; }; + DD007BAE2AA4E91200F5FA12 /* MyInfoEntityExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD007BAD2AA4E91200F5FA12 /* MyInfoEntityExtension.swift */; }; + DD007BB02AA5981000F5FA12 /* NodeInfoEntityExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD007BAF2AA5981000F5FA12 /* NodeInfoEntityExtension.swift */; }; DD0D3D222A55CEB10066DB71 /* CocoaMQTT in Frameworks */ = {isa = PBXBuildFile; productRef = DD0D3D212A55CEB10066DB71 /* CocoaMQTT */; }; DD0F791B28713C8A00A6FDAD /* AdminMessageList.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0F791A28713C8A00A6FDAD /* AdminMessageList.swift */; }; DD14E72E2A82A614006E39BC /* RemoteHardware.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD14E72D2A82A614006E39BC /* RemoteHardware.swift */; }; @@ -70,6 +72,7 @@ DD769E0328D18BF1001A3F05 /* DeviceMetricsLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD769E0228D18BF0001A3F05 /* DeviceMetricsLog.swift */; }; DD77093B2AA1ABB8007A8BF0 /* BluetoothTips.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD77093A2AA1ABB8007A8BF0 /* BluetoothTips.swift */; }; DD77093D2AA1AFA3007A8BF0 /* ChannelTips.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD77093C2AA1AFA3007A8BF0 /* ChannelTips.swift */; }; + DD77093F2AA1B146007A8BF0 /* UIColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD77093E2AA1B146007A8BF0 /* UIColor.swift */; }; DD798B072915928D005217CD /* ChannelMessageList.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD798B062915928D005217CD /* ChannelMessageList.swift */; }; DD8169F9271F1A6100F4AB02 /* MeshLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8169F8271F1A6100F4AB02 /* MeshLogger.swift */; }; DD8169FB271F1F3A00F4AB02 /* MeshLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8169FA271F1F3A00F4AB02 /* MeshLog.swift */; }; @@ -79,7 +82,6 @@ DD86D40C287F401000BAEB7A /* SaveChannelQRCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD86D40B287F401000BAEB7A /* SaveChannelQRCode.swift */; }; DD86D40F2881BE4C00BAEB7A /* CsvDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD86D40E2881BE4C00BAEB7A /* CsvDocument.swift */; }; DD86D4112881D16900BAEB7A /* WriteCsvFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD86D4102881D16900BAEB7A /* WriteCsvFile.swift */; }; - DD882F5D2772E4640005BF05 /* Contacts.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD882F5C2772E4640005BF05 /* Contacts.swift */; }; DD8EBF43285058FA00426DCA /* DisplayConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8EBF42285058FA00426DCA /* DisplayConfig.swift */; }; DD8ED9C52898D51F00B3B0AB /* NetworkConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8ED9C42898D51F00B3B0AB /* NetworkConfig.swift */; }; DD8ED9C8289CE4B900B3B0AB /* RoutingError.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8ED9C7289CE4B900B3B0AB /* RoutingError.swift */; }; @@ -209,6 +211,8 @@ 6DEDA55B2A9592F900321D2E /* MessageEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageEntityExtension.swift; sourceTree = ""; }; A65FA974296876BF00A97686 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; C9697F9C279336B700250207 /* LocalMBTileOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalMBTileOverlay.swift; sourceTree = ""; }; + DD007BAD2AA4E91200F5FA12 /* MyInfoEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyInfoEntityExtension.swift; sourceTree = ""; }; + DD007BAF2AA5981000F5FA12 /* NodeInfoEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeInfoEntityExtension.swift; sourceTree = ""; }; DD0E9C222A30CE3A00580CBB /* MeshtasticDataModelV14.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV14.xcdatamodel; sourceTree = ""; }; DD0F791A28713C8A00A6FDAD /* AdminMessageList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminMessageList.swift; sourceTree = ""; }; DD14E72C2A80738F006E39BC /* MeshtasticDataModelV15.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV15.xcdatamodel; sourceTree = ""; }; @@ -272,6 +276,7 @@ DD769E0228D18BF0001A3F05 /* DeviceMetricsLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceMetricsLog.swift; sourceTree = ""; }; DD77093A2AA1ABB8007A8BF0 /* BluetoothTips.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BluetoothTips.swift; sourceTree = ""; }; DD77093C2AA1AFA3007A8BF0 /* ChannelTips.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelTips.swift; sourceTree = ""; }; + DD77093E2AA1B146007A8BF0 /* UIColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIColor.swift; sourceTree = ""; }; DD798B062915928D005217CD /* ChannelMessageList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelMessageList.swift; sourceTree = ""; }; DD8169F8271F1A6100F4AB02 /* MeshLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshLogger.swift; sourceTree = ""; }; DD8169FA271F1F3A00F4AB02 /* MeshLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshLog.swift; sourceTree = ""; }; @@ -281,7 +286,6 @@ DD86D40B287F401000BAEB7A /* SaveChannelQRCode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveChannelQRCode.swift; sourceTree = ""; }; DD86D40E2881BE4C00BAEB7A /* CsvDocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CsvDocument.swift; sourceTree = ""; }; DD86D4102881D16900BAEB7A /* WriteCsvFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WriteCsvFile.swift; sourceTree = ""; }; - DD882F5C2772E4640005BF05 /* Contacts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Contacts.swift; sourceTree = ""; }; DD8EBF42285058FA00426DCA /* DisplayConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayConfig.swift; sourceTree = ""; }; DD8ED9C42898D51F00B3B0AB /* NetworkConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkConfig.swift; sourceTree = ""; }; DD8ED9C7289CE4B900B3B0AB /* RoutingError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoutingError.swift; sourceTree = ""; }; @@ -441,6 +445,20 @@ path = Custom; sourceTree = ""; }; + DD007BB12AA59B9A00F5FA12 /* CoreData */ = { + isa = PBXGroup; + children = ( + DD58C5F12919AD3C00D5BEFB /* ChannelEntityExtension.swift */, + 6DEDA55B2A9592F900321D2E /* MessageEntityExtension.swift */, + DD007BAD2AA4E91200F5FA12 /* MyInfoEntityExtension.swift */, + DD007BAF2AA5981000F5FA12 /* NodeInfoEntityExtension.swift */, + DD5394FD276BA0EF00AD86B1 /* PositionEntityExtension.swift */, + DDD9E4E3284B208E003777C5 /* UserEntityExtension.swift */, + DD964FC1297272AE007C176F /* WaypointEntityExtension.swift */, + ); + path = CoreData; + sourceTree = ""; + }; DD47E3CA26F0E50300029299 /* Nodes */ = { isa = PBXGroup; children = ( @@ -647,13 +665,14 @@ DDDB443E29F79A9400EE2349 /* Extensions */, DD90860A26F645B700DC5189 /* Meshtastic.entitlements */, DD8ED9C6289CE4A100B3B0AB /* Enums */, + DD86D40D2881BDB300BAEB7A /* Export */, + DDDB443E29F79A9400EE2349 /* Extensions */, + DDC2E1A526CEB32B0042C5E4 /* Helpers */, + DDC2E18826CE24EE0042C5E4 /* Model */, DDC4D5662754996200A4208E /* Persistence */, DDAF8C5626ED07740058C060 /* Protobufs */, - DD86D40D2881BDB300BAEB7A /* Export */, - DDC2E1A526CEB32B0042C5E4 /* Helpers */, - DDC2E18726CE24E40042C5E4 /* Views */, - DDC2E18826CE24EE0042C5E4 /* Model */, DDC2E18926CE24F70042C5E4 /* Resources */, + DDC2E18726CE24E40042C5E4 /* Views */, DDC2E15726CE248E0042C5E4 /* MeshtasticApp.swift */, DDC2E16526CE248F0042C5E4 /* Info.plist */, DDC2E15D26CE248F0042C5E4 /* Preview Content */, @@ -724,7 +743,6 @@ children = ( DDB8F4132A9EE5F000230ECE /* ChannelList.swift */, DD798B062915928D005217CD /* ChannelMessageList.swift */, - DD882F5C2772E4640005BF05 /* Contacts.swift */, DDB8F40F2A9EE5B400230ECE /* Messages.swift */, DDB8F4112A9EE5DD00230ECE /* UserList.swift */, DD1BF2F82776FE2E008C8D2F /* UserMessageList.swift */, @@ -773,13 +791,8 @@ isa = PBXGroup; children = ( DDC4D567275499A500A4208E /* Persistence.swift */, - DD5394FD276BA0EF00AD86B1 /* PositionEntityExtension.swift */, - DDD9E4E3284B208E003777C5 /* UserEntityExtension.swift */, - DD58C5F12919AD3C00D5BEFB /* ChannelEntityExtension.swift */, DD964FC52975DBFD007C176F /* QueryCoreData.swift */, DD3CC6C128EB9D4900FA9159 /* UpdateCoreData.swift */, - DD964FC1297272AE007C176F /* WaypointEntityExtension.swift */, - 6DEDA55B2A9592F900321D2E /* MessageEntityExtension.swift */, ); path = Persistence; sourceTree = ""; @@ -795,6 +808,7 @@ DDDB443E29F79A9400EE2349 /* Extensions */ = { isa = PBXGroup; children = ( + DD007BB12AA59B9A00F5FA12 /* CoreData */, DDDB444529F8A96500EE2349 /* Character.swift */, DDDB444929F8AA3A00EE2349 /* CLLocationCoordinate2D.swift */, DDDB444B29F8AAA600EE2349 /* Color.swift */, @@ -804,6 +818,7 @@ DDDB444329F8A8DD00EE2349 /* Float.swift */, DDDB444D29F8AB0E00EE2349 /* Int.swift */, DDDB444729F8A9C900EE2349 /* String.swift */, + DD77093E2AA1B146007A8BF0 /* UIColor.swift */, DDDB444F29F8AC9C00EE2349 /* UIImage.swift */, DDDB443F29F79AB000EE2349 /* UserDefaults.swift */, DDB75A0E2A05920E006ED576 /* FileManager.swift */, @@ -1089,6 +1104,7 @@ DD6193772862F90F00E59241 /* CannedMessagesConfig.swift in Sources */, DDDB444A29F8AA3A00EE2349 /* CLLocationCoordinate2D.swift in Sources */, DD41582628582E9B009B0E59 /* DeviceConfig.swift in Sources */, + DD007BAE2AA4E91200F5FA12 /* MyInfoEntityExtension.swift in Sources */, DD3CC6B528E33FD100FA9159 /* ShareChannels.swift in Sources */, DD1BF2F92776FE2E008C8D2F /* UserMessageList.swift in Sources */, DD5E5207298EE33B00D21B61 /* connection_status.pb.swift in Sources */, @@ -1097,11 +1113,11 @@ DDB6ABE628B1406100384BA1 /* LoraConfigEnums.swift in Sources */, DDB8F4142A9EE5F000230ECE /* ChannelList.swift in Sources */, DDD43FE32A78C8900083A3E9 /* MqttClientProxyManager.swift in Sources */, + DD007BB02AA5981000F5FA12 /* NodeInfoEntityExtension.swift in Sources */, DDDB444629F8A96500EE2349 /* Character.swift in Sources */, DD23A50F26FD1B4400D9B90C /* PeripheralModel.swift in Sources */, DDB6ABDB28B0AC6000384BA1 /* DistanceText.swift in Sources */, DD5E520D298EE33B00D21B61 /* storeforward.pb.swift in Sources */, - DD882F5D2772E4640005BF05 /* Contacts.swift in Sources */, DD964FC2297272AE007C176F /* WaypointEntityExtension.swift in Sources */, 6DA39D8E2A92DC52007E311C /* MeshtasticAppDelegate.swift in Sources */, DD47E3CE26F103C600029299 /* NodeList.swift in Sources */, @@ -1161,6 +1177,7 @@ DD3CC6BC28E366DF00FA9159 /* Meshtastic.xcdatamodeld in Sources */, DDC4C9FF2A8D982900CE201C /* DetectionSensorConfig.swift in Sources */, DD5E5210298EE33B00D21B61 /* telemetry.pb.swift in Sources */, + DD77093F2AA1B146007A8BF0 /* UIColor.swift in Sources */, DD5E5205298EE33B00D21B61 /* mesh.pb.swift in Sources */, DDF6B2482A9AEBF500BA6931 /* StoreForward.swift in Sources */, DD8169F9271F1A6100F4AB02 /* MeshLogger.swift in Sources */, @@ -1381,7 +1398,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.2.3; + MARKETING_VERSION = 2.2.4; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -1415,7 +1432,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.2.3; + MARKETING_VERSION = 2.2.4; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -1537,7 +1554,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 2.2.3; + MARKETING_VERSION = 2.2.4; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1570,7 +1587,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 2.2.3; + MARKETING_VERSION = 2.2.4; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/Meshtastic/Persistence/ChannelEntityExtension.swift b/Meshtastic/Extensions/CoreData/ChannelEntityExtension.swift similarity index 100% rename from Meshtastic/Persistence/ChannelEntityExtension.swift rename to Meshtastic/Extensions/CoreData/ChannelEntityExtension.swift diff --git a/Meshtastic/Persistence/MessageEntityExtension.swift b/Meshtastic/Extensions/CoreData/MessageEntityExtension.swift similarity index 100% rename from Meshtastic/Persistence/MessageEntityExtension.swift rename to Meshtastic/Extensions/CoreData/MessageEntityExtension.swift diff --git a/Meshtastic/Extensions/CoreData/MyInfoEntityExtension.swift b/Meshtastic/Extensions/CoreData/MyInfoEntityExtension.swift new file mode 100644 index 00000000..1e04282a --- /dev/null +++ b/Meshtastic/Extensions/CoreData/MyInfoEntityExtension.swift @@ -0,0 +1,20 @@ +// +// MyInfoEntityExtension.swift +// Meshtastic +// +// Copyright(c) Garth Vander Houwen 9/3/23. +// + +import Foundation + +extension MyInfoEntity { + + var messageList: [MessageEntity] { + self.value(forKey: "allMessages") as? [MessageEntity] ?? [MessageEntity]() + } + + var unreadMessages: Int { + let unreadMessages = messageList.filter{ ($0 as AnyObject).read == false } + return unreadMessages.count + } +} diff --git a/Meshtastic/Extensions/CoreData/NodeInfoEntityExtension.swift b/Meshtastic/Extensions/CoreData/NodeInfoEntityExtension.swift new file mode 100644 index 00000000..d24f69f1 --- /dev/null +++ b/Meshtastic/Extensions/CoreData/NodeInfoEntityExtension.swift @@ -0,0 +1,27 @@ +// +// NodeInfoEntityExtension.swift +// Meshtastic +// +// Copyright(c) Garth Vander Houwen 9/3/23. +// + +import Foundation + +extension NodeInfoEntity { + + var hasPositions: Bool { + return positions?.count ?? 0 > 0 + } + + var hasDeviceMetrics: Bool { + + let deviceMetrics = telemetries?.filter{ ($0 as AnyObject).metricsType == 0 } + return deviceMetrics?.count ?? 0 > 0 + } + + var hasEnvironmentMetrics: Bool { + + let environmentMetrics = telemetries?.filter{ ($0 as AnyObject).metricsType == 1 } + return environmentMetrics?.count ?? 0 > 0 + } +} diff --git a/Meshtastic/Persistence/PositionEntityExtension.swift b/Meshtastic/Extensions/CoreData/PositionEntityExtension.swift similarity index 100% rename from Meshtastic/Persistence/PositionEntityExtension.swift rename to Meshtastic/Extensions/CoreData/PositionEntityExtension.swift diff --git a/Meshtastic/Persistence/UserEntityExtension.swift b/Meshtastic/Extensions/CoreData/UserEntityExtension.swift similarity index 99% rename from Meshtastic/Persistence/UserEntityExtension.swift rename to Meshtastic/Extensions/CoreData/UserEntityExtension.swift index 1786a45b..927c0372 100644 --- a/Meshtastic/Persistence/UserEntityExtension.swift +++ b/Meshtastic/Extensions/CoreData/UserEntityExtension.swift @@ -18,7 +18,6 @@ extension UserEntity { } var unreadMessages: Int { - let unreadMessages = messageList.filter{ ($0 as AnyObject).read == false } return unreadMessages.count } diff --git a/Meshtastic/Persistence/WaypointEntityExtension.swift b/Meshtastic/Extensions/CoreData/WaypointEntityExtension.swift similarity index 100% rename from Meshtastic/Persistence/WaypointEntityExtension.swift rename to Meshtastic/Extensions/CoreData/WaypointEntityExtension.swift diff --git a/Meshtastic/Extensions/Date.swift b/Meshtastic/Extensions/Date.swift index bac54654..29a60320 100644 --- a/Meshtastic/Extensions/Date.swift +++ b/Meshtastic/Extensions/Date.swift @@ -15,6 +15,10 @@ extension Date { func formattedDate(format: String) -> String { let dateformat = DateFormatter() dateformat.dateFormat = format - return dateformat.string(from: self) + if self > Calendar.current.date(byAdding: .year, value: -5, to: Date())! { + return dateformat.string(from: self) + } else { + return "unknown.age".localized + } } } diff --git a/Meshtastic/Extensions/UIColor.swift b/Meshtastic/Extensions/UIColor.swift new file mode 100644 index 00000000..6a8909b9 --- /dev/null +++ b/Meshtastic/Extensions/UIColor.swift @@ -0,0 +1,41 @@ +// +// UIColor.swift +// Meshtastic +// +// Copyright(c) Garth Vander Houwen 8/31/23. +// +import Foundation +import Swift +import UIKit + +extension UIColor { + + private func makeColor(componentDelta: CGFloat) -> UIColor { + var red: CGFloat = 0 + var blue: CGFloat = 0 + var green: CGFloat = 0 + var alpha: CGFloat = 0 + + getRed(&red, green: &green, blue: &blue, alpha: &alpha) + + return UIColor( + red: add(componentDelta, toComponent: red), + green: add(componentDelta, toComponent: green), + blue: add(componentDelta, toComponent: blue), + alpha: alpha + ) + } + + func lighter(componentDelta: CGFloat = 0.1) -> UIColor { + return makeColor(componentDelta: componentDelta) + } + + func darker(componentDelta: CGFloat = 0.1) -> UIColor { + return makeColor(componentDelta: -1*componentDelta) + } + + private func add(_ value: CGFloat, toComponent: CGFloat) -> CGFloat { + return max(0, min(1, toComponent + value)) + } + +} diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index efc6eb51..ab010d52 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -28,6 +28,8 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate @Published var isSwitchedOn: Bool = false @Published var automaticallyReconnect: Bool = true @Published var mqttProxyConnected: Bool = false + + @StateObject var appState = AppState.shared public var minimumVersion = "2.0.0" public var connectedVersion: String public var isConnecting: Bool = false @@ -471,7 +473,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate if nodeInfo != nil { if self.connectedPeripheral != nil && self.connectedPeripheral.num == nodeInfo!.num { if nodeInfo!.user != nil { - connectedPeripheral.shortName = nodeInfo?.user?.shortName ?? "????" + connectedPeripheral.shortName = nodeInfo?.user?.shortName ?? "?" connectedPeripheral.longName = nodeInfo?.user?.longName ?? "unknown".localized } } @@ -516,6 +518,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate let version = decodedInfo.metadata.firmwareVersion[...(lastDotIndex ?? String.Index(utf16Offset: 6, in: decodedInfo.metadata.firmwareVersion))] nowKnown = true connectedVersion = String(version.dropLast()) + appState.firmwareVersion = connectedVersion } let supportedVersion = connectedVersion == "0.0.0" || self.minimumVersion.compare(connectedVersion, options: .numeric) == .orderedAscending || minimumVersion.compare(connectedVersion, options: .numeric) == .orderedSame @@ -613,11 +616,22 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(connectedPeripheral.num)) do { let fetchedNodeInfo = try context?.fetch(fetchNodeInfoRequest) as? [NodeInfoEntity] ?? [] - if fetchedNodeInfo.count == 1 && fetchedNodeInfo[0].mqttConfig != nil { - //Subscribe to Mqtt Client Proxy if enabled - if fetchedNodeInfo[0].mqttConfig?.proxyToClientEnabled ?? false { + if fetchedNodeInfo.count == 1 { + // Subscribe to Mqtt Client Proxy if enabled + if fetchedNodeInfo[0].mqttConfig != nil && fetchedNodeInfo[0].mqttConfig?.enabled ?? false && fetchedNodeInfo[0].mqttConfig?.proxyToClientEnabled ?? false { mqttManager.connectFromConfigSettings(node: fetchedNodeInfo[0]) + } else { + if mqttProxyConnected { + mqttManager.mqttClientProxy?.disconnect() + } } + // Set initial unread message badge states + let appState = AppState.shared + appState.unreadChannelMessages = fetchedNodeInfo[0].myInfo?.unreadMessages ?? 0 + appState.unreadDirectMessages = fetchedNodeInfo[0].user?.unreadMessages ?? 0 + //appState.connectedNode = fetchedNodeInfo[0] + UIApplication.shared.applicationIconBadgeNumber = appState.unreadChannelMessages + appState.unreadDirectMessages + } if fetchedNodeInfo.count == 1 && fetchedNodeInfo[0].rangeTestConfig?.enabled == true { wantRangeTestPackets = true; @@ -707,7 +721,6 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate if toUserNum > 0 { newMessage.toUser = fetchedUsers.first(where: { $0.num == toUserNum }) newMessage.toUser?.lastMessage = Date() - newMessage.toUser?.objectWillChange.send() } newMessage.fromUser = fetchedUsers.first(where: { $0.num == fromUserNum }) newMessage.isEmoji = isEmoji @@ -718,6 +731,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate } newMessage.messagePayload = message newMessage.messagePayloadMarkdown = generateMessageMarkdown(message: message) + newMessage.read = true let dataType = PortNum.textMessageApp var messageQuotesReplaced = message.replacingOccurrences(of: "’", with: "'") @@ -2143,7 +2157,7 @@ extension BLEManager: CBCentralManagerDelegate { print("â„šī¸ BLE Reconnecting to prefered peripheral: \(peripheral.name ?? "Unknown")") } let name = advertisementData[CBAdvertisementDataLocalNameKey] as? String - let device = Peripheral(id: peripheral.identifier.uuidString, num: 0, name: name ?? "Unknown", shortName: "????", longName: name ?? "Unknown", firmwareVersion: "Unknown", rssi: RSSI.intValue, lastUpdate: Date(), peripheral: peripheral) + let device = Peripheral(id: peripheral.identifier.uuidString, num: 0, name: name ?? "Unknown", shortName: "?", longName: name ?? "Unknown", firmwareVersion: "Unknown", rssi: RSSI.intValue, lastUpdate: Date(), peripheral: peripheral) let index = peripherals.map { $0.peripheral }.firstIndex(of: peripheral) if let peripheralIndex = index { diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index 0abe318a..042fc9d1 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -237,7 +237,7 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje return nil } // Not Found Insert - if fetchedNode.isEmpty && nodeInfo.hasUser { + if fetchedNode.isEmpty { let newNode = NodeInfoEntity(context: context) newNode.id = Int64(nodeInfo.num) @@ -296,6 +296,7 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje } do { try context.save() + print("💾 Saved a new Node Info For: \(String(nodeInfo.num))") return newNode } catch { context.rollback() @@ -314,7 +315,9 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje fetchedNode[0].channel = Int32(nodeInfo.channel) if nodeInfo.hasUser { - + if (fetchedNode[0].user == nil) { + fetchedNode[0].user = UserEntity(context: context) + } fetchedNode[0].user!.userId = nodeInfo.user.id fetchedNode[0].user!.num = Int64(nodeInfo.num) fetchedNode[0].user!.longName = nodeInfo.user.longName @@ -532,26 +535,26 @@ func routingPacket (packet: MeshPacket, connectedNodeNum: Int64, context: NSMana fetchedMessage![0].ackSNR = packet.rxSnr fetchedMessage![0].ackTimestamp = Int32(packet.rxTime) - if fetchedMessage![0].toUser != nil { - fetchedMessage![0].toUser?.objectWillChange.send() - } else { - let fetchMyInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "MyInfoEntity") - fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", connectedNodeNum) - do { - let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) as? [MyInfoEntity] - if fetchedMyInfo?.count ?? 0 > 0 { - - for ch in fetchedMyInfo![0].channels!.array as? [ChannelEntity] ?? [] { - - if ch.index == packet.channel { - ch.objectWillChange.send() - } - } - } - } catch { - - } - } +// if fetchedMessage![0].toUser != nil { +// //fetchedMessage![0].toUser?.objectWillChange.send() +// } else { +// let fetchMyInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "MyInfoEntity") +// fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", connectedNodeNum) +// do { +// let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) as? [MyInfoEntity] +// if fetchedMyInfo?.count ?? 0 > 0 { +// +// for ch in fetchedMyInfo![0].channels!.array as? [ChannelEntity] ?? [] { +// +// if ch.index == packet.channel { +// // ch.objectWillChange.send() +// } +// } +// } +// } catch { +// +// } +// } } else { return @@ -750,10 +753,9 @@ func textMessageAppPacket(packet: MeshPacket, blockRangeTest: Bool, connectedNod } newMessage.messagePayload = messageText newMessage.messagePayloadMarkdown = generateMessageMarkdown(message: messageText) - if packet.to != 4294967295 { + if packet.to != 4294967295 && newMessage.fromUser != nil { newMessage.fromUser?.lastMessage = Date() } - var messageSaved = false do { @@ -763,14 +765,20 @@ func textMessageAppPacket(packet: MeshPacket, blockRangeTest: Bool, connectedNod messageSaved = true if messageSaved { + let appState = AppState.shared if newMessage.fromUser != nil && newMessage.toUser != nil && !(newMessage.fromUser?.mute ?? false) { + // Set Unread Message Indicators + if packet.to == connectedNode { + appState.unreadDirectMessages = newMessage.toUser?.unreadMessages ?? 0 + UIApplication.shared.applicationIconBadgeNumber = appState.unreadChannelMessages + appState.unreadDirectMessages + } // Create an iOS Notification for the received DM message and schedule it immediately let manager = LocalNotificationManager() manager.notifications = [ Notification( id: ("notification.id.\(newMessage.messageId)"), title: "\(newMessage.fromUser?.longName ?? "unknown".localized)", - subtitle: "AKA \(newMessage.fromUser?.shortName ?? "???")", + subtitle: "AKA \(newMessage.fromUser?.shortName ?? "?")", content: messageText, target: "message" ) @@ -787,6 +795,9 @@ func textMessageAppPacket(packet: MeshPacket, blockRangeTest: Bool, connectedNod return } if !fetchedMyInfo.isEmpty { + appState.unreadChannelMessages = fetchedMyInfo[0].unreadMessages + UIApplication.shared.applicationIconBadgeNumber = appState.unreadChannelMessages + appState.unreadDirectMessages + for channel in (fetchedMyInfo[0].channels?.array ?? []) as? [ChannelEntity] ?? [] { if channel.index == newMessage.channel { context.refresh(channel, mergeChanges: true) @@ -798,7 +809,7 @@ func textMessageAppPacket(packet: MeshPacket, blockRangeTest: Bool, connectedNod Notification( id: ("notification.id.\(newMessage.messageId)"), title: "\(newMessage.fromUser?.longName ?? "unknown".localized)", - subtitle: "AKA \(newMessage.fromUser?.shortName ?? "???")", + subtitle: "AKA \(newMessage.fromUser?.shortName ?? "?")", content: messageText, target: "message") ] diff --git a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV17.xcdatamodel/contents b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV17.xcdatamodel/contents index b5b17a81..db7917c1 100644 --- a/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV17.xcdatamodel/contents +++ b/Meshtastic/Meshtastic.xcdatamodeld/MeshtasticDataModelV17.xcdatamodel/contents @@ -1,5 +1,5 @@ - + @@ -181,6 +181,9 @@ + + + diff --git a/Meshtastic/MeshtasticApp.swift b/Meshtastic/MeshtasticApp.swift index bcf3c8a5..9a43be75 100644 --- a/Meshtastic/MeshtasticApp.swift +++ b/Meshtastic/MeshtasticApp.swift @@ -17,6 +17,7 @@ struct MeshtasticAppleApp: App { @State var incomingUrl: URL? @State var channelSettings: String? @StateObject var appState = AppState.shared + var body: some Scene { WindowGroup { ContentView() @@ -143,4 +144,8 @@ class AppState: ObservableObject { static let shared = AppState() @Published var tabSelection: Tab = .ble + @Published var unreadDirectMessages: Int = 0 + @Published var unreadChannelMessages: Int = 0 + @Published var firmwareVersion: String = "0.0.0" + @Published var connectedNode: NodeInfoEntity? } diff --git a/Meshtastic/MeshtasticAppDelegate.swift b/Meshtastic/MeshtasticAppDelegate.swift index 0d130ccb..0918f9ea 100644 --- a/Meshtastic/MeshtasticAppDelegate.swift +++ b/Meshtastic/MeshtasticAppDelegate.swift @@ -9,7 +9,12 @@ import SwiftUI class MeshtasticAppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { - print("App launched!") + print("🚀 Meshtstic Apple App launched!") + // Default User Default Values + UserDefaults.standard.register(defaults: ["blockRangeTest" : true]) + UserDefaults.standard.register(defaults: ["meshMapRecentering" : true]) + UserDefaults.standard.register(defaults: ["meshMapShowNodeHistory" : true]) + UserDefaults.standard.register(defaults: ["meshMapShowRouteLines" : true]) UNUserNotificationCenter.current().delegate = self return true } diff --git a/Meshtastic/Model/PeripheralModel.swift b/Meshtastic/Model/PeripheralModel.swift index ef7230a0..2382f7cb 100644 --- a/Meshtastic/Model/PeripheralModel.swift +++ b/Meshtastic/Model/PeripheralModel.swift @@ -12,18 +12,6 @@ struct Peripheral: Identifiable { var lastUpdate: Date var peripheral: CBPeripheral -// init(id: String, num: Int64, name: String, shortName: String, longName: String, firmwareVersion: String, rssi: Int, lastUpdate: Date, peripheral: CBPeripheral) { -// self.id = id -// self.num = num -// self.name = name -// self.shortName = shortName -// self.longName = longName -// self.firmwareVersion = firmwareVersion -// self.rssi = rssi -// self.lastUpdate = lastUpdate -// self.peripheral = peripheral -// } - func getSignalStrength() -> BLESignalStrength { if NSNumber(value: rssi).compare(NSNumber(-65)) == ComparisonResult.orderedDescending { return BLESignalStrength.strong diff --git a/Meshtastic/Persistence/UpdateCoreData.swift b/Meshtastic/Persistence/UpdateCoreData.swift index 72bf71a6..17310c61 100644 --- a/Meshtastic/Persistence/UpdateCoreData.swift +++ b/Meshtastic/Persistence/UpdateCoreData.swift @@ -143,7 +143,7 @@ func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext) print("đŸ’Ĩ Error Inserting New Core Data MyInfoEntity: \(nsError)") } newNode.myInfo = myInfoEntity - newNode.objectWillChange.send() + //newNode.objectWillChange.send() } else { // Update an existing node @@ -476,7 +476,6 @@ func upsertLoRaConfigPacket(config: Meshtastic.Config.LoRaConfig, nodeNum: Int64 } do { try context.save() - context.refresh(fetchedNode[0], mergeChanges: true) print("💾 Updated LoRa Config for node number: \(String(nodeNum))") } catch { context.rollback() diff --git a/Meshtastic/Views/Bluetooth/Connect.swift b/Meshtastic/Views/Bluetooth/Connect.swift index efde3cf5..fa1f79ee 100644 --- a/Meshtastic/Views/Bluetooth/Connect.swift +++ b/Meshtastic/Views/Bluetooth/Connect.swift @@ -52,7 +52,7 @@ struct Connect: View { } HStack { VStack(alignment: .center) { - CircleText(text: node?.user?.shortName ?? "???", color: Color(UIColor(hex: UInt32(node?.num ?? 0))), circleSize: 90, fontSize: (node?.user?.shortName ?? "???").isEmoji() ? 52 : (node?.user?.shortName?.count ?? 0 == 4 ? 26 : 36), textColor: UIColor(hex: UInt32(node?.num ?? 0)).isLight() ? .black : .white ) + CircleText(text: node?.user?.shortName ?? "?", color: Color(UIColor(hex: UInt32(node?.num ?? 0))), circleSize: 90) } .padding(.trailing) VStack(alignment: .leading) { @@ -108,7 +108,7 @@ struct Connect: View { } #endif Text("Num: \(String(node!.num))") - Text("Short Name: \(node?.user?.shortName ?? "????")") + Text("Short Name: \(node?.user?.shortName ?? "?")") Text("Long Name: \(node?.user?.longName ?? "unknown".localized)") Text("BLE RSSI: \(bleManager.connectedPeripheral.rssi)") } @@ -250,7 +250,7 @@ struct Connect: View { .navigationTitle("bluetooth") .navigationBarItems(leading: MeshtasticLogo(), trailing: ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????", mqttProxyConnected: bleManager.mqttProxyConnected) + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?", mqttProxyConnected: bleManager.mqttProxyConnected) }) } .sheet(isPresented: $invalidFirmwareVersion, onDismiss: didDismissSheet) { diff --git a/Meshtastic/Views/ContentView.swift b/Meshtastic/Views/ContentView.swift index a80e06c6..35b2a9ea 100644 --- a/Meshtastic/Views/ContentView.swift +++ b/Meshtastic/Views/ContentView.swift @@ -13,7 +13,7 @@ struct ContentView: View { Label("messages", systemImage: "message") } .tag(Tab.contacts) - // .badge(42) + .badge(appState.unreadDirectMessages + appState.unreadChannelMessages) Connect() .tabItem { Label("bluetooth", systemImage: "antenna.radiowaves.left.and.right") diff --git a/Meshtastic/Views/Helpers/CircleText.swift b/Meshtastic/Views/Helpers/CircleText.swift index 2f8ebb05..bd2f0b0d 100644 --- a/Meshtastic/Views/Helpers/CircleText.swift +++ b/Meshtastic/Views/Helpers/CircleText.swift @@ -8,29 +8,44 @@ import SwiftUI struct CircleText: View { var text: String var color: Color - var circleSize: CGFloat? = 60 - var fontSize: CGFloat? = 20 - var brightness: Double? = 0 - var textColor: Color? = .white - + var circleSize: CGFloat = 45 + var body: some View { - let font = Font.system(size: fontSize!) - ZStack { Circle() .fill(color) - .brightness(brightness ?? 0) .frame(width: circleSize, height: circleSize) - Text(text).textCase(.uppercase).font(font).foregroundColor(textColor).fixedSize() - .frame(width: circleSize, height: circleSize, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/).offset(x: 0, y: 0) + Text(text) + .textCase(.uppercase) + .foregroundColor(color.isLight() ? .black : .white) + .font(.system(size: 500)) + .minimumScaleFactor(0.001) + .frame(width: circleSize * 0.94, height: circleSize * 0.94, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/) } + .aspectRatio(1, contentMode: .fit) } } struct CircleText_Previews: PreviewProvider { static var previews: some View { - CircleText(text: "MOMO", color: Color.accentColor) - .previewLayout(.fixed(width: 300, height: 100)) + VStack { + CircleText(text: "MOMO", color: Color.secondary, circleSize: 80) + .previewLayout(.fixed(width: 300, height: 100)) + CircleText(text: "WWWW", color: Color.accentColor, circleSize: 80) + .previewLayout(.fixed(width: 300, height: 100)) + CircleText(text: "LCP", color: Color.primary, circleSize: 80) + .previewLayout(.fixed(width: 300, height: 100)) + CircleText(text: "69", color: Color.green, circleSize: 80) + .previewLayout(.fixed(width: 300, height: 100)) + CircleText(text: "N1", color: Color.yellow, circleSize: 80) + .previewLayout(.fixed(width: 300, height: 100)) + CircleText(text: "8", color: Color.purple, circleSize: 80) + .previewLayout(.fixed(width: 300, height: 100)) + CircleText(text: "😝", color: Color.red, circleSize: 80) + .previewLayout(.fixed(width: 300, height: 100)) + CircleText(text: "🍔", color: Color.brown, circleSize: 80) + .previewLayout(.fixed(width: 300, height: 100)) + } } } diff --git a/Meshtastic/Views/Helpers/Node/NodeInfoView.swift b/Meshtastic/Views/Helpers/Node/NodeInfoView.swift index f6de23a6..362de937 100644 --- a/Meshtastic/Views/Helpers/Node/NodeInfoView.swift +++ b/Meshtastic/Views/Helpers/Node/NodeInfoView.swift @@ -27,7 +27,7 @@ struct NodeInfoView: View { if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac { HStack { VStack(alignment: .center) { - CircleText(text: node.user?.shortName ?? "???", color: Color(UIColor(hex: UInt32(node.num))), circleSize: 150, fontSize: (node.user?.shortName ?? "???").isEmoji() ? 105 : 55, textColor: UIColor(hex: UInt32(node.num)).isLight() ? .black : .white ) + CircleText(text: node.user?.shortName ?? "?", color: Color(UIColor(hex: UInt32(node.num))), circleSize: 150) } Divider() VStack { @@ -58,8 +58,9 @@ struct NodeInfoView: View { } Divider() } - let deviceMetrics = node.telemetries?.filtered(using: NSPredicate(format: "metricsType == 0")) - if deviceMetrics?.count ?? 0 >= 1 { + + if node.hasDeviceMetrics { + let deviceMetrics = node.telemetries?.filtered(using: NSPredicate(format: "metricsType == 0")) let mostRecent = deviceMetrics?.lastObject as? TelemetryEntity VStack(alignment: .center) { BatteryGauge(batteryLevel: Double(mostRecent?.batteryLevel ?? 0)) @@ -123,7 +124,7 @@ struct NodeInfoView: View { HStack { VStack(alignment: .center) { - CircleText(text: node.user?.shortName ?? "???", color: Color(UIColor(hex: UInt32(node.num))), circleSize: 65, fontSize: (node.user?.shortName ?? "???").isEmoji() ? 42 : 20, textColor: UIColor(hex: UInt32(node.num)).isLight() ? .black : .white ) + CircleText(text: node.user?.shortName ?? "?", color: Color(UIColor(hex: UInt32(node.num))), circleSize: 65) } if node.user != nil { Divider() @@ -177,7 +178,7 @@ struct NodeInfoView: View { .symbolRenderingMode(.hierarchical) Text("User Id:").font(.title2) } - Text(node.user?.userId ?? "??????").font(.title3).foregroundColor(.gray) + Text(node.user?.userId ?? "?").font(.title3).foregroundColor(.gray) } Divider() VStack { @@ -196,7 +197,7 @@ struct NodeInfoView: View { VStack { - if (node.positions?.count ?? 0) > 0 { + if node.hasPositions{ NavigationLink { PositionLog(node: node) @@ -213,20 +214,22 @@ struct NodeInfoView: View { Divider() } - if (node.telemetries?.count ?? 0) > 0 { - + if node.hasDeviceMetrics { + NavigationLink { DeviceMetricsLog(node: node) } label: { - + Image(systemName: "flipphone") .symbolRenderingMode(.hierarchical) .font(.title) - + Text("Device Metrics Log") .font(.title3) } Divider() + } + if node.hasEnvironmentMetrics { NavigationLink { EnvironmentMetricsLog(node: node) } label: { diff --git a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift index 1e0a767b..62e834b0 100644 --- a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift +++ b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift @@ -75,23 +75,24 @@ struct MapViewSwiftUI: UIViewRepresentable { mapView.showsBuildings = true mapView.showsScale = true mapView.showsTraffic = true - #if targetEnvironment(macCatalyst) - // Show the default always visible compass and the mac only controls - mapView.showsCompass = true - mapView.showsZoomControls = true - mapView.showsPitchControl = true - #else - #if os(iOS) - // Move the default compass under the mapbuttons control + mapView.showsCompass = false let compass = MKCompassButton(mapView: mapView) compass.translatesAutoresizingMaskIntoConstraints = false + #if targetEnvironment(macCatalyst) + // Show the default always visible compass and the mac only controls + compass.compassVisibility = .visible + mapView.addSubview(compass) + mapView.showsZoomControls = true + mapView.showsPitchControl = true + compass.trailingAnchor.constraint(equalTo: mapView.trailingAnchor, constant: -115).isActive = true + compass.bottomAnchor.constraint(equalTo: mapView.bottomAnchor, constant: -5).isActive = true + #else compass.compassVisibility = .adaptive mapView.addSubview(compass) compass.trailingAnchor.constraint(equalTo: mapView.trailingAnchor, constant: -5).isActive = true compass.topAnchor.constraint(equalTo: mapView.topAnchor, constant: 145).isActive = true #endif - #endif } private func setMapBaseLayer(mapView: MKMapView) { // Avoid refreshing UI if selectedLayer has not changed @@ -251,18 +252,17 @@ struct MapViewSwiftUI: UIViewRepresentable { mapView.removeAnnotations(mapView.annotations) mapView.addAnnotations(waypoints) } + mapView.addAnnotations(showNodeHistory ? positions : latest) if userTrackingMode == MKUserTrackingMode.none { mapView.showsUserLocation = false if UserDefaults.enableMapRecentering { if latest.count == 1 { mapView.fit(annotations: showNodeHistory ? positions : latest, andShow: true) } else { - mapView.addAnnotations(showNodeHistory ? positions : latest) mapView.fitAllAnnotations() } } } else { - mapView.addAnnotations(showNodeHistory ? positions : latest) mapView.showsUserLocation = true } mapView.setUserTrackingMode(userTrackingMode, animated: true) @@ -290,11 +290,11 @@ struct MapViewSwiftUI: UIViewRepresentable { annotationView.tag = -1 annotationView.canShowCallout = true if positionAnnotation.latest { - annotationView.markerTintColor = .systemRed + annotationView.markerTintColor = UIColor(hex: UInt32(positionAnnotation.nodePosition?.num ?? 0)).darker() annotationView.displayPriority = .required annotationView.titleVisibility = .visible } else { - annotationView.markerTintColor = UIColor(hex: UInt32(positionAnnotation.nodePosition?.num ?? 0)) + annotationView.markerTintColor = UIColor(hex: UInt32(positionAnnotation.nodePosition?.num ?? 0)).lighter() annotationView.displayPriority = .defaultHigh annotationView.titleVisibility = .adaptive } @@ -409,9 +409,7 @@ struct MapViewSwiftUI: UIViewRepresentable { } func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) { switch view.annotation { - case let positionAnnotation as PositionEntity: - print(positionAnnotation) - case let waypointAnnotation as WaypointEntity: + case _ as WaypointEntity: // Only Allow Edit for waypoint annotations with a id if view.tag > 0 { parent.onWaypointEdit(view.tag) @@ -442,7 +440,7 @@ struct MapViewSwiftUI: UIViewRepresentable { if let routePolyline = overlay as? MKPolyline { let titleString = routePolyline.title ?? "0" let renderer = MKPolylineRenderer(polyline: routePolyline) - renderer.strokeColor = UIColor(hex: UInt32(titleString) ?? 0) + renderer.strokeColor = UIColor(hex: UInt32(titleString) ?? 0).lighter() renderer.lineWidth = 8 return renderer } diff --git a/Meshtastic/Views/Messages/ChannelList.swift b/Meshtastic/Views/Messages/ChannelList.swift index 39c0cff3..952f54e8 100644 --- a/Meshtastic/Views/Messages/ChannelList.swift +++ b/Meshtastic/Views/Messages/ChannelList.swift @@ -10,6 +10,7 @@ import CoreData struct ChannelList: View { + @StateObject var appState = AppState.shared @Environment(\.managedObjectContext) var context @EnvironmentObject var bleManager: BLEManager @@ -32,7 +33,7 @@ struct ChannelList: View { ForEach(node!.myInfo!.channels!.array as! [ChannelEntity], id: \.self) { (channel: ChannelEntity) in if channel.name?.lowercased() ?? "" != "admin" && channel.name?.lowercased() ?? "" != "gpio" && channel.name?.lowercased() ?? "" != "serial" { - NavigationLink(destination: ChannelMessageList(channel: channel)) { + NavigationLink(destination: ChannelMessageList(myInfo: node!.myInfo!, channel: channel)) { let mostRecent = channel.allPrivateMessages.last(where: { $0.channel == channel.index }) let lastMessageTime = Date(timeIntervalSince1970: TimeInterval(Int64((mostRecent?.messageTimestamp ?? 0 )))) @@ -47,7 +48,7 @@ struct ChannelList: View { .foregroundColor(.accentColor) .brightness(0.2) } - CircleText(text: String(channel.index), color: .accentColor, circleSize: 45, fontSize: 40) + CircleText(text: String(channel.index), color: .accentColor) .brightness(0.2) VStack(alignment: .leading){ @@ -100,23 +101,6 @@ struct ChannelList: View { } .frame(height: 62) .contextMenu { - Button { - channel.mute = !channel.mute - - do { - try context.save() - // Would rather not do this but the merge changes on - // A single object is only working on mac GVH - context.refreshAllObjects() - // context.refresh(channel, mergeChanges: true) - } catch { - context.rollback() - print("đŸ’Ĩ Save Channel Mute Error") - } - } label: { - Label(channel.mute ? "Show Alerts" : "Hide Alerts", systemImage: channel.mute ? "bell" : "bell.slash") - } - if channel.allPrivateMessages.count > 0 { Button(role: .destructive) { isPresentingDeleteChannelMessagesConfirm = true @@ -134,6 +118,7 @@ struct ChannelList: View { Button(role: .destructive) { deleteChannelMessages(channel: channelSelection!, context: context) context.refresh(node!.myInfo!, mergeChanges: true) + UIApplication.shared.applicationIconBadgeNumber = appState.unreadChannelMessages + appState.unreadDirectMessages channelSelection = nil } label: { Text("delete") diff --git a/Meshtastic/Views/Messages/ChannelMessageList.swift b/Meshtastic/Views/Messages/ChannelMessageList.swift index 4186dd2a..df4be238 100644 --- a/Meshtastic/Views/Messages/ChannelMessageList.swift +++ b/Meshtastic/Views/Messages/ChannelMessageList.swift @@ -10,6 +10,7 @@ import CoreData struct ChannelMessageList: View { + @StateObject var appState = AppState.shared @Environment(\.managedObjectContext) var context @EnvironmentObject var bleManager: BLEManager @@ -23,6 +24,7 @@ struct ChannelMessageList: View { var maxbytes = 228 @FocusState var focusedField: Field? + @ObservedObject var myInfo: MyInfoEntity @ObservedObject var channel: ChannelEntity @State var showDeleteMessageAlert = false @State private var deleteMessageId: Int64 = 0 @@ -56,7 +58,7 @@ struct ChannelMessageList: View { HStack(alignment: .top) { if currentUser { Spacer(minLength: 50) } if !currentUser { - CircleText(text: message.fromUser?.shortName ?? "????", color: Color(UIColor(hex: UInt32(message.fromUser?.num ?? 0))), circleSize: 44, fontSize: 14, textColor: UIColor(hex: UInt32(message.fromUser?.num ?? 0)).isLight() ? .black : .white) + CircleText(text: message.fromUser?.shortName ?? "?", color: Color(UIColor(hex: UInt32(message.fromUser?.num ?? 0))), circleSize: 44) .padding(.all, 5) .offset(y: -5) } @@ -170,7 +172,7 @@ struct ChannelMessageList: View { VStack { let image = tapback.messagePayload!.image(fontSize: 20) Image(uiImage: image!).font(.caption) - Text("\(tapback.fromUser?.shortName ?? "????")") + Text("\(tapback.fromUser?.shortName ?? "?")") .font(.caption2) .foregroundColor(.gray) .fixedSize() @@ -229,10 +231,12 @@ struct ChannelMessageList: View { .onAppear { if !message.read { message.read = true - message.toUser?.objectWillChange.send() do { try context.save() print("Read message \(message.messageId) ") + appState.unreadChannelMessages = myInfo.unreadMessages + UIApplication.shared.applicationIconBadgeNumber = appState.unreadChannelMessages + appState.unreadDirectMessages + context.refresh(myInfo, mergeChanges: true) } catch { print("Failed to read message \(message.messageId)") } @@ -401,7 +405,7 @@ struct ChannelMessageList: View { .toolbar { ToolbarItem(placement: .principal) { HStack { - CircleText(text: String(channel.index), color: .accentColor, circleSize: 44, fontSize: 30).fixedSize() + CircleText(text: String(channel.index), color: .accentColor, circleSize: 44).fixedSize() Text(String(channel.name ?? "unknown".localized).camelCaseToWords()).font(.headline) } } @@ -410,7 +414,7 @@ struct ChannelMessageList: View { ConnectedDevice( bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, - name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") + name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?") } } } diff --git a/Meshtastic/Views/Messages/Contacts.swift b/Meshtastic/Views/Messages/Contacts.swift deleted file mode 100644 index 3edaf03d..00000000 --- a/Meshtastic/Views/Messages/Contacts.swift +++ /dev/null @@ -1,301 +0,0 @@ -// -// Contacts.swift -// MeshtasticApple -// -// Created by Garth Vander Houwen on 12/21/21. -// -// -//import SwiftUI -//import CoreData -// -//struct Contacts: View { -// -// @Environment(\.managedObjectContext) var context -// @EnvironmentObject var bleManager: BLEManager -// -// @FetchRequest( -// sortDescriptors: [NSSortDescriptor(key: "lastMessage", ascending: false), NSSortDescriptor(key: "longName", ascending: true)], -// animation: .default) -// -// private var users: FetchedResults -// @State var node: NodeInfoEntity? -// @State private var userSelection: UserEntity? // Nothing selected by default. -// @State private var channelSelection: ChannelEntity? // Nothing selected by default. -// @State private var isPresentingDeleteChannelMessagesConfirm: Bool = false -// @State private var isPresentingDeleteUserMessagesConfirm: Bool = false -// @State private var isPresentingTraceRouteSentAlert = false -// -// var body: some View { -// -// NavigationSplitView { -// let localeDateFormat = DateFormatter.dateFormat(fromTemplate: "yyMMdd", options: 0, locale: Locale.current) -// let dateFormatString = (localeDateFormat ?? "MM/dd/YY") -// List { -// Section(header: Text("channels")) { -// // Display Contacts for the rest of the non admin channels -// if node != nil && node!.myInfo != nil && node!.myInfo!.channels != nil { -// ForEach(node!.myInfo!.channels!.array as! [ChannelEntity], id: \.self) { (channel: ChannelEntity) in -// if channel.name?.lowercased() ?? "" != "admin" && channel.name?.lowercased() ?? "" != "gpio" && channel.name?.lowercased() ?? "" != "serial" { -// -// NavigationLink(destination: ChannelMessageList(channel: channel)) { -// -// let mostRecent = channel.allPrivateMessages.last(where: { $0.channel == channel.index }) -// let lastMessageTime = Date(timeIntervalSince1970: TimeInterval(Int64((mostRecent?.messageTimestamp ?? 0 )))) -// let lastMessageDay = Calendar.current.dateComponents([.day], from: lastMessageTime).day ?? 0 -// let currentDay = Calendar.current.dateComponents([.day], from: Date()).day ?? 0 -// -// -// ZStack { -// Image(systemName: "circle.fill") -// .opacity(channel.unreadMessages > 0 ? 1 : 0) -// .font(.system(size: 10)) -// .foregroundColor(.accentColor) -// .brightness(0.2) -// } -// CircleText(text: String(channel.index), color: .accentColor, circleSize: 45, fontSize: 40) -// .brightness(0.2) -// -// VStack(alignment: .leading){ -// HStack{ -// if channel.name?.isEmpty ?? false { -// if channel.role == 1 { -// Text(String("PrimaryChannel").camelCaseToWords()) -// } else { -// Text(String("Channel \(channel.index)").camelCaseToWords()) -// } -// } else { -// Text(String(channel.name ?? "Channel \(channel.index)").camelCaseToWords()) -// } -// -// Spacer() -// -// if channel.allPrivateMessages.count > 0 { -// -// if lastMessageDay == currentDay { -// Text(lastMessageTime, style: .time ) -// .font(.system(size: 16)) -// .foregroundColor(.secondary) -// } else if lastMessageDay == (currentDay - 1) { -// Text("Yesterday") -// .font(.system(size: 16)) -// .foregroundColor(.secondary) -// } else if lastMessageDay < (currentDay - 1) && lastMessageDay > (currentDay - 5) { -// Text(lastMessageTime.formattedDate(format: dateFormatString)) -// .font(.system(size: 16)) -// .foregroundColor(.secondary) -// } else if lastMessageDay < (currentDay - 1800) { -// Text(lastMessageTime.formattedDate(format: dateFormatString)) -// .font(.system(size: 16)) -// .foregroundColor(.secondary) -// } -// } -// -//// Image(systemName: "chevron.forward") -//// .font(.caption) -//// .foregroundColor(.secondary) -// } -// -// if channel.allPrivateMessages.count > 0 { -// HStack(alignment: .top) { -// Text("\(mostRecent != nil ? mostRecent!.messagePayload! : " ")") -// .font(.system(size: 16)) -// .foregroundColor(.secondary) -// } -// } -// } -// } -// .frame(height: 62) -// .contextMenu { -// Button { -// channel.mute = !channel.mute -// -// do { -// try context.save() -// // Would rather not do this but the merge changes on -// // A single object is only working on mac GVH -// context.refreshAllObjects() -// // context.refresh(channel, mergeChanges: true) -// } catch { -// context.rollback() -// print("đŸ’Ĩ Save Channel Mute Error") -// } -// } label: { -// Label(channel.mute ? "Show Alerts" : "Hide Alerts", systemImage: channel.mute ? "bell" : "bell.slash") -// } -// -// if channel.allPrivateMessages.count > 0 { -// Button(role: .destructive) { -// isPresentingDeleteChannelMessagesConfirm = true -// channelSelection = channel -// } label: { -// Label("Delete Messages", systemImage: "trash") -// } -// } -// } -// .confirmationDialog( -// "This conversation will be deleted.", -// isPresented: $isPresentingDeleteChannelMessagesConfirm, -// titleVisibility: .visible -// ) { -// Button(role: .destructive) { -// deleteChannelMessages(channel: channelSelection!, context: context) -// context.refresh(node!.myInfo!, mergeChanges: true) -// channelSelection = nil -// } label: { -// Text("delete") -// } -// } -// } -// } -// .padding([.top, .bottom]) -// } -// } -// Section(header: Text("direct.messages")) { -// -// ForEach(users) { (user: UserEntity) in -// -// let mostRecent = user.messageList.last -// let lastMessageTime = Date(timeIntervalSince1970: TimeInterval(Int64((mostRecent?.messageTimestamp ?? 0 )))) -// let lastMessageDay = Calendar.current.dateComponents([.day], from: lastMessageTime).day ?? 0 -// let currentDay = Calendar.current.dateComponents([.day], from: Date()).day ?? 0 -// if user.num != bleManager.connectedPeripheral?.num ?? 0 { -// -// NavigationLink(destination: UserMessageList(user: user)) { -// ZStack { -// Image(systemName: "circle.fill") -// .opacity(user.unreadMessages > 0 ? 1 : 0) -// .font(.system(size: 10)) -// .foregroundColor(.accentColor) -// .brightness(0.2) -// } -// -// CircleText(text: user.shortName ?? "???", color: Color(UIColor(hex: UInt32(user.num))), circleSize: 45, fontSize: (user.shortName ?? "???").isEmoji() ? 32 : (user.shortName?.count ?? 0 == 4 ? 14 : (user.shortName?.count ?? 0 == 3 ? 18 : 22)), brightness: 0.0, textColor: UIColor(hex: UInt32(user.num)).isLight() ? .black : .white) -// -// VStack(alignment: .leading){ -// HStack{ -// Text(user.longName ?? "unknown".localized) -// -// Spacer() -// -// if user.messageList.count > 0 { -// if lastMessageDay == currentDay { -// Text(lastMessageTime, style: .time ) -// .font(.system(size: 16)) -// .foregroundColor(.secondary) -// } else if lastMessageDay == (currentDay - 1) { -// Text("Yesterday") -// .font(.system(size: 16)) -// .foregroundColor(.secondary) -// } else if lastMessageDay < (currentDay - 1) && lastMessageDay > (currentDay - 5) { -// Text(lastMessageTime.formattedDate(format: dateFormatString)) -// .font(.system(size: 16)) -// .foregroundColor(.secondary) -// } else if lastMessageDay < (currentDay - 1800) { -// Text(lastMessageTime.formattedDate(format: dateFormatString)) -// .font(.system(size: 16)) -// .foregroundColor(.secondary) -// } -// } -// -//// Image(systemName: "chevron.forward") -//// .font(.caption) -//// .foregroundColor(.secondary) -// } -// -// if user.messageList.count > 0 { -// HStack(alignment: .top) { -// Text("\(mostRecent != nil ? mostRecent!.messagePayload! : " ")") -// .font(.system(size: 16)) -// .foregroundColor(.secondary) -// } -// } -// } -// } -// .frame(height: 62) -// .contextMenu { -// Button { -// user.mute = !user.mute -// do { -// try context.save() -// } catch { -// context.rollback() -// print("đŸ’Ĩ Save User Mute Error") -// } -// } label: { -// Label(user.mute ? "Show Alerts" : "Hide Alerts", systemImage: user.mute ? "bell" : "bell.slash") -// } -// Button { -// let success = bleManager.sendTraceRouteRequest(destNum: user.num, wantResponse: true) -// if success { -// isPresentingTraceRouteSentAlert = true -// } -// } label: { -// Label("Trace Route", systemImage: "signpost.right.and.left") -// } -// if user.messageList.count > 0 { -// Button(role: .destructive) { -// isPresentingDeleteUserMessagesConfirm = true -// userSelection = user -// } label: { -// Label("Delete Messages", systemImage: "trash") -// } -// } -// } -// .alert( -// "Trace Route Sent", -// isPresented: $isPresentingTraceRouteSentAlert -// ) { -// Button("OK", role: .cancel) { } -// } -// message: { -// Text("This could take a while, response will appear in the mesh log.") -// } -// .confirmationDialog( -// "This conversation will be deleted.", -// isPresented: $isPresentingDeleteUserMessagesConfirm, -// titleVisibility: .visible -// ) { -// Button(role: .destructive) { -// deleteUserMessages(user: userSelection!, context: context) -// context.refresh(node!.user!, mergeChanges: true) -// } label: { -// Text("delete") -// } -// } -// } -// } -// } -// } -// .listStyle(.grouped) -// .navigationTitle("contacts") -// .navigationBarItems(leading: -// MeshtasticLogo() -// ) -// .onAppear { -// self.bleManager.context = context -// if UserDefaults.preferredPeripheralId.count > 0 { -// let fetchNodeInfoRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") -// fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(bleManager.connectedPeripheral?.num ?? -1)) -// do { -// guard let fetchedNode = try context.fetch(fetchNodeInfoRequest) as? [NodeInfoEntity] else { -// return -// } -// // Found a node, check it for a region -// if !fetchedNode.isEmpty { -// node = fetchedNode[0] -// } -// } catch { -// -// } -// } -// } -// } detail: { -// if let user = userSelection { -// UserMessageList(user: user) -// -// } else { -// Text("select.contact") -// } -// } -// } -//} diff --git a/Meshtastic/Views/Messages/Messages.swift b/Meshtastic/Views/Messages/Messages.swift index 1acbb673..c7cb7917 100644 --- a/Meshtastic/Views/Messages/Messages.swift +++ b/Meshtastic/Views/Messages/Messages.swift @@ -10,6 +10,7 @@ import CoreData struct Messages: View { + @StateObject var appState = AppState.shared @Environment(\.managedObjectContext) var context @EnvironmentObject var bleManager: BLEManager @@ -38,6 +39,7 @@ struct Messages: View { .brightness(0.2) Text("channels") .font(.title2) + .badge(appState.unreadChannelMessages) } NavigationLink { UserList(node: node) @@ -48,9 +50,11 @@ struct Messages: View { .brightness(0.2) Text("direct.messages") .font(.title2) + .badge(appState.unreadDirectMessages) } } .navigationTitle("messages") + .navigationBarTitleDisplayMode(.large) .navigationBarItems(leading: MeshtasticLogo()) .onAppear { self.bleManager.context = context diff --git a/Meshtastic/Views/Messages/UserList.swift b/Meshtastic/Views/Messages/UserList.swift index 9b0c2c49..9d3e050e 100644 --- a/Meshtastic/Views/Messages/UserList.swift +++ b/Meshtastic/Views/Messages/UserList.swift @@ -10,6 +10,9 @@ import CoreData struct UserList: View { + @StateObject var appState = AppState.shared + @Environment(\.managedObjectContext) var context + @EnvironmentObject var bleManager: BLEManager @State private var searchText = "" var usersQuery: Binding { Binding { @@ -19,12 +22,8 @@ struct UserList: View { users.nsPredicate = newValue.isEmpty ? nil : NSPredicate(format: "longName CONTAINS[c] %@ OR shortName CONTAINS[c] %@", newValue, newValue) } } - - @Environment(\.managedObjectContext) var context - @EnvironmentObject var bleManager: BLEManager - @FetchRequest( - sortDescriptors: [NSSortDescriptor(key: "lastMessage", ascending: false), NSSortDescriptor(key: "longName", ascending: true)], + sortDescriptors: [NSSortDescriptor(key: "lastMessage", ascending: false), NSSortDescriptor(key: "vip", ascending: false), NSSortDescriptor(key: "longName", ascending: true)], animation: .default) private var users: FetchedResults @@ -56,14 +55,17 @@ struct UserList: View { .brightness(0.2) } - CircleText(text: user.shortName ?? "???", color: Color(UIColor(hex: UInt32(user.num))), circleSize: 45, fontSize: (user.shortName ?? "???").isEmoji() ? 32 : (user.shortName?.count ?? 0 == 4 ? 14 : (user.shortName?.count ?? 0 == 3 ? 18 : 22)), brightness: 0.0, textColor: UIColor(hex: UInt32(user.num)).isLight() ? .black : .white) + CircleText(text: user.shortName ?? "?", color: Color(UIColor(hex: UInt32(user.num)))) VStack(alignment: .leading){ HStack{ Text(user.longName ?? "unknown".localized) Spacer() - + if user.vip { + Image(systemName: "star.fill") + .foregroundColor(.secondary) + } if user.messageList.count > 0 { if lastMessageDay == currentDay { Text(lastMessageTime, style: .time ) @@ -99,6 +101,17 @@ struct UserList: View { } .frame(height: 62) .contextMenu { + Button { + user.vip = !user.vip + do { + try context.save() + } catch { + context.rollback() + print("đŸ’Ĩ Save User VIP Error") + } + } label: { + Label(user.vip ? "Un-Favorite" : "Favorite", systemImage: user.vip ? "star.slash.fill" : "star.fill") + } Button { user.mute = !user.mute do { @@ -144,6 +157,7 @@ struct UserList: View { Button(role: .destructive) { deleteUserMessages(user: userSelection!, context: context) context.refresh(node!.user!, mergeChanges: true) + UIApplication.shared.applicationIconBadgeNumber = appState.unreadChannelMessages + appState.unreadDirectMessages } label: { Text("delete") } diff --git a/Meshtastic/Views/Messages/UserMessageList.swift b/Meshtastic/Views/Messages/UserMessageList.swift index d6fbacdc..e93bffd5 100644 --- a/Meshtastic/Views/Messages/UserMessageList.swift +++ b/Meshtastic/Views/Messages/UserMessageList.swift @@ -10,6 +10,7 @@ import CoreData struct UserMessageList: View { + @StateObject var appState = AppState.shared @Environment(\.managedObjectContext) var context @EnvironmentObject var bleManager: BLEManager @@ -160,7 +161,7 @@ struct UserMessageList: View { VStack { let image = tapback.messagePayload!.image(fontSize: 20) Image(uiImage: image!).font(.caption) - Text("\(tapback.fromUser?.shortName ?? "????")") + Text("\(tapback.fromUser?.shortName ?? "?")") .font(.caption2) .foregroundColor(.gray) .fixedSize() @@ -219,10 +220,12 @@ struct UserMessageList: View { .onAppear { if !message.read { message.read = true - message.toUser?.objectWillChange.send() do { try context.save() print("Read message \(message.messageId) ") + appState.unreadDirectMessages = user.unreadMessages + UIApplication.shared.applicationIconBadgeNumber = appState.unreadChannelMessages + appState.unreadDirectMessages + } catch { print("Failed to read message \(message.messageId)") } @@ -364,7 +367,7 @@ struct UserMessageList: View { .toolbar { ToolbarItem(placement: .principal) { HStack { - CircleText(text: user.shortName ?? "???", color: Color(UIColor(hex: UInt32(user.num))), circleSize: 44, fontSize: (user.shortName ?? "???").isEmoji() ? 32 : (user.shortName?.count ?? 0 == 4 ? 14 : (user.shortName?.count ?? 0 == 3 ? 18 : 22)), brightness: 0.0, textColor: UIColor(hex: UInt32(user.num)).isLight() ? .black : .white) + CircleText(text: user.shortName ?? "?", color: Color(UIColor(hex: UInt32(user.num))), circleSize: 44) } } ToolbarItem(placement: .navigationBarTrailing) { @@ -372,7 +375,7 @@ struct UserMessageList: View { ConnectedDevice( bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, - name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") + name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?") } } } diff --git a/Meshtastic/Views/Nodes/DetectionSensorLog.swift b/Meshtastic/Views/Nodes/DetectionSensorLog.swift index b4b25b2f..dcda62a4 100644 --- a/Meshtastic/Views/Nodes/DetectionSensorLog.swift +++ b/Meshtastic/Views/Nodes/DetectionSensorLog.swift @@ -121,7 +121,7 @@ struct DetectionSensorLog: View { .navigationBarTitleDisplayMode(.inline) .navigationBarItems(trailing: ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?") }) .onAppear { self.bleManager.context = context diff --git a/Meshtastic/Views/Nodes/DeviceMetricsLog.swift b/Meshtastic/Views/Nodes/DeviceMetricsLog.swift index 844de54b..25a161a4 100644 --- a/Meshtastic/Views/Nodes/DeviceMetricsLog.swift +++ b/Meshtastic/Views/Nodes/DeviceMetricsLog.swift @@ -157,7 +157,7 @@ struct DeviceMetricsLog: View { .font(.caption) Text("\(String(format: "%.2f", dm.airUtilTx))%") .font(.caption) - Text(dm.time?.formattedDate(format: dateFormatString) ?? "Unknown time") + Text(dm.time?.formattedDate(format: dateFormatString) ?? "unknown.age".localized) .font(.caption) } } @@ -208,7 +208,7 @@ struct DeviceMetricsLog: View { .navigationBarTitleDisplayMode(.inline) .navigationBarItems(trailing: ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?") }) .onAppear { self.bleManager.context = context diff --git a/Meshtastic/Views/Nodes/EnvironmentMetricsLog.swift b/Meshtastic/Views/Nodes/EnvironmentMetricsLog.swift index 4ba0a84f..10fb667e 100644 --- a/Meshtastic/Views/Nodes/EnvironmentMetricsLog.swift +++ b/Meshtastic/Views/Nodes/EnvironmentMetricsLog.swift @@ -190,7 +190,7 @@ struct EnvironmentMetricsLog: View { .navigationBarTitleDisplayMode(.inline) .navigationBarItems(trailing: ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?") }) .onAppear { self.bleManager.context = context diff --git a/Meshtastic/Views/Nodes/NodeDetail.swift b/Meshtastic/Views/Nodes/NodeDetail.swift index 2f3d51b0..99ffcd04 100644 --- a/Meshtastic/Views/Nodes/NodeDetail.swift +++ b/Meshtastic/Views/Nodes/NodeDetail.swift @@ -16,9 +16,7 @@ struct NodeDetail: View { @AppStorage("meshMapType") private var meshMapType = 0 @AppStorage("meshMapShowNodeHistory") private var meshMapShowNodeHistory = false @AppStorage("meshMapShowRouteLines") private var meshMapShowRouteLines = false - //@State private var mapType: MKMapType = .standard @State private var selectedMapLayer: MapLayer = .standard - @State var mapRect: MKMapRect = MKMapRect() @State var waypointCoordinate: WaypointCoordinate? @State var editingWaypoint: Int = 0 @State private var loadedWeather: Bool = false @@ -26,15 +24,12 @@ struct NodeDetail: View { @State private var showingForecast = false @State private var showingShutdownConfirm: Bool = false @State private var showingRebootConfirm: Bool = false - @State private var showOverlays: Bool = true @State private var customMapOverlay: MapViewSwiftUI.CustomMapOverlay? = MapViewSwiftUI.CustomMapOverlay( - mapName: "offlinemap", - tileType: "png", - canReplaceMapContent: true - ) - + mapName: "offlinemap", + tileType: "png", + canReplaceMapContent: true + ) var node: NodeInfoEntity - @FetchRequest(sortDescriptors: [NSSortDescriptor(key: "name", ascending: false)], predicate: NSPredicate( format: "expire == nil || expire >= %@", Date() as NSDate @@ -49,13 +44,14 @@ struct NodeDetail: View { @State private var attributionLink: URL? @State private var attributionLogo: URL? + var body: some View { let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral?.num ?? -1, context: context) NavigationStack { GeometryReader { bounds in VStack { - if node.positions?.count ?? 0 > 0 { + if node.hasPositions { ZStack { let positionArray = node.positions?.array as? [PositionEntity] ?? [] let lastTenThousand = Array(positionArray.prefix(10000)) @@ -68,11 +64,9 @@ struct NodeDetail: View { waypointCoordinate = WaypointCoordinate(id: .init(), coordinate: nil, waypointId: Int64(wpId)) } }, - // visibleMapRect: $mapRect, selectedMapLayer: selectedMapLayer, positions: lastTenThousand, waypoints: Array(waypoints), - // mapViewType: mapType, userTrackingMode: MKUserTrackingMode.none, showNodeHistory: meshMapShowNodeHistory, showRouteLines: meshMapShowRouteLines, @@ -223,16 +217,12 @@ struct NodeDetail: View { ConnectedDevice( bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, - name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") + name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?") }) - .onAppear { - self.bleManager.context = context - // mapType = .standard// MeshMapTypes(rawValue: meshMapType)?.MKMapTypeValue() ?? .standard - } .task(id: node.num) { if !loadedWeather { do { - if node.positions?.count ?? 0 > 0 { + 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 diff --git a/Meshtastic/Views/Nodes/NodeList.swift b/Meshtastic/Views/Nodes/NodeList.swift index 033e7d62..8cd324e5 100644 --- a/Meshtastic/Views/Nodes/NodeList.swift +++ b/Meshtastic/Views/Nodes/NodeList.swift @@ -47,7 +47,7 @@ struct NodeList: View { LazyVStack(alignment: .leading) { HStack { VStack(alignment: .leading) { - CircleText(text: node.user?.shortName ?? "???", color: Color(UIColor(hex: UInt32(node.num))), circleSize: 65, fontSize: (node.user?.shortName ?? "???").isEmoji() ? 44 : (node.user?.shortName?.count ?? 0 == 4 ? 19 : 26), brightness: 0.0, textColor: UIColor(hex: UInt32(node.num)).isLight() ? .black : .white) + CircleText(text: node.user?.shortName ?? "?", color: Color(UIColor(hex: UInt32(node.num))), circleSize: 65) .padding(.trailing, 5) let deviceMetrics = node.telemetries?.filtered(using: NSPredicate(format: "metricsType == 0")) if deviceMetrics?.count ?? 0 >= 1 { diff --git a/Meshtastic/Views/Nodes/NodeMap.swift b/Meshtastic/Views/Nodes/NodeMap.swift index 7764256f..1948f6ea 100644 --- a/Meshtastic/Views/Nodes/NodeMap.swift +++ b/Meshtastic/Views/Nodes/NodeMap.swift @@ -232,7 +232,7 @@ struct NodeMap: View { bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : - "????") + "?") }) .onAppear(perform: { UIApplication.shared.isIdleTimerDisabled = true diff --git a/Meshtastic/Views/Nodes/PositionLog.swift b/Meshtastic/Views/Nodes/PositionLog.swift index bc11b5fd..4cd48761 100644 --- a/Meshtastic/Views/Nodes/PositionLog.swift +++ b/Meshtastic/Views/Nodes/PositionLog.swift @@ -19,7 +19,11 @@ struct PositionLog: View { @State var exportString = "" var node: NodeInfoEntity @State private var isPresentingClearLogConfirm = false - @State private var sortOrder = [KeyPathComparator(\PositionEntity.latitude)] + //@State private var sortOrder = [KeyPathComparator(\PositionEntity.latitude)] + + @State var sortOrder: [KeyPathComparator] = [ + .init(\.latitude, order: SortOrder.forward) + ] var body: some View { NavigationStack { let localeDateFormat = DateFormatter.dateFormat(fromTemplate: "yyMMddjmma", options: 0, locale: Locale.current) @@ -27,7 +31,7 @@ struct PositionLog: View { if UIDevice.current.userInterfaceIdiom == .pad && !useGrid || UIDevice.current.userInterfaceIdiom == .mac { // Add a table for mac and ipad let positions = node.positions?.reversed() as? [PositionEntity] ?? [] - Table(positions) { + Table(positions, sortOrder: $sortOrder) { TableColumn("Latitude") { position in Text(String(format: "%.5f", position.latitude ?? 0)) } @@ -58,6 +62,7 @@ struct PositionLog: View { } .width(min: 180) } + } else { ScrollView { // Use a grid on iOS as a table only shows a single column @@ -97,7 +102,7 @@ struct PositionLog: View { .font(.caption2) Text(altitude.formatted()) .font(.caption2) - Text(mappin.time?.formattedDate(format: dateFormatString) ?? "Unknown time") + Text(mappin.time?.formattedDate(format: dateFormatString) ?? "unknown.age".localized) .font(.caption2) } } @@ -159,7 +164,7 @@ struct PositionLog: View { .navigationTitle("Position Log \(node.positions?.count ?? 0) Points") .navigationBarItems(trailing: ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?") }) .onAppear { self.bleManager.context = context diff --git a/Meshtastic/Views/Settings/AdminMessageList.swift b/Meshtastic/Views/Settings/AdminMessageList.swift index ea9a8951..bb624dc0 100644 --- a/Meshtastic/Views/Settings/AdminMessageList.swift +++ b/Meshtastic/Views/Settings/AdminMessageList.swift @@ -67,7 +67,7 @@ struct AdminMessageList: View { .navigationTitle("admin.log") .navigationBarItems(trailing: ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?") }) .onAppear { self.bleManager.context = context diff --git a/Meshtastic/Views/Settings/AppSettings.swift b/Meshtastic/Views/Settings/AppSettings.swift index 02c33fc9..859f5a3f 100644 --- a/Meshtastic/Views/Settings/AppSettings.swift +++ b/Meshtastic/Views/Settings/AppSettings.swift @@ -143,7 +143,7 @@ struct AppSettings: View { .navigationTitle("app.settings") .navigationBarItems(trailing: ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?") }) .onAppear { self.bleManager.context = context diff --git a/Meshtastic/Views/Settings/Channels.swift b/Meshtastic/Views/Settings/Channels.swift index c28b580c..03f48f56 100644 --- a/Meshtastic/Views/Settings/Channels.swift +++ b/Meshtastic/Views/Settings/Channels.swift @@ -64,8 +64,9 @@ struct Channels: View { }) { VStack(alignment: .leading) { HStack { - CircleText(text: String(channel.index), color: .accentColor, circleSize: 45, fontSize: 36, brightness: 0.1) + CircleText(text: String(channel.index), color: .accentColor, circleSize: 45) .padding(.trailing, 5) + .brightness(0.1) VStack { HStack { if channel.name?.isEmpty ?? false { @@ -281,7 +282,7 @@ struct Channels: View { .navigationTitle("channels") .navigationBarItems(trailing: ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?") }) .onAppear { bleManager.context = context diff --git a/Meshtastic/Views/Settings/Config/BluetoothConfig.swift b/Meshtastic/Views/Settings/Config/BluetoothConfig.swift index 9c08e0cd..4370f95e 100644 --- a/Meshtastic/Views/Settings/Config/BluetoothConfig.swift +++ b/Meshtastic/Views/Settings/Config/BluetoothConfig.swift @@ -134,7 +134,7 @@ struct BluetoothConfig: View { .navigationTitle("bluetooth.config") .navigationBarItems(trailing: ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?") }) .onAppear { self.bleManager.context = context diff --git a/Meshtastic/Views/Settings/Config/DeviceConfig.swift b/Meshtastic/Views/Settings/Config/DeviceConfig.swift index d0a3c609..f7d97eb5 100644 --- a/Meshtastic/Views/Settings/Config/DeviceConfig.swift +++ b/Meshtastic/Views/Settings/Config/DeviceConfig.swift @@ -223,7 +223,7 @@ struct DeviceConfig: View { .navigationTitle("device.config") .navigationBarItems(trailing: ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?") }) .onAppear { self.bleManager.context = context diff --git a/Meshtastic/Views/Settings/Config/DisplayConfig.swift b/Meshtastic/Views/Settings/Config/DisplayConfig.swift index 98df1d7a..d3d95b7d 100644 --- a/Meshtastic/Views/Settings/Config/DisplayConfig.swift +++ b/Meshtastic/Views/Settings/Config/DisplayConfig.swift @@ -178,7 +178,7 @@ struct DisplayConfig: View { .navigationTitle("display.config") .navigationBarItems(trailing: ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?") }) .onAppear { self.bleManager.context = context diff --git a/Meshtastic/Views/Settings/Config/LoRaConfig.swift b/Meshtastic/Views/Settings/Config/LoRaConfig.swift index 735cb11f..8aac3f02 100644 --- a/Meshtastic/Views/Settings/Config/LoRaConfig.swift +++ b/Meshtastic/Views/Settings/Config/LoRaConfig.swift @@ -220,7 +220,7 @@ struct LoRaConfig: View { .navigationTitle("lora.config") .navigationBarItems(trailing: ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?") }) .onAppear { diff --git a/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift b/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift index ab4f9902..f79c90b6 100644 --- a/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/CannedMessagesConfig.swift @@ -262,7 +262,7 @@ struct CannedMessagesConfig: View { .navigationTitle("canned.messages.config") .navigationBarItems(trailing: ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?") }) .onAppear { self.bleManager.context = context diff --git a/Meshtastic/Views/Settings/Config/Module/DetectionSensorConfig.swift b/Meshtastic/Views/Settings/Config/Module/DetectionSensorConfig.swift index 3f77c554..35ab9b0e 100644 --- a/Meshtastic/Views/Settings/Config/Module/DetectionSensorConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/DetectionSensorConfig.swift @@ -168,7 +168,7 @@ struct DetectionSensorConfig: View { .navigationTitle("detection.sensor.config") .navigationBarItems(trailing: ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?") }) .onAppear { self.bleManager.context = context diff --git a/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift b/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift index 6d16ead5..3b99f7f0 100644 --- a/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/ExternalNotificationConfig.swift @@ -214,7 +214,7 @@ struct ExternalNotificationConfig: View { .navigationTitle("external.notification.config") .navigationBarItems(trailing: ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?") }) .onAppear { self.bleManager.context = context diff --git a/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift b/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift index a07aa840..b9d9a190 100644 --- a/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift @@ -236,7 +236,7 @@ struct MQTTConfig: View { .navigationTitle("mqtt.config") .navigationBarItems(trailing: ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?") }) .onAppear { self.bleManager.context = context diff --git a/Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift b/Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift index 5d89dedb..746d3f51 100644 --- a/Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/RangeTestConfig.swift @@ -114,7 +114,7 @@ struct RangeTestConfig: View { .navigationTitle("range.test.config") .navigationBarItems(trailing: ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?") }) .onAppear { self.bleManager.context = context diff --git a/Meshtastic/Views/Settings/Config/Module/RtttlConfig.swift b/Meshtastic/Views/Settings/Config/Module/RtttlConfig.swift index 4f96b909..4ca9bdc3 100644 --- a/Meshtastic/Views/Settings/Config/Module/RtttlConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/RtttlConfig.swift @@ -112,7 +112,7 @@ struct RtttlConfig: View { .navigationTitle("ringtone.config") .navigationBarItems(trailing: ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?") }) .onAppear { self.bleManager.context = context diff --git a/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift b/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift index 02705f58..7e716166 100644 --- a/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/SerialConfig.swift @@ -175,7 +175,7 @@ struct SerialConfig: View { .navigationBarItems(trailing: ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?") }) .onAppear { diff --git a/Meshtastic/Views/Settings/Config/Module/StoreForward.swift b/Meshtastic/Views/Settings/Config/Module/StoreForward.swift index 52845e7d..f46efe6b 100644 --- a/Meshtastic/Views/Settings/Config/Module/StoreForward.swift +++ b/Meshtastic/Views/Settings/Config/Module/StoreForward.swift @@ -134,7 +134,7 @@ struct StoreForwardConfig: View { .navigationTitle("storeforward.config") .navigationBarItems(trailing: ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?") }) .onAppear { self.bleManager.context = context diff --git a/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift b/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift index bf9dce3a..560331e2 100644 --- a/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift +++ b/Meshtastic/Views/Settings/Config/Module/TelemetryConfig.swift @@ -130,7 +130,7 @@ struct TelemetryConfig: View { .navigationTitle("telemetry.config") .navigationBarItems(trailing: ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?") }) .onAppear { self.bleManager.context = context diff --git a/Meshtastic/Views/Settings/Config/NetworkConfig.swift b/Meshtastic/Views/Settings/Config/NetworkConfig.swift index ab74b1ec..34da6391 100644 --- a/Meshtastic/Views/Settings/Config/NetworkConfig.swift +++ b/Meshtastic/Views/Settings/Config/NetworkConfig.swift @@ -163,7 +163,7 @@ struct NetworkConfig: View { .navigationTitle("network.config") .navigationBarItems(trailing: ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?") }) .onAppear { self.bleManager.context = context diff --git a/Meshtastic/Views/Settings/Config/PositionConfig.swift b/Meshtastic/Views/Settings/Config/PositionConfig.swift index dea77cca..2ed55fe2 100644 --- a/Meshtastic/Views/Settings/Config/PositionConfig.swift +++ b/Meshtastic/Views/Settings/Config/PositionConfig.swift @@ -323,7 +323,7 @@ struct PositionConfig: View { ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?") }) .onAppear { diff --git a/Meshtastic/Views/Settings/ShareChannels.swift b/Meshtastic/Views/Settings/ShareChannels.swift index f8bc464a..0f90bea2 100644 --- a/Meshtastic/Views/Settings/ShareChannels.swift +++ b/Meshtastic/Views/Settings/ShareChannels.swift @@ -221,7 +221,7 @@ struct ShareChannels: View { .navigationBarTitleDisplayMode(.inline) .navigationBarItems(trailing: ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?") }) .onAppear { bleManager.context = context diff --git a/Meshtastic/Views/Settings/UserConfig.swift b/Meshtastic/Views/Settings/UserConfig.swift index a61d4228..ba7b5d74 100644 --- a/Meshtastic/Views/Settings/UserConfig.swift +++ b/Meshtastic/Views/Settings/UserConfig.swift @@ -178,7 +178,7 @@ struct UserConfig: View { .navigationTitle("User Config") .navigationBarItems(trailing: ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????") + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?") }) .onAppear { self.bleManager.context = context