From 163788bcc9cee3f95c1c35e62017b43ba29c010f Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Thu, 31 Aug 2023 22:15:03 -0700 Subject: [PATCH 01/28] Fix mesh packet empty user bugs --- Meshtastic/Helpers/MeshPackets.swift | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index 0abe318a..9419da0f 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 @@ -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 { From 976a6098030dbe4347f84e258c105b0eb416121e Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Thu, 31 Aug 2023 22:48:52 -0700 Subject: [PATCH 02/28] Add functions to make lighter and darker UIColors for the map --- Meshtastic.xcodeproj/project.pbxproj | 4 ++ Meshtastic/Extensions/UIColor.swift | 41 +++++++++++++++++++ .../Views/Map/Custom/MapViewSwiftUI.swift | 2 +- 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 Meshtastic/Extensions/UIColor.swift diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 6850954b..02aeccf1 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -68,6 +68,7 @@ DD6193792863875F00E59241 /* SerialConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6193782863875F00E59241 /* SerialConfig.swift */; }; DD73FD1128750779000852D6 /* PositionLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD73FD1028750779000852D6 /* PositionLog.swift */; }; DD769E0328D18BF1001A3F05 /* DeviceMetricsLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD769E0228D18BF0001A3F05 /* DeviceMetricsLog.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 */; }; @@ -268,6 +269,7 @@ DD6193782863875F00E59241 /* SerialConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SerialConfig.swift; sourceTree = ""; }; DD73FD1028750779000852D6 /* PositionLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionLog.swift; sourceTree = ""; }; DD769E0228D18BF0001A3F05 /* DeviceMetricsLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceMetricsLog.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 = ""; }; @@ -790,6 +792,7 @@ DDDB444329F8A8DD00EE2349 /* Float.swift */, DDDB444D29F8AB0E00EE2349 /* Int.swift */, DDDB444729F8A9C900EE2349 /* String.swift */, + DD77093E2AA1B146007A8BF0 /* UIColor.swift */, DDDB444F29F8AC9C00EE2349 /* UIImage.swift */, DDDB443F29F79AB000EE2349 /* UserDefaults.swift */, DDB75A0E2A05920E006ED576 /* FileManager.swift */, @@ -1145,6 +1148,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 */, diff --git a/Meshtastic/Extensions/UIColor.swift b/Meshtastic/Extensions/UIColor.swift new file mode 100644 index 00000000..482ddc1b --- /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: -3*componentDelta) + } + + private func add(_ value: CGFloat, toComponent: CGFloat) -> CGFloat { + return max(0, min(1, toComponent + value)) + } + +} diff --git a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift index 1e0a767b..f6d4a9e6 100644 --- a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift +++ b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift @@ -290,7 +290,7 @@ 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 { From 5f9218921c8eea4878c800cf529912fca8462a5c Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Thu, 31 Aug 2023 23:01:51 -0700 Subject: [PATCH 03/28] Bump version --- Meshtastic.xcodeproj/project.pbxproj | 4 ++-- Meshtastic/Views/Messages/ChannelMessageList.swift | 1 - Meshtastic/Views/Messages/UserMessageList.swift | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 02aeccf1..1083f9f6 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -1369,7 +1369,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; @@ -1403,7 +1403,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; diff --git a/Meshtastic/Views/Messages/ChannelMessageList.swift b/Meshtastic/Views/Messages/ChannelMessageList.swift index 4186dd2a..2b91f2dc 100644 --- a/Meshtastic/Views/Messages/ChannelMessageList.swift +++ b/Meshtastic/Views/Messages/ChannelMessageList.swift @@ -229,7 +229,6 @@ struct ChannelMessageList: View { .onAppear { if !message.read { message.read = true - message.toUser?.objectWillChange.send() do { try context.save() print("Read message \(message.messageId) ") diff --git a/Meshtastic/Views/Messages/UserMessageList.swift b/Meshtastic/Views/Messages/UserMessageList.swift index d6fbacdc..da1d899e 100644 --- a/Meshtastic/Views/Messages/UserMessageList.swift +++ b/Meshtastic/Views/Messages/UserMessageList.swift @@ -219,7 +219,6 @@ struct UserMessageList: View { .onAppear { if !message.read { message.read = true - message.toUser?.objectWillChange.send() do { try context.save() print("Read message \(message.messageId) ") From 1e17bf61af7b03a23e6289ee400e46212c8cf8f6 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Thu, 31 Aug 2023 23:03:05 -0700 Subject: [PATCH 04/28] Update project.pbxproj Bump version --- Meshtastic.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 1083f9f6..d55d4e87 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -1525,7 +1525,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 = ""; @@ -1558,7 +1558,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 = ""; From 42cc9725b26088ac01bb2b6840be34d68c33a8db Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Fri, 1 Sep 2023 00:22:30 -0700 Subject: [PATCH 05/28] Simplify circle text so they are all consistant --- Meshtastic/Extensions/UIColor.swift | 2 +- Meshtastic/Views/Bluetooth/Connect.swift | 2 +- Meshtastic/Views/Helpers/CircleText.swift | 19 ++++++++++--------- .../Views/Helpers/Node/NodeInfoView.swift | 4 ++-- Meshtastic/Views/Messages/ChannelList.swift | 2 +- .../Views/Messages/ChannelMessageList.swift | 4 ++-- Meshtastic/Views/Messages/UserList.swift | 2 +- .../Views/Messages/UserMessageList.swift | 2 +- Meshtastic/Views/Nodes/NodeList.swift | 2 +- Meshtastic/Views/Settings/Channels.swift | 3 ++- 10 files changed, 22 insertions(+), 20 deletions(-) diff --git a/Meshtastic/Extensions/UIColor.swift b/Meshtastic/Extensions/UIColor.swift index 482ddc1b..83623967 100644 --- a/Meshtastic/Extensions/UIColor.swift +++ b/Meshtastic/Extensions/UIColor.swift @@ -31,7 +31,7 @@ extension UIColor { } func darker(componentDelta: CGFloat = 0.1) -> UIColor { - return makeColor(componentDelta: -3*componentDelta) + return makeColor(componentDelta: -2*componentDelta) } private func add(_ value: CGFloat, toComponent: CGFloat) -> CGFloat { diff --git a/Meshtastic/Views/Bluetooth/Connect.swift b/Meshtastic/Views/Bluetooth/Connect.swift index c5633eb7..e0be4345 100644 --- a/Meshtastic/Views/Bluetooth/Connect.swift +++ b/Meshtastic/Views/Bluetooth/Connect.swift @@ -49,7 +49,7 @@ struct Connect: View { if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.peripheral.state == .connected { 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, textColor: UIColor(hex: UInt32(node?.num ?? 0)).isLight() ? .black : .white ) } .padding(.trailing) VStack(alignment: .leading) { diff --git a/Meshtastic/Views/Helpers/CircleText.swift b/Meshtastic/Views/Helpers/CircleText.swift index 2f8ebb05..dcfd7118 100644 --- a/Meshtastic/Views/Helpers/CircleText.swift +++ b/Meshtastic/Views/Helpers/CircleText.swift @@ -8,29 +8,30 @@ import SwiftUI struct CircleText: View { var text: String var color: Color - var circleSize: CGFloat? = 60 - var fontSize: CGFloat? = 20 - var brightness: Double? = 0 + var circleSize: CGFloat? = 45 var textColor: Color? = .white - + var body: some View { - let font = Font.system(size: fontSize!) + let font = Font.system(size: (text.count == 1) ? ((circleSize ?? 45) * 0.75) : (text.count == 2 ? ((circleSize ?? 45) * 0.52) : (text.count == 3 ? ((circleSize ?? 45) * 0.42) : ((circleSize ?? 45) * 0.32)))) 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) + .font(font) + .foregroundColor(textColor) + .fixedSize() + .frame(width: circleSize, height: circleSize, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/) } } } struct CircleText_Previews: PreviewProvider { static var previews: some View { - CircleText(text: "MOMO", color: Color.accentColor) + CircleText(text: "MOMO", color: Color.accentColor, 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..5e73a481 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, textColor: UIColor(hex: UInt32(node.num)).isLight() ? .black : .white) } Divider() VStack { @@ -123,7 +123,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, textColor: UIColor(hex: UInt32(node.num)).isLight() ? .black : .white ) } if node.user != nil { Divider() diff --git a/Meshtastic/Views/Messages/ChannelList.swift b/Meshtastic/Views/Messages/ChannelList.swift index 39c0cff3..dfbf5e9f 100644 --- a/Meshtastic/Views/Messages/ChannelList.swift +++ b/Meshtastic/Views/Messages/ChannelList.swift @@ -47,7 +47,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){ diff --git a/Meshtastic/Views/Messages/ChannelMessageList.swift b/Meshtastic/Views/Messages/ChannelMessageList.swift index 2b91f2dc..a1f83d11 100644 --- a/Meshtastic/Views/Messages/ChannelMessageList.swift +++ b/Meshtastic/Views/Messages/ChannelMessageList.swift @@ -56,7 +56,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, textColor: UIColor(hex: UInt32(message.fromUser?.num ?? 0)).isLight() ? .black : .white) .padding(.all, 5) .offset(y: -5) } @@ -400,7 +400,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) } } diff --git a/Meshtastic/Views/Messages/UserList.swift b/Meshtastic/Views/Messages/UserList.swift index 9b0c2c49..7d8f29dd 100644 --- a/Meshtastic/Views/Messages/UserList.swift +++ b/Meshtastic/Views/Messages/UserList.swift @@ -56,7 +56,7 @@ 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))), textColor: UIColor(hex: UInt32(user.num)).isLight() ? .black : .white) VStack(alignment: .leading){ HStack{ diff --git a/Meshtastic/Views/Messages/UserMessageList.swift b/Meshtastic/Views/Messages/UserMessageList.swift index da1d899e..5f25bda7 100644 --- a/Meshtastic/Views/Messages/UserMessageList.swift +++ b/Meshtastic/Views/Messages/UserMessageList.swift @@ -363,7 +363,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, textColor: UIColor(hex: UInt32(user.num)).isLight() ? .black : .white) } } ToolbarItem(placement: .navigationBarTrailing) { diff --git a/Meshtastic/Views/Nodes/NodeList.swift b/Meshtastic/Views/Nodes/NodeList.swift index 033e7d62..af72c01d 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, textColor: UIColor(hex: UInt32(node.num)).isLight() ? .black : .white) .padding(.trailing, 5) let deviceMetrics = node.telemetries?.filtered(using: NSPredicate(format: "metricsType == 0")) if deviceMetrics?.count ?? 0 >= 1 { diff --git a/Meshtastic/Views/Settings/Channels.swift b/Meshtastic/Views/Settings/Channels.swift index c28b580c..04373b6e 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 { From 7feaa7dd9cf325b3f7e53642806dd0e38004f4db Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Fri, 1 Sep 2023 07:11:49 -0500 Subject: [PATCH 06/28] Finally got to the bottom of the Mesh Map not updating bug! --- Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift index f6d4a9e6..0373daf9 100644 --- a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift +++ b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift @@ -251,18 +251,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) From 87a1115727de77445b53e1928ffce6445af03126 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sat, 2 Sep 2023 08:50:23 -0700 Subject: [PATCH 07/28] Adjust map pin darker and lighter logic so that the latest pin is darker than the node color and history pins and route lines are lighter --- Meshtastic/Extensions/UIColor.swift | 2 +- Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Meshtastic/Extensions/UIColor.swift b/Meshtastic/Extensions/UIColor.swift index 83623967..6a8909b9 100644 --- a/Meshtastic/Extensions/UIColor.swift +++ b/Meshtastic/Extensions/UIColor.swift @@ -31,7 +31,7 @@ extension UIColor { } func darker(componentDelta: CGFloat = 0.1) -> UIColor { - return makeColor(componentDelta: -2*componentDelta) + return makeColor(componentDelta: -1*componentDelta) } private func add(_ value: CGFloat, toComponent: CGFloat) -> CGFloat { diff --git a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift index 0373daf9..10c32e2e 100644 --- a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift +++ b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift @@ -293,7 +293,7 @@ struct MapViewSwiftUI: UIViewRepresentable { 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 } @@ -441,7 +441,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 } From c25a60942d702294515eeb1ceefad62bdbe35c92 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sat, 2 Sep 2023 09:23:20 -0700 Subject: [PATCH 08/28] Font scaling for Short name circle text --- Meshtastic/Views/Helpers/CircleText.swift | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Meshtastic/Views/Helpers/CircleText.swift b/Meshtastic/Views/Helpers/CircleText.swift index dcfd7118..66cbb746 100644 --- a/Meshtastic/Views/Helpers/CircleText.swift +++ b/Meshtastic/Views/Helpers/CircleText.swift @@ -8,23 +8,22 @@ import SwiftUI struct CircleText: View { var text: String var color: Color - var circleSize: CGFloat? = 45 + var circleSize: CGFloat = 45 var textColor: Color? = .white var body: some View { - let font = Font.system(size: (text.count == 1) ? ((circleSize ?? 45) * 0.75) : (text.count == 2 ? ((circleSize ?? 45) * 0.52) : (text.count == 3 ? ((circleSize ?? 45) * 0.42) : ((circleSize ?? 45) * 0.32)))) - ZStack { Circle() .fill(color) .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@*/) + .font(.system(size: 500)) + .minimumScaleFactor(0.001) + .frame(width: circleSize * 0.90, + height: circleSize * 0.90, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/) } } } @@ -32,6 +31,9 @@ struct CircleText: View { struct CircleText_Previews: PreviewProvider { static var previews: some View { CircleText(text: "MOMO", color: Color.accentColor, circleSize: 80) + CircleText(text: "WWWW", color: Color.accentColor, circleSize: 80) + CircleText(text: "LCP", color: Color.accentColor, circleSize: 80) + CircleText(text: "8", color: Color.accentColor, circleSize: 80) .previewLayout(.fixed(width: 300, height: 100)) } } From b6cfd0700049a19f2609a2ac7315283fe03205c8 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sat, 2 Sep 2023 17:37:35 -0700 Subject: [PATCH 09/28] Question everything --- Meshtastic/Helpers/BLEManager.swift | 4 ++-- Meshtastic/Helpers/MeshPackets.swift | 4 ++-- Meshtastic/Views/Bluetooth/Connect.swift | 6 +++--- Meshtastic/Views/Helpers/Node/NodeInfoView.swift | 6 +++--- Meshtastic/Views/Messages/ChannelMessageList.swift | 6 +++--- Meshtastic/Views/Messages/Contacts.swift | 2 +- Meshtastic/Views/Messages/UserList.swift | 2 +- Meshtastic/Views/Messages/UserMessageList.swift | 6 +++--- Meshtastic/Views/Nodes/DetectionSensorLog.swift | 2 +- Meshtastic/Views/Nodes/DeviceMetricsLog.swift | 2 +- Meshtastic/Views/Nodes/EnvironmentMetricsLog.swift | 2 +- Meshtastic/Views/Nodes/NodeDetail.swift | 2 +- Meshtastic/Views/Nodes/NodeList.swift | 2 +- Meshtastic/Views/Nodes/NodeMap.swift | 2 +- Meshtastic/Views/Nodes/PositionLog.swift | 2 +- Meshtastic/Views/Settings/AdminMessageList.swift | 2 +- Meshtastic/Views/Settings/AppSettings.swift | 2 +- Meshtastic/Views/Settings/Channels.swift | 2 +- Meshtastic/Views/Settings/Config/BluetoothConfig.swift | 2 +- Meshtastic/Views/Settings/Config/DeviceConfig.swift | 2 +- Meshtastic/Views/Settings/Config/DisplayConfig.swift | 2 +- Meshtastic/Views/Settings/Config/LoRaConfig.swift | 2 +- .../Views/Settings/Config/Module/CannedMessagesConfig.swift | 2 +- .../Settings/Config/Module/DetectionSensorConfig.swift | 2 +- .../Settings/Config/Module/ExternalNotificationConfig.swift | 2 +- Meshtastic/Views/Settings/Config/Module/MQTTConfig.swift | 2 +- .../Views/Settings/Config/Module/RangeTestConfig.swift | 2 +- Meshtastic/Views/Settings/Config/Module/RtttlConfig.swift | 2 +- Meshtastic/Views/Settings/Config/Module/SerialConfig.swift | 2 +- Meshtastic/Views/Settings/Config/Module/StoreForward.swift | 2 +- .../Views/Settings/Config/Module/TelemetryConfig.swift | 2 +- Meshtastic/Views/Settings/Config/NetworkConfig.swift | 2 +- Meshtastic/Views/Settings/Config/PositionConfig.swift | 2 +- Meshtastic/Views/Settings/ShareChannels.swift | 6 +++--- Meshtastic/Views/Settings/UserConfig.swift | 2 +- 35 files changed, 47 insertions(+), 47 deletions(-) diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index efc6eb51..e6f93622 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -471,7 +471,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 } } @@ -2143,7 +2143,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 9419da0f..073ee7ff 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -772,7 +772,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" ) @@ -800,7 +800,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/Views/Bluetooth/Connect.swift b/Meshtastic/Views/Bluetooth/Connect.swift index e0be4345..25be7d12 100644 --- a/Meshtastic/Views/Bluetooth/Connect.swift +++ b/Meshtastic/Views/Bluetooth/Connect.swift @@ -49,7 +49,7 @@ struct Connect: View { if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.peripheral.state == .connected { HStack { VStack(alignment: .center) { - CircleText(text: node?.user?.shortName ?? "???", color: Color(UIColor(hex: UInt32(node?.num ?? 0))), circleSize: 90, 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, textColor: UIColor(hex: UInt32(node?.num ?? 0)).isLight() ? .black : .white ) } .padding(.trailing) VStack(alignment: .leading) { @@ -105,7 +105,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)") } @@ -247,7 +247,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/Helpers/Node/NodeInfoView.swift b/Meshtastic/Views/Helpers/Node/NodeInfoView.swift index 5e73a481..da95af44 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, textColor: UIColor(hex: UInt32(node.num)).isLight() ? .black : .white) + CircleText(text: node.user?.shortName ?? "?", color: Color(UIColor(hex: UInt32(node.num))), circleSize: 150, textColor: UIColor(hex: UInt32(node.num)).isLight() ? .black : .white) } Divider() VStack { @@ -123,7 +123,7 @@ struct NodeInfoView: View { HStack { VStack(alignment: .center) { - CircleText(text: node.user?.shortName ?? "???", color: Color(UIColor(hex: UInt32(node.num))), circleSize: 65, textColor: UIColor(hex: UInt32(node.num)).isLight() ? .black : .white ) + CircleText(text: node.user?.shortName ?? "?", color: Color(UIColor(hex: UInt32(node.num))), circleSize: 65, textColor: UIColor(hex: UInt32(node.num)).isLight() ? .black : .white ) } if node.user != nil { Divider() @@ -177,7 +177,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 { diff --git a/Meshtastic/Views/Messages/ChannelMessageList.swift b/Meshtastic/Views/Messages/ChannelMessageList.swift index a1f83d11..45a0e16b 100644 --- a/Meshtastic/Views/Messages/ChannelMessageList.swift +++ b/Meshtastic/Views/Messages/ChannelMessageList.swift @@ -56,7 +56,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, 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, textColor: UIColor(hex: UInt32(message.fromUser?.num ?? 0)).isLight() ? .black : .white) .padding(.all, 5) .offset(y: -5) } @@ -170,7 +170,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() @@ -409,7 +409,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 index 3edaf03d..e3b3de07 100644 --- a/Meshtastic/Views/Messages/Contacts.swift +++ b/Meshtastic/Views/Messages/Contacts.swift @@ -169,7 +169,7 @@ // .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))), 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{ diff --git a/Meshtastic/Views/Messages/UserList.swift b/Meshtastic/Views/Messages/UserList.swift index 7d8f29dd..7a7e8a3d 100644 --- a/Meshtastic/Views/Messages/UserList.swift +++ b/Meshtastic/Views/Messages/UserList.swift @@ -56,7 +56,7 @@ struct UserList: View { .brightness(0.2) } - CircleText(text: user.shortName ?? "???", color: Color(UIColor(hex: UInt32(user.num))), textColor: UIColor(hex: UInt32(user.num)).isLight() ? .black : .white) + CircleText(text: user.shortName ?? "?", color: Color(UIColor(hex: UInt32(user.num))), textColor: UIColor(hex: UInt32(user.num)).isLight() ? .black : .white) VStack(alignment: .leading){ HStack{ diff --git a/Meshtastic/Views/Messages/UserMessageList.swift b/Meshtastic/Views/Messages/UserMessageList.swift index 5f25bda7..bfe09a1e 100644 --- a/Meshtastic/Views/Messages/UserMessageList.swift +++ b/Meshtastic/Views/Messages/UserMessageList.swift @@ -160,7 +160,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() @@ -363,7 +363,7 @@ struct UserMessageList: View { .toolbar { ToolbarItem(placement: .principal) { HStack { - CircleText(text: user.shortName ?? "???", color: Color(UIColor(hex: UInt32(user.num))), circleSize: 44, textColor: UIColor(hex: UInt32(user.num)).isLight() ? .black : .white) + CircleText(text: user.shortName ?? "?", color: Color(UIColor(hex: UInt32(user.num))), circleSize: 44, textColor: UIColor(hex: UInt32(user.num)).isLight() ? .black : .white) } } ToolbarItem(placement: .navigationBarTrailing) { @@ -371,7 +371,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..a0ef93be 100644 --- a/Meshtastic/Views/Nodes/DeviceMetricsLog.swift +++ b/Meshtastic/Views/Nodes/DeviceMetricsLog.swift @@ -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..fdd32a56 100644 --- a/Meshtastic/Views/Nodes/NodeDetail.swift +++ b/Meshtastic/Views/Nodes/NodeDetail.swift @@ -223,7 +223,7 @@ 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 diff --git a/Meshtastic/Views/Nodes/NodeList.swift b/Meshtastic/Views/Nodes/NodeList.swift index af72c01d..5bde18ca 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, textColor: UIColor(hex: UInt32(node.num)).isLight() ? .black : .white) + CircleText(text: node.user?.shortName ?? "?", color: Color(UIColor(hex: UInt32(node.num))), circleSize: 65, textColor: UIColor(hex: UInt32(node.num)).isLight() ? .black : .white) .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..0f28ba3e 100644 --- a/Meshtastic/Views/Nodes/PositionLog.swift +++ b/Meshtastic/Views/Nodes/PositionLog.swift @@ -159,7 +159,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 04373b6e..03f48f56 100644 --- a/Meshtastic/Views/Settings/Channels.swift +++ b/Meshtastic/Views/Settings/Channels.swift @@ -282,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 86d79877..42dd2803 100644 --- a/Meshtastic/Views/Settings/ShareChannels.swift +++ b/Meshtastic/Views/Settings/ShareChannels.swift @@ -185,9 +185,9 @@ struct ShareChannels: View { if node != nil { ShareLink("Share QR Code & Link", item: Image(uiImage: qrImage), - subject: Text("Meshtastic Node \(node?.user?.shortName ?? "????") has shared channels with you"), + subject: Text("Meshtastic Node \(node?.user?.shortName ?? "?") has shared channels with you"), message: Text(channelsUrl), - preview: SharePreview("Meshtastic Node \(node?.user?.shortName ?? "????") has shared channels with you", + preview: SharePreview("Meshtastic Node \(node?.user?.shortName ?? "?") has shared channels with you", image: Image(uiImage: qrImage)) ) .buttonStyle(.bordered) @@ -257,7 +257,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 From 06f39d40f20cf059b6b9b264ad2ab31015918834 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sat, 2 Sep 2023 18:02:51 -0700 Subject: [PATCH 10/28] Remove another circletext parameter --- Meshtastic/Views/Bluetooth/Connect.swift | 2 +- Meshtastic/Views/Helpers/CircleText.swift | 29 +++++++++++++------ .../Views/Helpers/Node/NodeInfoView.swift | 4 +-- .../Views/Messages/ChannelMessageList.swift | 2 +- Meshtastic/Views/Messages/UserList.swift | 2 +- .../Views/Messages/UserMessageList.swift | 2 +- Meshtastic/Views/Nodes/NodeList.swift | 2 +- 7 files changed, 27 insertions(+), 16 deletions(-) diff --git a/Meshtastic/Views/Bluetooth/Connect.swift b/Meshtastic/Views/Bluetooth/Connect.swift index 25be7d12..545ba52d 100644 --- a/Meshtastic/Views/Bluetooth/Connect.swift +++ b/Meshtastic/Views/Bluetooth/Connect.swift @@ -49,7 +49,7 @@ struct Connect: View { if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.peripheral.state == .connected { HStack { VStack(alignment: .center) { - CircleText(text: node?.user?.shortName ?? "?", color: Color(UIColor(hex: UInt32(node?.num ?? 0))), circleSize: 90, 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) { diff --git a/Meshtastic/Views/Helpers/CircleText.swift b/Meshtastic/Views/Helpers/CircleText.swift index 66cbb746..3b84b0d8 100644 --- a/Meshtastic/Views/Helpers/CircleText.swift +++ b/Meshtastic/Views/Helpers/CircleText.swift @@ -9,7 +9,6 @@ struct CircleText: View { var text: String var color: Color var circleSize: CGFloat = 45 - var textColor: Color? = .white var body: some View { @@ -19,21 +18,33 @@ struct CircleText: View { .frame(width: circleSize, height: circleSize) Text(text) .textCase(.uppercase) - .foregroundColor(textColor) + .foregroundColor(color.isLight() ? .black : .white) .font(.system(size: 500)) .minimumScaleFactor(0.001) - .frame(width: circleSize * 0.90, - height: circleSize * 0.90, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/) + .frame(width: circleSize * 0.94, height: circleSize * 0.94, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/) } } } struct CircleText_Previews: PreviewProvider { static var previews: some View { - CircleText(text: "MOMO", color: Color.accentColor, circleSize: 80) - CircleText(text: "WWWW", color: Color.accentColor, circleSize: 80) - CircleText(text: "LCP", color: Color.accentColor, circleSize: 80) - CircleText(text: "8", color: Color.accentColor, circleSize: 80) - .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 da95af44..8e30b7ea 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, 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 { @@ -123,7 +123,7 @@ struct NodeInfoView: View { HStack { VStack(alignment: .center) { - CircleText(text: node.user?.shortName ?? "?", color: Color(UIColor(hex: UInt32(node.num))), circleSize: 65, 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() diff --git a/Meshtastic/Views/Messages/ChannelMessageList.swift b/Meshtastic/Views/Messages/ChannelMessageList.swift index 45a0e16b..15336573 100644 --- a/Meshtastic/Views/Messages/ChannelMessageList.swift +++ b/Meshtastic/Views/Messages/ChannelMessageList.swift @@ -56,7 +56,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, 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) } diff --git a/Meshtastic/Views/Messages/UserList.swift b/Meshtastic/Views/Messages/UserList.swift index 7a7e8a3d..1e88d615 100644 --- a/Meshtastic/Views/Messages/UserList.swift +++ b/Meshtastic/Views/Messages/UserList.swift @@ -56,7 +56,7 @@ struct UserList: View { .brightness(0.2) } - CircleText(text: user.shortName ?? "?", color: Color(UIColor(hex: UInt32(user.num))), textColor: UIColor(hex: UInt32(user.num)).isLight() ? .black : .white) + CircleText(text: user.shortName ?? "?", color: Color(UIColor(hex: UInt32(user.num)))) VStack(alignment: .leading){ HStack{ diff --git a/Meshtastic/Views/Messages/UserMessageList.swift b/Meshtastic/Views/Messages/UserMessageList.swift index bfe09a1e..68dfbbd8 100644 --- a/Meshtastic/Views/Messages/UserMessageList.swift +++ b/Meshtastic/Views/Messages/UserMessageList.swift @@ -363,7 +363,7 @@ struct UserMessageList: View { .toolbar { ToolbarItem(placement: .principal) { HStack { - CircleText(text: user.shortName ?? "?", color: Color(UIColor(hex: UInt32(user.num))), circleSize: 44, 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) { diff --git a/Meshtastic/Views/Nodes/NodeList.swift b/Meshtastic/Views/Nodes/NodeList.swift index 5bde18ca..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, 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 { From 7b9d6b11cfe5f36f49aac7bd9310dfb7cdbd002a Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 3 Sep 2023 09:04:07 -0700 Subject: [PATCH 11/28] Unread direct messages indicator --- Meshtastic.xcodeproj/project.pbxproj | 16 ++++++++-------- Meshtastic/Helpers/BLEManager.swift | 1 + Meshtastic/Helpers/MeshPackets.swift | 5 +++++ Meshtastic/MeshtasticApp.swift | 3 +++ Meshtastic/MeshtasticAppDelegate.swift | 7 ++++++- Meshtastic/Model/PeripheralModel.swift | 12 ------------ Meshtastic/Persistence/UserEntityExtension.swift | 1 - Meshtastic/Views/ContentView.swift | 2 +- Meshtastic/Views/Helpers/CircleText.swift | 1 + Meshtastic/Views/Messages/UserMessageList.swift | 3 +++ 10 files changed, 28 insertions(+), 23 deletions(-) diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index d55d4e87..09f44353 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -632,16 +632,16 @@ DDC2E15626CE248E0042C5E4 /* Meshtastic */ = { isa = PBXGroup; children = ( - 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 */, @@ -760,14 +760,14 @@ DDC4D5662754996200A4208E /* Persistence */ = { isa = PBXGroup; children = ( + DD58C5F12919AD3C00D5BEFB /* ChannelEntityExtension.swift */, + 6DEDA55B2A9592F900321D2E /* MessageEntityExtension.swift */, DDC4D567275499A500A4208E /* Persistence.swift */, DD5394FD276BA0EF00AD86B1 /* PositionEntityExtension.swift */, - DDD9E4E3284B208E003777C5 /* UserEntityExtension.swift */, - DD58C5F12919AD3C00D5BEFB /* ChannelEntityExtension.swift */, DD964FC52975DBFD007C176F /* QueryCoreData.swift */, DD3CC6C128EB9D4900FA9159 /* UpdateCoreData.swift */, + DDD9E4E3284B208E003777C5 /* UserEntityExtension.swift */, DD964FC1297272AE007C176F /* WaypointEntityExtension.swift */, - 6DEDA55B2A9592F900321D2E /* MessageEntityExtension.swift */, ); path = Persistence; sourceTree = ""; diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index e6f93622..88b00ce4 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -718,6 +718,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: "'") diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index 073ee7ff..fcf99fa5 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -766,6 +766,11 @@ func textMessageAppPacket(packet: MeshPacket, blockRangeTest: Bool, connectedNod if messageSaved { if newMessage.fromUser != nil && newMessage.toUser != nil && !(newMessage.fromUser?.mute ?? false) { + // Set Unread Message Indicators + if packet.to == connectedNode { + var state = AppState.shared + state.unreadDirectMessages = newMessage.toUser?.unreadMessages ?? 0 + } // Create an iOS Notification for the received DM message and schedule it immediately let manager = LocalNotificationManager() manager.notifications = [ diff --git a/Meshtastic/MeshtasticApp.swift b/Meshtastic/MeshtasticApp.swift index b5f24076..d92d14b9 100644 --- a/Meshtastic/MeshtasticApp.swift +++ b/Meshtastic/MeshtasticApp.swift @@ -14,6 +14,7 @@ struct MeshtasticAppleApp: App { @State var incomingUrl: URL? @State var channelSettings: String? @StateObject var appState = AppState.shared + var body: some Scene { WindowGroup { ContentView() @@ -121,4 +122,6 @@ class AppState: ObservableObject { static let shared = AppState() @Published var tabSelection: Tab = .ble + @Published var unreadDirectMessages: Int = 0 + @Published var unreadChannelMessages: Int = 0 } 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/UserEntityExtension.swift b/Meshtastic/Persistence/UserEntityExtension.swift index 1786a45b..927c0372 100644 --- a/Meshtastic/Persistence/UserEntityExtension.swift +++ b/Meshtastic/Persistence/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/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 3b84b0d8..bd2f0b0d 100644 --- a/Meshtastic/Views/Helpers/CircleText.swift +++ b/Meshtastic/Views/Helpers/CircleText.swift @@ -23,6 +23,7 @@ struct CircleText: View { .minimumScaleFactor(0.001) .frame(width: circleSize * 0.94, height: circleSize * 0.94, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/) } + .aspectRatio(1, contentMode: .fit) } } diff --git a/Meshtastic/Views/Messages/UserMessageList.swift b/Meshtastic/Views/Messages/UserMessageList.swift index 68dfbbd8..e4829d0e 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 @@ -222,6 +223,8 @@ struct UserMessageList: View { do { try context.save() print("Read message \(message.messageId) ") + appState.unreadDirectMessages = user.unreadMessages + } catch { print("Failed to read message \(message.messageId)") } From f6b909805e8b9c229f0868aff3b504a4c6561085 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 3 Sep 2023 09:11:45 -0700 Subject: [PATCH 12/28] Messages view unread messages badges --- Meshtastic/Views/Messages/Messages.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Meshtastic/Views/Messages/Messages.swift b/Meshtastic/Views/Messages/Messages.swift index 1acbb673..837e517e 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,6 +50,7 @@ struct Messages: View { .brightness(0.2) Text("direct.messages") .font(.title2) + .badge(appState.unreadDirectMessages) } } .navigationTitle("messages") From 1a613a0860aab6875e99f46ad298ca26650dd814 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 3 Sep 2023 10:00:12 -0700 Subject: [PATCH 13/28] Hook up new channel messages badge --- Meshtastic.xcodeproj/project.pbxproj | 4 ++++ Meshtastic/Helpers/MeshPackets.swift | 5 +++-- .../contents | 5 ++++- .../Persistence/MyInfoEntityExtension.swift | 20 +++++++++++++++++++ 4 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 Meshtastic/Persistence/MyInfoEntityExtension.swift diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 09f44353..fc33a6dc 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -12,6 +12,7 @@ 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 */; }; 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 */; }; @@ -208,6 +209,7 @@ 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 = ""; }; 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 = ""; }; @@ -762,6 +764,7 @@ children = ( DD58C5F12919AD3C00D5BEFB /* ChannelEntityExtension.swift */, 6DEDA55B2A9592F900321D2E /* MessageEntityExtension.swift */, + DD007BAD2AA4E91200F5FA12 /* MyInfoEntityExtension.swift */, DDC4D567275499A500A4208E /* Persistence.swift */, DD5394FD276BA0EF00AD86B1 /* PositionEntityExtension.swift */, DD964FC52975DBFD007C176F /* QueryCoreData.swift */, @@ -1076,6 +1079,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 */, diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index fcf99fa5..5c3b447b 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -765,11 +765,11 @@ func textMessageAppPacket(packet: MeshPacket, blockRangeTest: Bool, connectedNod messageSaved = true if messageSaved { + var appState = AppState.shared if newMessage.fromUser != nil && newMessage.toUser != nil && !(newMessage.fromUser?.mute ?? false) { // Set Unread Message Indicators if packet.to == connectedNode { - var state = AppState.shared - state.unreadDirectMessages = newMessage.toUser?.unreadMessages ?? 0 + appState.unreadDirectMessages = newMessage.toUser?.unreadMessages ?? 0 } // Create an iOS Notification for the received DM message and schedule it immediately let manager = LocalNotificationManager() @@ -794,6 +794,7 @@ func textMessageAppPacket(packet: MeshPacket, blockRangeTest: Bool, connectedNod return } if !fetchedMyInfo.isEmpty { + appState.unreadChannelMessages = fetchedMyInfo[0].unreadMessages for channel in (fetchedMyInfo[0].channels?.array ?? []) as? [ChannelEntity] ?? [] { if channel.index == newMessage.channel { context.refresh(channel, mergeChanges: true) 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/Persistence/MyInfoEntityExtension.swift b/Meshtastic/Persistence/MyInfoEntityExtension.swift new file mode 100644 index 00000000..1e04282a --- /dev/null +++ b/Meshtastic/Persistence/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 + } +} From 5854dd7578d2b3e8157063f236851a05de590eb8 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 3 Sep 2023 10:07:39 -0700 Subject: [PATCH 14/28] Try and force large messages view on ios --- Meshtastic/Views/Messages/Messages.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Meshtastic/Views/Messages/Messages.swift b/Meshtastic/Views/Messages/Messages.swift index 837e517e..c7cb7917 100644 --- a/Meshtastic/Views/Messages/Messages.swift +++ b/Meshtastic/Views/Messages/Messages.swift @@ -54,6 +54,7 @@ struct Messages: View { } } .navigationTitle("messages") + .navigationBarTitleDisplayMode(.large) .navigationBarItems(leading: MeshtasticLogo()) .onAppear { self.bleManager.context = context From 23affc1ffb8dcd6d062d9d1fa1f3ad83751646a5 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 3 Sep 2023 16:47:35 -0700 Subject: [PATCH 15/28] Clean up unread messages view --- Meshtastic.xcodeproj/project.pbxproj | 4 - Meshtastic/Views/Messages/ChannelList.swift | 2 +- .../Views/Messages/ChannelMessageList.swift | 5 +- Meshtastic/Views/Messages/Contacts.swift | 301 ------------------ .../Views/Messages/UserMessageList.swift | 2 +- Meshtastic/Views/Nodes/NodeDetail.swift | 2 +- 6 files changed, 7 insertions(+), 309 deletions(-) delete mode 100644 Meshtastic/Views/Messages/Contacts.swift diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index fc33a6dc..fe9506d9 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -79,7 +79,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 */; }; @@ -281,7 +280,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 = ""; }; @@ -714,7 +712,6 @@ children = ( DDB8F4132A9EE5F000230ECE /* ChannelList.swift */, DD798B062915928D005217CD /* ChannelMessageList.swift */, - DD882F5C2772E4640005BF05 /* Contacts.swift */, DDB8F40F2A9EE5B400230ECE /* Messages.swift */, DDB8F4112A9EE5DD00230ECE /* UserList.swift */, DD1BF2F82776FE2E008C8D2F /* UserMessageList.swift */, @@ -1092,7 +1089,6 @@ 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 */, diff --git a/Meshtastic/Views/Messages/ChannelList.swift b/Meshtastic/Views/Messages/ChannelList.swift index dfbf5e9f..05fae6b7 100644 --- a/Meshtastic/Views/Messages/ChannelList.swift +++ b/Meshtastic/Views/Messages/ChannelList.swift @@ -32,7 +32,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 )))) diff --git a/Meshtastic/Views/Messages/ChannelMessageList.swift b/Meshtastic/Views/Messages/ChannelMessageList.swift index 15336573..5a1924c4 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,7 +24,8 @@ struct ChannelMessageList: View { var maxbytes = 228 @FocusState var focusedField: Field? - @ObservedObject var channel: ChannelEntity + @StateObject var myInfo: MyInfoEntity + @StateObject var channel: ChannelEntity @State var showDeleteMessageAlert = false @State private var deleteMessageId: Int64 = 0 @State private var replyMessageId: Int64 = 0 @@ -232,6 +234,7 @@ struct ChannelMessageList: View { do { try context.save() print("Read message \(message.messageId) ") + appState.unreadChannelMessages = myInfo.unreadMessages } catch { print("Failed to read message \(message.messageId)") } diff --git a/Meshtastic/Views/Messages/Contacts.swift b/Meshtastic/Views/Messages/Contacts.swift deleted file mode 100644 index e3b3de07..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/UserMessageList.swift b/Meshtastic/Views/Messages/UserMessageList.swift index e4829d0e..40320633 100644 --- a/Meshtastic/Views/Messages/UserMessageList.swift +++ b/Meshtastic/Views/Messages/UserMessageList.swift @@ -23,7 +23,7 @@ struct UserMessageList: View { var maxbytes = 228 @FocusState var focusedField: Field? // View State Items - @ObservedObject var user: UserEntity + @StateObject var user: UserEntity @State var showDeleteMessageAlert = false @State private var deleteMessageId: Int64 = 0 @State private var replyMessageId: Int64 = 0 diff --git a/Meshtastic/Views/Nodes/NodeDetail.swift b/Meshtastic/Views/Nodes/NodeDetail.swift index fdd32a56..768f172f 100644 --- a/Meshtastic/Views/Nodes/NodeDetail.swift +++ b/Meshtastic/Views/Nodes/NodeDetail.swift @@ -33,7 +33,7 @@ struct NodeDetail: View { canReplaceMapContent: true ) - var node: NodeInfoEntity + @ObservedObject var node: NodeInfoEntity @FetchRequest(sortDescriptors: [NSSortDescriptor(key: "name", ascending: false)], predicate: NSPredicate( From 3fb33f5951d0c202be8d891ed6d3b464687d9058 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 3 Sep 2023 17:31:49 -0700 Subject: [PATCH 16/28] App icon unread messages badge --- Meshtastic/Helpers/MeshPackets.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index 5c3b447b..7ee8569e 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -770,6 +770,7 @@ func textMessageAppPacket(packet: MeshPacket, blockRangeTest: Bool, connectedNod // 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() @@ -795,6 +796,8 @@ func textMessageAppPacket(packet: MeshPacket, blockRangeTest: Bool, connectedNod } 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) From 3330c88c490ab77b9eeb1a1c3fdcb3ab4c855a5b Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 3 Sep 2023 17:46:57 -0700 Subject: [PATCH 17/28] Update unread app icon when messages are read --- Meshtastic/Views/Messages/ChannelMessageList.swift | 1 + Meshtastic/Views/Messages/UserMessageList.swift | 1 + 2 files changed, 2 insertions(+) diff --git a/Meshtastic/Views/Messages/ChannelMessageList.swift b/Meshtastic/Views/Messages/ChannelMessageList.swift index 5a1924c4..be3c2911 100644 --- a/Meshtastic/Views/Messages/ChannelMessageList.swift +++ b/Meshtastic/Views/Messages/ChannelMessageList.swift @@ -235,6 +235,7 @@ struct ChannelMessageList: View { try context.save() print("Read message \(message.messageId) ") appState.unreadChannelMessages = myInfo.unreadMessages + UIApplication.shared.applicationIconBadgeNumber = appState.unreadChannelMessages + appState.unreadDirectMessages } catch { print("Failed to read message \(message.messageId)") } diff --git a/Meshtastic/Views/Messages/UserMessageList.swift b/Meshtastic/Views/Messages/UserMessageList.swift index 40320633..5715d0b8 100644 --- a/Meshtastic/Views/Messages/UserMessageList.swift +++ b/Meshtastic/Views/Messages/UserMessageList.swift @@ -224,6 +224,7 @@ struct UserMessageList: View { 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)") From 38501624c2a505c0d0e9fd38774549f130fdfaf5 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 3 Sep 2023 20:23:40 -0700 Subject: [PATCH 18/28] fix map compass, add favorites feature to direct messages --- .../Views/Map/Custom/MapViewSwiftUI.swift | 19 ++++++++++--------- Meshtastic/Views/Messages/UserList.swift | 18 ++++++++++++++++-- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift index 10c32e2e..1620f184 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 diff --git a/Meshtastic/Views/Messages/UserList.swift b/Meshtastic/Views/Messages/UserList.swift index 1e88d615..d9fa02f4 100644 --- a/Meshtastic/Views/Messages/UserList.swift +++ b/Meshtastic/Views/Messages/UserList.swift @@ -24,7 +24,7 @@ struct UserList: View { @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 @@ -63,7 +63,10 @@ struct UserList: View { 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 +102,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.fill" : "star.slash.fill") + } Button { user.mute = !user.mute do { From 0598b26f5304ae818e12f3397c47e7b42a35b797 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 3 Sep 2023 21:36:54 -0700 Subject: [PATCH 19/28] Load unread message state when the app loads. --- Meshtastic/Helpers/BLEManager.swift | 11 +++++++++-- Meshtastic/MeshtasticApp.swift | 1 + 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 88b00ce4..4e5db2f0 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -614,10 +614,17 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate 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 { + // Subscribe to Mqtt Client Proxy if enabled + if fetchedNodeInfo[0].mqttConfig?.enabled ?? false && fetchedNodeInfo[0].mqttConfig?.proxyToClientEnabled ?? false { mqttManager.connectFromConfigSettings(node: fetchedNodeInfo[0]) } + // Set initial unread message badge states + var 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; diff --git a/Meshtastic/MeshtasticApp.swift b/Meshtastic/MeshtasticApp.swift index d92d14b9..6092371e 100644 --- a/Meshtastic/MeshtasticApp.swift +++ b/Meshtastic/MeshtasticApp.swift @@ -124,4 +124,5 @@ class AppState: ObservableObject { @Published var tabSelection: Tab = .ble @Published var unreadDirectMessages: Int = 0 @Published var unreadChannelMessages: Int = 0 + @Published var connectedNode: NodeInfoEntity? } From e399a426ade66b0dc42ce95ed234332dfe474d3f Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 3 Sep 2023 22:14:59 -0700 Subject: [PATCH 20/28] Only show logs when there is data. Move coredata extentions. Allow turning off of mqtt client proxy. Save unread message badges on configcomplete --- Meshtastic.xcodeproj/project.pbxproj | 24 ++++++++++++----- .../CoreData}/ChannelEntityExtension.swift | 0 .../CoreData}/MessageEntityExtension.swift | 0 .../CoreData}/MyInfoEntityExtension.swift | 0 .../CoreData/NodeInfoEntityExtension.swift | 27 +++++++++++++++++++ .../CoreData}/PositionEntityExtension.swift | 0 .../CoreData}/UserEntityExtension.swift | 0 .../CoreData}/WaypointEntityExtension.swift | 0 Meshtastic/Helpers/BLEManager.swift | 12 ++++++--- .../Views/Helpers/Node/NodeInfoView.swift | 17 +++++++----- Meshtastic/Views/Nodes/NodeDetail.swift | 4 +-- 11 files changed, 65 insertions(+), 19 deletions(-) rename Meshtastic/{Persistence => Extensions/CoreData}/ChannelEntityExtension.swift (100%) rename Meshtastic/{Persistence => Extensions/CoreData}/MessageEntityExtension.swift (100%) rename Meshtastic/{Persistence => Extensions/CoreData}/MyInfoEntityExtension.swift (100%) create mode 100644 Meshtastic/Extensions/CoreData/NodeInfoEntityExtension.swift rename Meshtastic/{Persistence => Extensions/CoreData}/PositionEntityExtension.swift (100%) rename Meshtastic/{Persistence => Extensions/CoreData}/UserEntityExtension.swift (100%) rename Meshtastic/{Persistence => Extensions/CoreData}/WaypointEntityExtension.swift (100%) diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index fe9506d9..765de0df 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -13,6 +13,7 @@ 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 */; }; @@ -209,6 +210,7 @@ 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 = ""; }; @@ -439,6 +441,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 = ( @@ -759,15 +775,9 @@ DDC4D5662754996200A4208E /* Persistence */ = { isa = PBXGroup; children = ( - DD58C5F12919AD3C00D5BEFB /* ChannelEntityExtension.swift */, - 6DEDA55B2A9592F900321D2E /* MessageEntityExtension.swift */, - DD007BAD2AA4E91200F5FA12 /* MyInfoEntityExtension.swift */, DDC4D567275499A500A4208E /* Persistence.swift */, - DD5394FD276BA0EF00AD86B1 /* PositionEntityExtension.swift */, DD964FC52975DBFD007C176F /* QueryCoreData.swift */, DD3CC6C128EB9D4900FA9159 /* UpdateCoreData.swift */, - DDD9E4E3284B208E003777C5 /* UserEntityExtension.swift */, - DD964FC1297272AE007C176F /* WaypointEntityExtension.swift */, ); path = Persistence; sourceTree = ""; @@ -783,6 +793,7 @@ DDDB443E29F79A9400EE2349 /* Extensions */ = { isa = PBXGroup; children = ( + DD007BB12AA59B9A00F5FA12 /* CoreData */, DDDB444529F8A96500EE2349 /* Character.swift */, DDDB444929F8AA3A00EE2349 /* CLLocationCoordinate2D.swift */, DDDB444B29F8AAA600EE2349 /* Color.swift */, @@ -1085,6 +1096,7 @@ 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 */, 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/Persistence/MyInfoEntityExtension.swift b/Meshtastic/Extensions/CoreData/MyInfoEntityExtension.swift similarity index 100% rename from Meshtastic/Persistence/MyInfoEntityExtension.swift rename to Meshtastic/Extensions/CoreData/MyInfoEntityExtension.swift 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 100% rename from Meshtastic/Persistence/UserEntityExtension.swift rename to Meshtastic/Extensions/CoreData/UserEntityExtension.swift 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/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 4e5db2f0..e3d3e43b 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -613,16 +613,20 @@ 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 { + if fetchedNodeInfo.count == 1 { // Subscribe to Mqtt Client Proxy if enabled - if fetchedNodeInfo[0].mqttConfig?.enabled ?? false && fetchedNodeInfo[0].mqttConfig?.proxyToClientEnabled ?? false { + 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 - var appState = AppState.shared + let appState = AppState.shared appState.unreadChannelMessages = fetchedNodeInfo[0].myInfo?.unreadMessages ?? 0 appState.unreadDirectMessages = fetchedNodeInfo[0].user?.unreadMessages ?? 0 - appState.connectedNode = fetchedNodeInfo[0] + //appState.connectedNode = fetchedNodeInfo[0] UIApplication.shared.applicationIconBadgeNumber = appState.unreadChannelMessages + appState.unreadDirectMessages } diff --git a/Meshtastic/Views/Helpers/Node/NodeInfoView.swift b/Meshtastic/Views/Helpers/Node/NodeInfoView.swift index 8e30b7ea..362de937 100644 --- a/Meshtastic/Views/Helpers/Node/NodeInfoView.swift +++ b/Meshtastic/Views/Helpers/Node/NodeInfoView.swift @@ -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)) @@ -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/Nodes/NodeDetail.swift b/Meshtastic/Views/Nodes/NodeDetail.swift index 768f172f..6992dea3 100644 --- a/Meshtastic/Views/Nodes/NodeDetail.swift +++ b/Meshtastic/Views/Nodes/NodeDetail.swift @@ -55,7 +55,7 @@ struct NodeDetail: View { 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)) @@ -232,7 +232,7 @@ struct NodeDetail: View { .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 From 4cabf05a5a2fbb383267f7d52e4cfe05e2bc01ee Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 3 Sep 2023 22:46:00 -0700 Subject: [PATCH 21/28] Clean up node detail --- Meshtastic/Views/Nodes/NodeDetail.swift | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/Meshtastic/Views/Nodes/NodeDetail.swift b/Meshtastic/Views/Nodes/NodeDetail.swift index 6992dea3..cf3b30f8 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 + ) @ObservedObject var node: NodeInfoEntity - @FetchRequest(sortDescriptors: [NSSortDescriptor(key: "name", ascending: false)], predicate: NSPredicate( format: "expire == nil || expire >= %@", Date() as NSDate @@ -49,6 +44,7 @@ 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) From c8103e91c26c93aa7f3543ed749a07354971473d Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Mon, 4 Sep 2023 06:31:38 -0700 Subject: [PATCH 22/28] Switch user back to observedobject --- Meshtastic/Views/Messages/UserMessageList.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Meshtastic/Views/Messages/UserMessageList.swift b/Meshtastic/Views/Messages/UserMessageList.swift index 5715d0b8..e93bffd5 100644 --- a/Meshtastic/Views/Messages/UserMessageList.swift +++ b/Meshtastic/Views/Messages/UserMessageList.swift @@ -23,7 +23,7 @@ struct UserMessageList: View { var maxbytes = 228 @FocusState var focusedField: Field? // View State Items - @StateObject var user: UserEntity + @ObservedObject var user: UserEntity @State var showDeleteMessageAlert = false @State private var deleteMessageId: Int64 = 0 @State private var replyMessageId: Int64 = 0 From 0d5f36d9cb2b1062422fbbdc584b47332bfdadd1 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Mon, 4 Sep 2023 07:29:35 -0700 Subject: [PATCH 23/28] Flip favorite star icons --- Meshtastic/Views/Messages/UserList.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Meshtastic/Views/Messages/UserList.swift b/Meshtastic/Views/Messages/UserList.swift index d9fa02f4..bad3418b 100644 --- a/Meshtastic/Views/Messages/UserList.swift +++ b/Meshtastic/Views/Messages/UserList.swift @@ -111,7 +111,7 @@ struct UserList: View { print("💥 Save User VIP Error") } } label: { - Label(user.vip ? "Un-Favorite" : "Favorite", systemImage: user.vip ? "star.fill" : "star.slash.fill") + Label(user.vip ? "Un-Favorite" : "Favorite", systemImage: user.vip ? "star.slash.fill" : "star.fill") } Button { user.mute = !user.mute From 1c19ae11e6760ae7cc4a6f0195bf9dc070a45161 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Mon, 4 Sep 2023 20:17:09 -0700 Subject: [PATCH 24/28] Clean up object graph updates --- Meshtastic/Extensions/Date.swift | 6 ++- Meshtastic/Helpers/BLEManager.swift | 4 +- Meshtastic/Helpers/MeshPackets.swift | 42 +++++++++---------- Meshtastic/MeshtasticApp.swift | 1 + Meshtastic/Persistence/UpdateCoreData.swift | 2 +- .../Views/Map/Custom/MapViewSwiftUI.swift | 4 +- .../Views/Messages/ChannelMessageList.swift | 4 +- Meshtastic/Views/Nodes/DeviceMetricsLog.swift | 2 +- Meshtastic/Views/Nodes/NodeDetail.swift | 6 --- Meshtastic/Views/Nodes/PositionLog.swift | 11 +++-- 10 files changed, 43 insertions(+), 39 deletions(-) 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/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index e3d3e43b..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 @@ -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 @@ -718,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 diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index 7ee8569e..042fc9d1 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -535,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 @@ -765,7 +765,7 @@ func textMessageAppPacket(packet: MeshPacket, blockRangeTest: Bool, connectedNod messageSaved = true if messageSaved { - var appState = AppState.shared + let appState = AppState.shared if newMessage.fromUser != nil && newMessage.toUser != nil && !(newMessage.fromUser?.mute ?? false) { // Set Unread Message Indicators if packet.to == connectedNode { diff --git a/Meshtastic/MeshtasticApp.swift b/Meshtastic/MeshtasticApp.swift index 6092371e..04af0914 100644 --- a/Meshtastic/MeshtasticApp.swift +++ b/Meshtastic/MeshtasticApp.swift @@ -124,5 +124,6 @@ class AppState: ObservableObject { @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/Persistence/UpdateCoreData.swift b/Meshtastic/Persistence/UpdateCoreData.swift index 72bf71a6..1baa6ebc 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 diff --git a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift index 1620f184..62e834b0 100644 --- a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift +++ b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift @@ -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) diff --git a/Meshtastic/Views/Messages/ChannelMessageList.swift b/Meshtastic/Views/Messages/ChannelMessageList.swift index be3c2911..8780dacb 100644 --- a/Meshtastic/Views/Messages/ChannelMessageList.swift +++ b/Meshtastic/Views/Messages/ChannelMessageList.swift @@ -24,8 +24,8 @@ struct ChannelMessageList: View { var maxbytes = 228 @FocusState var focusedField: Field? - @StateObject var myInfo: MyInfoEntity - @StateObject var channel: ChannelEntity + @ObservedObject var myInfo: MyInfoEntity + @ObservedObject var channel: ChannelEntity @State var showDeleteMessageAlert = false @State private var deleteMessageId: Int64 = 0 @State private var replyMessageId: Int64 = 0 diff --git a/Meshtastic/Views/Nodes/DeviceMetricsLog.swift b/Meshtastic/Views/Nodes/DeviceMetricsLog.swift index a0ef93be..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) } } diff --git a/Meshtastic/Views/Nodes/NodeDetail.swift b/Meshtastic/Views/Nodes/NodeDetail.swift index cf3b30f8..89814e4a 100644 --- a/Meshtastic/Views/Nodes/NodeDetail.swift +++ b/Meshtastic/Views/Nodes/NodeDetail.swift @@ -64,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, @@ -221,10 +219,6 @@ struct NodeDetail: View { deviceConnected: bleManager.connectedPeripheral != nil, 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 { diff --git a/Meshtastic/Views/Nodes/PositionLog.swift b/Meshtastic/Views/Nodes/PositionLog.swift index 0f28ba3e..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) } } From 1b25be694cc395f6a1cf7b816fc3ba4f0a8c3473 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Mon, 4 Sep 2023 22:31:30 -0700 Subject: [PATCH 25/28] Properly refresh channel after read message, delete problematic mute channel feature --- Meshtastic/Views/Messages/ChannelList.swift | 19 ++----------------- .../Views/Messages/ChannelMessageList.swift | 1 + 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/Meshtastic/Views/Messages/ChannelList.swift b/Meshtastic/Views/Messages/ChannelList.swift index 05fae6b7..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 @@ -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 8780dacb..df4be238 100644 --- a/Meshtastic/Views/Messages/ChannelMessageList.swift +++ b/Meshtastic/Views/Messages/ChannelMessageList.swift @@ -236,6 +236,7 @@ struct ChannelMessageList: View { 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)") } From 00d330127d2cb0674bf0520b6f8230709e2b9cea Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Mon, 4 Sep 2023 22:51:30 -0700 Subject: [PATCH 26/28] Update app badge when deleting user messages --- Meshtastic/Views/Messages/UserList.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Meshtastic/Views/Messages/UserList.swift b/Meshtastic/Views/Messages/UserList.swift index bad3418b..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,10 +22,6 @@ 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: "vip", ascending: false), NSSortDescriptor(key: "longName", ascending: true)], animation: .default) @@ -158,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") } From ebc4fedeae4c5c2da6032af1b7b57ef5ead36d2f Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Mon, 4 Sep 2023 23:10:17 -0700 Subject: [PATCH 27/28] Remove observedobject --- Meshtastic/Views/Nodes/NodeDetail.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Meshtastic/Views/Nodes/NodeDetail.swift b/Meshtastic/Views/Nodes/NodeDetail.swift index 89814e4a..99ffcd04 100644 --- a/Meshtastic/Views/Nodes/NodeDetail.swift +++ b/Meshtastic/Views/Nodes/NodeDetail.swift @@ -29,7 +29,7 @@ struct NodeDetail: View { tileType: "png", canReplaceMapContent: true ) - @ObservedObject var node: NodeInfoEntity + var node: NodeInfoEntity @FetchRequest(sortDescriptors: [NSSortDescriptor(key: "name", ascending: false)], predicate: NSPredicate( format: "expire == nil || expire >= %@", Date() as NSDate From 2ab147a228f1fbdd3c4c44c390dc44b353801055 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Tue, 5 Sep 2023 17:08:15 -0700 Subject: [PATCH 28/28] Remove unnessary forced update. --- Meshtastic/Persistence/UpdateCoreData.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Meshtastic/Persistence/UpdateCoreData.swift b/Meshtastic/Persistence/UpdateCoreData.swift index 1baa6ebc..17310c61 100644 --- a/Meshtastic/Persistence/UpdateCoreData.swift +++ b/Meshtastic/Persistence/UpdateCoreData.swift @@ -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()