mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Merge pull request #391 from meshtastic/2.2.4_Working_Changes_iOS16
2.2.4 working changes iOS 16
This commit is contained in:
commit
55b18fe9b8
55 changed files with 351 additions and 498 deletions
|
|
@ -12,6 +12,8 @@
|
|||
6DEDA55C2A9592F900321D2E /* MessageEntityExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DEDA55B2A9592F900321D2E /* MessageEntityExtension.swift */; };
|
||||
C9697F9D279336B700250207 /* LocalMBTileOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9697F9C279336B700250207 /* LocalMBTileOverlay.swift */; };
|
||||
C9697FA527933B8C00250207 /* SQLite in Frameworks */ = {isa = PBXBuildFile; productRef = C9697FA427933B8C00250207 /* SQLite */; };
|
||||
DD007BAE2AA4E91200F5FA12 /* MyInfoEntityExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD007BAD2AA4E91200F5FA12 /* MyInfoEntityExtension.swift */; };
|
||||
DD007BB02AA5981000F5FA12 /* NodeInfoEntityExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD007BAF2AA5981000F5FA12 /* NodeInfoEntityExtension.swift */; };
|
||||
DD0D3D222A55CEB10066DB71 /* CocoaMQTT in Frameworks */ = {isa = PBXBuildFile; productRef = DD0D3D212A55CEB10066DB71 /* CocoaMQTT */; };
|
||||
DD0F791B28713C8A00A6FDAD /* AdminMessageList.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0F791A28713C8A00A6FDAD /* AdminMessageList.swift */; };
|
||||
DD14E72E2A82A614006E39BC /* RemoteHardware.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD14E72D2A82A614006E39BC /* RemoteHardware.swift */; };
|
||||
|
|
@ -68,6 +70,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 */; };
|
||||
|
|
@ -77,7 +80,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 */; };
|
||||
|
|
@ -207,6 +209,8 @@
|
|||
6DEDA55B2A9592F900321D2E /* MessageEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageEntityExtension.swift; sourceTree = "<group>"; };
|
||||
A65FA974296876BF00A97686 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||
C9697F9C279336B700250207 /* LocalMBTileOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalMBTileOverlay.swift; sourceTree = "<group>"; };
|
||||
DD007BAD2AA4E91200F5FA12 /* MyInfoEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyInfoEntityExtension.swift; sourceTree = "<group>"; };
|
||||
DD007BAF2AA5981000F5FA12 /* NodeInfoEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeInfoEntityExtension.swift; sourceTree = "<group>"; };
|
||||
DD0E9C222A30CE3A00580CBB /* MeshtasticDataModelV14.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV14.xcdatamodel; sourceTree = "<group>"; };
|
||||
DD0F791A28713C8A00A6FDAD /* AdminMessageList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminMessageList.swift; sourceTree = "<group>"; };
|
||||
DD14E72C2A80738F006E39BC /* MeshtasticDataModelV15.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV15.xcdatamodel; sourceTree = "<group>"; };
|
||||
|
|
@ -268,6 +272,7 @@
|
|||
DD6193782863875F00E59241 /* SerialConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SerialConfig.swift; sourceTree = "<group>"; };
|
||||
DD73FD1028750779000852D6 /* PositionLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionLog.swift; sourceTree = "<group>"; };
|
||||
DD769E0228D18BF0001A3F05 /* DeviceMetricsLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceMetricsLog.swift; sourceTree = "<group>"; };
|
||||
DD77093E2AA1B146007A8BF0 /* UIColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIColor.swift; sourceTree = "<group>"; };
|
||||
DD798B062915928D005217CD /* ChannelMessageList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelMessageList.swift; sourceTree = "<group>"; };
|
||||
DD8169F8271F1A6100F4AB02 /* MeshLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshLogger.swift; sourceTree = "<group>"; };
|
||||
DD8169FA271F1F3A00F4AB02 /* MeshLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshLog.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -277,7 +282,6 @@
|
|||
DD86D40B287F401000BAEB7A /* SaveChannelQRCode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveChannelQRCode.swift; sourceTree = "<group>"; };
|
||||
DD86D40E2881BE4C00BAEB7A /* CsvDocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CsvDocument.swift; sourceTree = "<group>"; };
|
||||
DD86D4102881D16900BAEB7A /* WriteCsvFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WriteCsvFile.swift; sourceTree = "<group>"; };
|
||||
DD882F5C2772E4640005BF05 /* Contacts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Contacts.swift; sourceTree = "<group>"; };
|
||||
DD8EBF42285058FA00426DCA /* DisplayConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayConfig.swift; sourceTree = "<group>"; };
|
||||
DD8ED9C42898D51F00B3B0AB /* NetworkConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkConfig.swift; sourceTree = "<group>"; };
|
||||
DD8ED9C7289CE4B900B3B0AB /* RoutingError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoutingError.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -437,6 +441,20 @@
|
|||
path = Custom;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
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 = "<group>";
|
||||
};
|
||||
DD47E3CA26F0E50300029299 /* Nodes */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
|
@ -630,16 +648,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 */,
|
||||
|
|
@ -710,7 +728,6 @@
|
|||
children = (
|
||||
DDB8F4132A9EE5F000230ECE /* ChannelList.swift */,
|
||||
DD798B062915928D005217CD /* ChannelMessageList.swift */,
|
||||
DD882F5C2772E4640005BF05 /* Contacts.swift */,
|
||||
DDB8F40F2A9EE5B400230ECE /* Messages.swift */,
|
||||
DDB8F4112A9EE5DD00230ECE /* UserList.swift */,
|
||||
DD1BF2F82776FE2E008C8D2F /* UserMessageList.swift */,
|
||||
|
|
@ -759,13 +776,8 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
DDC4D567275499A500A4208E /* Persistence.swift */,
|
||||
DD5394FD276BA0EF00AD86B1 /* PositionEntityExtension.swift */,
|
||||
DDD9E4E3284B208E003777C5 /* UserEntityExtension.swift */,
|
||||
DD58C5F12919AD3C00D5BEFB /* ChannelEntityExtension.swift */,
|
||||
DD964FC52975DBFD007C176F /* QueryCoreData.swift */,
|
||||
DD3CC6C128EB9D4900FA9159 /* UpdateCoreData.swift */,
|
||||
DD964FC1297272AE007C176F /* WaypointEntityExtension.swift */,
|
||||
6DEDA55B2A9592F900321D2E /* MessageEntityExtension.swift */,
|
||||
);
|
||||
path = Persistence;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -781,6 +793,7 @@
|
|||
DDDB443E29F79A9400EE2349 /* Extensions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DD007BB12AA59B9A00F5FA12 /* CoreData */,
|
||||
DDDB444529F8A96500EE2349 /* Character.swift */,
|
||||
DDDB444929F8AA3A00EE2349 /* CLLocationCoordinate2D.swift */,
|
||||
DDDB444B29F8AAA600EE2349 /* Color.swift */,
|
||||
|
|
@ -790,6 +803,7 @@
|
|||
DDDB444329F8A8DD00EE2349 /* Float.swift */,
|
||||
DDDB444D29F8AB0E00EE2349 /* Int.swift */,
|
||||
DDDB444729F8A9C900EE2349 /* String.swift */,
|
||||
DD77093E2AA1B146007A8BF0 /* UIColor.swift */,
|
||||
DDDB444F29F8AC9C00EE2349 /* UIImage.swift */,
|
||||
DDDB443F29F79AB000EE2349 /* UserDefaults.swift */,
|
||||
DDB75A0E2A05920E006ED576 /* FileManager.swift */,
|
||||
|
|
@ -1073,6 +1087,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 */,
|
||||
|
|
@ -1081,11 +1096,11 @@
|
|||
DDB6ABE628B1406100384BA1 /* LoraConfigEnums.swift in Sources */,
|
||||
DDB8F4142A9EE5F000230ECE /* ChannelList.swift in Sources */,
|
||||
DDD43FE32A78C8900083A3E9 /* MqttClientProxyManager.swift in Sources */,
|
||||
DD007BB02AA5981000F5FA12 /* NodeInfoEntityExtension.swift in Sources */,
|
||||
DDDB444629F8A96500EE2349 /* Character.swift in Sources */,
|
||||
DD23A50F26FD1B4400D9B90C /* PeripheralModel.swift in Sources */,
|
||||
DDB6ABDB28B0AC6000384BA1 /* DistanceText.swift in Sources */,
|
||||
DD5E520D298EE33B00D21B61 /* storeforward.pb.swift in Sources */,
|
||||
DD882F5D2772E4640005BF05 /* Contacts.swift in Sources */,
|
||||
DD964FC2297272AE007C176F /* WaypointEntityExtension.swift in Sources */,
|
||||
6DA39D8E2A92DC52007E311C /* MeshtasticAppDelegate.swift in Sources */,
|
||||
DD47E3CE26F103C600029299 /* NodeList.swift in Sources */,
|
||||
|
|
@ -1145,6 +1160,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 */,
|
||||
|
|
@ -1365,7 +1381,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;
|
||||
|
|
@ -1399,7 +1415,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;
|
||||
|
|
@ -1521,7 +1537,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 = "";
|
||||
|
|
@ -1554,7 +1570,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 = "";
|
||||
|
|
|
|||
20
Meshtastic/Extensions/CoreData/MyInfoEntityExtension.swift
Normal file
20
Meshtastic/Extensions/CoreData/MyInfoEntityExtension.swift
Normal file
|
|
@ -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
|
||||
}
|
||||
}
|
||||
27
Meshtastic/Extensions/CoreData/NodeInfoEntityExtension.swift
Normal file
27
Meshtastic/Extensions/CoreData/NodeInfoEntityExtension.swift
Normal file
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -18,7 +18,6 @@ extension UserEntity {
|
|||
}
|
||||
|
||||
var unreadMessages: Int {
|
||||
|
||||
let unreadMessages = messageList.filter{ ($0 as AnyObject).read == false }
|
||||
return unreadMessages.count
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
41
Meshtastic/Extensions/UIColor.swift
Normal file
41
Meshtastic/Extensions/UIColor.swift
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
//
|
||||
// UIColor.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Copyright(c) Garth Vander Houwen 8/31/23.
|
||||
//
|
||||
import Foundation
|
||||
import Swift
|
||||
import UIKit
|
||||
|
||||
extension UIColor {
|
||||
|
||||
private func makeColor(componentDelta: CGFloat) -> UIColor {
|
||||
var red: CGFloat = 0
|
||||
var blue: CGFloat = 0
|
||||
var green: CGFloat = 0
|
||||
var alpha: CGFloat = 0
|
||||
|
||||
getRed(&red, green: &green, blue: &blue, alpha: &alpha)
|
||||
|
||||
return UIColor(
|
||||
red: add(componentDelta, toComponent: red),
|
||||
green: add(componentDelta, toComponent: green),
|
||||
blue: add(componentDelta, toComponent: blue),
|
||||
alpha: alpha
|
||||
)
|
||||
}
|
||||
|
||||
func lighter(componentDelta: CGFloat = 0.1) -> UIColor {
|
||||
return makeColor(componentDelta: componentDelta)
|
||||
}
|
||||
|
||||
func darker(componentDelta: CGFloat = 0.1) -> UIColor {
|
||||
return makeColor(componentDelta: -1*componentDelta)
|
||||
}
|
||||
|
||||
private func add(_ value: CGFloat, toComponent: CGFloat) -> CGFloat {
|
||||
return max(0, min(1, toComponent + value))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -28,6 +28,8 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
@Published var isSwitchedOn: Bool = false
|
||||
@Published var automaticallyReconnect: Bool = true
|
||||
@Published var mqttProxyConnected: Bool = false
|
||||
|
||||
@StateObject var appState = AppState.shared
|
||||
public var minimumVersion = "2.0.0"
|
||||
public var connectedVersion: String
|
||||
public var isConnecting: Bool = false
|
||||
|
|
@ -471,7 +473,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
if nodeInfo != nil {
|
||||
if self.connectedPeripheral != nil && self.connectedPeripheral.num == nodeInfo!.num {
|
||||
if nodeInfo!.user != nil {
|
||||
connectedPeripheral.shortName = nodeInfo?.user?.shortName ?? "????"
|
||||
connectedPeripheral.shortName = nodeInfo?.user?.shortName ?? "?"
|
||||
connectedPeripheral.longName = nodeInfo?.user?.longName ?? "unknown".localized
|
||||
}
|
||||
}
|
||||
|
|
@ -516,6 +518,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
let version = decodedInfo.metadata.firmwareVersion[...(lastDotIndex ?? String.Index(utf16Offset: 6, in: decodedInfo.metadata.firmwareVersion))]
|
||||
nowKnown = true
|
||||
connectedVersion = String(version.dropLast())
|
||||
appState.firmwareVersion = connectedVersion
|
||||
}
|
||||
|
||||
let supportedVersion = connectedVersion == "0.0.0" || self.minimumVersion.compare(connectedVersion, options: .numeric) == .orderedAscending || minimumVersion.compare(connectedVersion, options: .numeric) == .orderedSame
|
||||
|
|
@ -613,11 +616,22 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(connectedPeripheral.num))
|
||||
do {
|
||||
let fetchedNodeInfo = try context?.fetch(fetchNodeInfoRequest) as? [NodeInfoEntity] ?? []
|
||||
if fetchedNodeInfo.count == 1 && fetchedNodeInfo[0].mqttConfig != nil {
|
||||
//Subscribe to Mqtt Client Proxy if enabled
|
||||
if fetchedNodeInfo[0].mqttConfig?.proxyToClientEnabled ?? false {
|
||||
if fetchedNodeInfo.count == 1 {
|
||||
// Subscribe to Mqtt Client Proxy if enabled
|
||||
if fetchedNodeInfo[0].mqttConfig != nil && fetchedNodeInfo[0].mqttConfig?.enabled ?? false && fetchedNodeInfo[0].mqttConfig?.proxyToClientEnabled ?? false {
|
||||
mqttManager.connectFromConfigSettings(node: fetchedNodeInfo[0])
|
||||
} else {
|
||||
if mqttProxyConnected {
|
||||
mqttManager.mqttClientProxy?.disconnect()
|
||||
}
|
||||
}
|
||||
// Set initial unread message badge states
|
||||
let appState = AppState.shared
|
||||
appState.unreadChannelMessages = fetchedNodeInfo[0].myInfo?.unreadMessages ?? 0
|
||||
appState.unreadDirectMessages = fetchedNodeInfo[0].user?.unreadMessages ?? 0
|
||||
//appState.connectedNode = fetchedNodeInfo[0]
|
||||
UIApplication.shared.applicationIconBadgeNumber = appState.unreadChannelMessages + appState.unreadDirectMessages
|
||||
|
||||
}
|
||||
if fetchedNodeInfo.count == 1 && fetchedNodeInfo[0].rangeTestConfig?.enabled == true {
|
||||
wantRangeTestPackets = true;
|
||||
|
|
@ -707,7 +721,6 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
if toUserNum > 0 {
|
||||
newMessage.toUser = fetchedUsers.first(where: { $0.num == toUserNum })
|
||||
newMessage.toUser?.lastMessage = Date()
|
||||
newMessage.toUser?.objectWillChange.send()
|
||||
}
|
||||
newMessage.fromUser = fetchedUsers.first(where: { $0.num == fromUserNum })
|
||||
newMessage.isEmoji = isEmoji
|
||||
|
|
@ -718,6 +731,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
}
|
||||
newMessage.messagePayload = message
|
||||
newMessage.messagePayloadMarkdown = generateMessageMarkdown(message: message)
|
||||
newMessage.read = true
|
||||
|
||||
let dataType = PortNum.textMessageApp
|
||||
var messageQuotesReplaced = message.replacingOccurrences(of: "’", with: "'")
|
||||
|
|
@ -2143,7 +2157,7 @@ extension BLEManager: CBCentralManagerDelegate {
|
|||
print("ℹ️ BLE Reconnecting to prefered peripheral: \(peripheral.name ?? "Unknown")")
|
||||
}
|
||||
let name = advertisementData[CBAdvertisementDataLocalNameKey] as? String
|
||||
let device = Peripheral(id: peripheral.identifier.uuidString, num: 0, name: name ?? "Unknown", shortName: "????", longName: name ?? "Unknown", firmwareVersion: "Unknown", rssi: RSSI.intValue, lastUpdate: Date(), peripheral: peripheral)
|
||||
let device = Peripheral(id: peripheral.identifier.uuidString, num: 0, name: name ?? "Unknown", shortName: "?", longName: name ?? "Unknown", firmwareVersion: "Unknown", rssi: RSSI.intValue, lastUpdate: Date(), peripheral: peripheral)
|
||||
let index = peripherals.map { $0.peripheral }.firstIndex(of: peripheral)
|
||||
|
||||
if let peripheralIndex = index {
|
||||
|
|
|
|||
|
|
@ -237,7 +237,7 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje
|
|||
return nil
|
||||
}
|
||||
// Not Found Insert
|
||||
if fetchedNode.isEmpty && nodeInfo.hasUser {
|
||||
if fetchedNode.isEmpty {
|
||||
|
||||
let newNode = NodeInfoEntity(context: context)
|
||||
newNode.id = Int64(nodeInfo.num)
|
||||
|
|
@ -296,6 +296,7 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje
|
|||
}
|
||||
do {
|
||||
try context.save()
|
||||
print("💾 Saved a new Node Info For: \(String(nodeInfo.num))")
|
||||
return newNode
|
||||
} catch {
|
||||
context.rollback()
|
||||
|
|
@ -314,7 +315,9 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje
|
|||
fetchedNode[0].channel = Int32(nodeInfo.channel)
|
||||
|
||||
if nodeInfo.hasUser {
|
||||
|
||||
if (fetchedNode[0].user == nil) {
|
||||
fetchedNode[0].user = UserEntity(context: context)
|
||||
}
|
||||
fetchedNode[0].user!.userId = nodeInfo.user.id
|
||||
fetchedNode[0].user!.num = Int64(nodeInfo.num)
|
||||
fetchedNode[0].user!.longName = nodeInfo.user.longName
|
||||
|
|
@ -532,26 +535,26 @@ func routingPacket (packet: MeshPacket, connectedNodeNum: Int64, context: NSMana
|
|||
fetchedMessage![0].ackSNR = packet.rxSnr
|
||||
fetchedMessage![0].ackTimestamp = Int32(packet.rxTime)
|
||||
|
||||
if fetchedMessage![0].toUser != nil {
|
||||
fetchedMessage![0].toUser?.objectWillChange.send()
|
||||
} else {
|
||||
let fetchMyInfoRequest: NSFetchRequest<NSFetchRequestResult> = 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<NSFetchRequestResult> = NSFetchRequest.init(entityName: "MyInfoEntity")
|
||||
// fetchMyInfoRequest.predicate = NSPredicate(format: "myNodeNum == %lld", connectedNodeNum)
|
||||
// do {
|
||||
// let fetchedMyInfo = try context.fetch(fetchMyInfoRequest) as? [MyInfoEntity]
|
||||
// if fetchedMyInfo?.count ?? 0 > 0 {
|
||||
//
|
||||
// for ch in fetchedMyInfo![0].channels!.array as? [ChannelEntity] ?? [] {
|
||||
//
|
||||
// if ch.index == packet.channel {
|
||||
// // ch.objectWillChange.send()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// } catch {
|
||||
//
|
||||
// }
|
||||
// }
|
||||
|
||||
} else {
|
||||
return
|
||||
|
|
@ -750,10 +753,9 @@ func textMessageAppPacket(packet: MeshPacket, blockRangeTest: Bool, connectedNod
|
|||
}
|
||||
newMessage.messagePayload = messageText
|
||||
newMessage.messagePayloadMarkdown = generateMessageMarkdown(message: messageText)
|
||||
if packet.to != 4294967295 {
|
||||
if packet.to != 4294967295 && newMessage.fromUser != nil {
|
||||
newMessage.fromUser?.lastMessage = Date()
|
||||
}
|
||||
|
||||
var messageSaved = false
|
||||
|
||||
do {
|
||||
|
|
@ -763,14 +765,20 @@ func textMessageAppPacket(packet: MeshPacket, blockRangeTest: Bool, connectedNod
|
|||
messageSaved = true
|
||||
|
||||
if messageSaved {
|
||||
let appState = AppState.shared
|
||||
if newMessage.fromUser != nil && newMessage.toUser != nil && !(newMessage.fromUser?.mute ?? false) {
|
||||
// Set Unread Message Indicators
|
||||
if packet.to == connectedNode {
|
||||
appState.unreadDirectMessages = newMessage.toUser?.unreadMessages ?? 0
|
||||
UIApplication.shared.applicationIconBadgeNumber = appState.unreadChannelMessages + appState.unreadDirectMessages
|
||||
}
|
||||
// Create an iOS Notification for the received DM message and schedule it immediately
|
||||
let manager = LocalNotificationManager()
|
||||
manager.notifications = [
|
||||
Notification(
|
||||
id: ("notification.id.\(newMessage.messageId)"),
|
||||
title: "\(newMessage.fromUser?.longName ?? "unknown".localized)",
|
||||
subtitle: "AKA \(newMessage.fromUser?.shortName ?? "???")",
|
||||
subtitle: "AKA \(newMessage.fromUser?.shortName ?? "?")",
|
||||
content: messageText,
|
||||
target: "message"
|
||||
)
|
||||
|
|
@ -787,6 +795,9 @@ func textMessageAppPacket(packet: MeshPacket, blockRangeTest: Bool, connectedNod
|
|||
return
|
||||
}
|
||||
if !fetchedMyInfo.isEmpty {
|
||||
appState.unreadChannelMessages = fetchedMyInfo[0].unreadMessages
|
||||
UIApplication.shared.applicationIconBadgeNumber = appState.unreadChannelMessages + appState.unreadDirectMessages
|
||||
|
||||
for channel in (fetchedMyInfo[0].channels?.array ?? []) as? [ChannelEntity] ?? [] {
|
||||
if channel.index == newMessage.channel {
|
||||
context.refresh(channel, mergeChanges: true)
|
||||
|
|
@ -798,7 +809,7 @@ func textMessageAppPacket(packet: MeshPacket, blockRangeTest: Bool, connectedNod
|
|||
Notification(
|
||||
id: ("notification.id.\(newMessage.messageId)"),
|
||||
title: "\(newMessage.fromUser?.longName ?? "unknown".localized)",
|
||||
subtitle: "AKA \(newMessage.fromUser?.shortName ?? "???")",
|
||||
subtitle: "AKA \(newMessage.fromUser?.shortName ?? "?")",
|
||||
content: messageText,
|
||||
target: "message")
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="21754" systemVersion="22G90" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="22221.1" systemVersion="23A5337a" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<entity name="BluetoothConfigEntity" representedClassName="BluetoothConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="fixedPin" optional="YES" attributeType="Integer 32" defaultValueString="123456" usesScalarValueType="YES"/>
|
||||
|
|
@ -181,6 +181,9 @@
|
|||
<attribute name="rebootCount" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="channels" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="ChannelEntity" inverseName="myInfoChannel" inverseEntity="ChannelEntity"/>
|
||||
<relationship name="myInfoNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="myInfo" inverseEntity="NodeInfoEntity"/>
|
||||
<fetchedProperty name="allMessages" optional="YES">
|
||||
<fetchRequest name="fetchedPropertyFetchRequest" entity="MessageEntity" predicateString="toUser == nil"/>
|
||||
</fetchedProperty>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="myNodeNum"/>
|
||||
|
|
|
|||
|
|
@ -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,8 @@ class AppState: ObservableObject {
|
|||
static let shared = AppState()
|
||||
|
||||
@Published var tabSelection: Tab = .ble
|
||||
@Published var unreadDirectMessages: Int = 0
|
||||
@Published var unreadChannelMessages: Int = 0
|
||||
@Published var firmwareVersion: String = "0.0.0"
|
||||
@Published var connectedNode: NodeInfoEntity?
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext)
|
|||
print("💥 Error Inserting New Core Data MyInfoEntity: \(nsError)")
|
||||
}
|
||||
newNode.myInfo = myInfoEntity
|
||||
newNode.objectWillChange.send()
|
||||
//newNode.objectWillChange.send()
|
||||
|
||||
} else {
|
||||
// Update an existing node
|
||||
|
|
@ -476,7 +476,6 @@ func upsertLoRaConfigPacket(config: Meshtastic.Config.LoRaConfig, nodeNum: Int64
|
|||
}
|
||||
do {
|
||||
try context.save()
|
||||
context.refresh(fetchedNode[0], mergeChanges: true)
|
||||
print("💾 Updated LoRa Config for node number: \(String(nodeNum))")
|
||||
} catch {
|
||||
context.rollback()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
.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) {
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -8,29 +8,44 @@ import SwiftUI
|
|||
struct CircleText: View {
|
||||
var text: String
|
||||
var color: Color
|
||||
var circleSize: CGFloat? = 60
|
||||
var fontSize: CGFloat? = 20
|
||||
var brightness: Double? = 0
|
||||
var textColor: Color? = .white
|
||||
|
||||
var circleSize: CGFloat = 45
|
||||
|
||||
var body: some View {
|
||||
|
||||
let font = Font.system(size: fontSize!)
|
||||
|
||||
ZStack {
|
||||
Circle()
|
||||
.fill(color)
|
||||
.brightness(brightness ?? 0)
|
||||
.frame(width: circleSize, height: circleSize)
|
||||
Text(text).textCase(.uppercase).font(font).foregroundColor(textColor).fixedSize()
|
||||
.frame(width: circleSize, height: circleSize, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/).offset(x: 0, y: 0)
|
||||
Text(text)
|
||||
.textCase(.uppercase)
|
||||
.foregroundColor(color.isLight() ? .black : .white)
|
||||
.font(.system(size: 500))
|
||||
.minimumScaleFactor(0.001)
|
||||
.frame(width: circleSize * 0.94, height: circleSize * 0.94, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/)
|
||||
}
|
||||
.aspectRatio(1, contentMode: .fit)
|
||||
}
|
||||
}
|
||||
|
||||
struct CircleText_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
CircleText(text: "MOMO", color: Color.accentColor)
|
||||
.previewLayout(.fixed(width: 300, height: 100))
|
||||
VStack {
|
||||
CircleText(text: "MOMO", color: Color.secondary, circleSize: 80)
|
||||
.previewLayout(.fixed(width: 300, height: 100))
|
||||
CircleText(text: "WWWW", color: Color.accentColor, circleSize: 80)
|
||||
.previewLayout(.fixed(width: 300, height: 100))
|
||||
CircleText(text: "LCP", color: Color.primary, circleSize: 80)
|
||||
.previewLayout(.fixed(width: 300, height: 100))
|
||||
CircleText(text: "69", color: Color.green, circleSize: 80)
|
||||
.previewLayout(.fixed(width: 300, height: 100))
|
||||
CircleText(text: "N1", color: Color.yellow, circleSize: 80)
|
||||
.previewLayout(.fixed(width: 300, height: 100))
|
||||
CircleText(text: "8", color: Color.purple, circleSize: 80)
|
||||
.previewLayout(.fixed(width: 300, height: 100))
|
||||
CircleText(text: "😝", color: Color.red, circleSize: 80)
|
||||
.previewLayout(.fixed(width: 300, height: 100))
|
||||
CircleText(text: "🍔", color: Color.brown, circleSize: 80)
|
||||
.previewLayout(.fixed(width: 300, height: 100))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ struct NodeInfoView: View {
|
|||
if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
|
||||
HStack {
|
||||
VStack(alignment: .center) {
|
||||
CircleText(text: node.user?.shortName ?? "???", color: Color(UIColor(hex: UInt32(node.num))), circleSize: 150, fontSize: (node.user?.shortName ?? "???").isEmoji() ? 105 : 55, textColor: UIColor(hex: UInt32(node.num)).isLight() ? .black : .white )
|
||||
CircleText(text: node.user?.shortName ?? "?", color: Color(UIColor(hex: UInt32(node.num))), circleSize: 150)
|
||||
}
|
||||
Divider()
|
||||
VStack {
|
||||
|
|
@ -58,8 +58,9 @@ struct NodeInfoView: View {
|
|||
}
|
||||
Divider()
|
||||
}
|
||||
let deviceMetrics = node.telemetries?.filtered(using: NSPredicate(format: "metricsType == 0"))
|
||||
if deviceMetrics?.count ?? 0 >= 1 {
|
||||
|
||||
if node.hasDeviceMetrics {
|
||||
let deviceMetrics = node.telemetries?.filtered(using: NSPredicate(format: "metricsType == 0"))
|
||||
let mostRecent = deviceMetrics?.lastObject as? TelemetryEntity
|
||||
VStack(alignment: .center) {
|
||||
BatteryGauge(batteryLevel: Double(mostRecent?.batteryLevel ?? 0))
|
||||
|
|
@ -123,7 +124,7 @@ struct NodeInfoView: View {
|
|||
HStack {
|
||||
|
||||
VStack(alignment: .center) {
|
||||
CircleText(text: node.user?.shortName ?? "???", color: Color(UIColor(hex: UInt32(node.num))), circleSize: 65, fontSize: (node.user?.shortName ?? "???").isEmoji() ? 42 : 20, textColor: UIColor(hex: UInt32(node.num)).isLight() ? .black : .white )
|
||||
CircleText(text: node.user?.shortName ?? "?", color: Color(UIColor(hex: UInt32(node.num))), circleSize: 65)
|
||||
}
|
||||
if node.user != nil {
|
||||
Divider()
|
||||
|
|
@ -177,7 +178,7 @@ struct NodeInfoView: View {
|
|||
.symbolRenderingMode(.hierarchical)
|
||||
Text("User Id:").font(.title2)
|
||||
}
|
||||
Text(node.user?.userId ?? "??????").font(.title3).foregroundColor(.gray)
|
||||
Text(node.user?.userId ?? "?").font(.title3).foregroundColor(.gray)
|
||||
}
|
||||
Divider()
|
||||
VStack {
|
||||
|
|
@ -196,7 +197,7 @@ struct NodeInfoView: View {
|
|||
|
||||
VStack {
|
||||
|
||||
if (node.positions?.count ?? 0) > 0 {
|
||||
if node.hasPositions{
|
||||
|
||||
NavigationLink {
|
||||
PositionLog(node: node)
|
||||
|
|
@ -213,20 +214,22 @@ struct NodeInfoView: View {
|
|||
Divider()
|
||||
}
|
||||
|
||||
if (node.telemetries?.count ?? 0) > 0 {
|
||||
|
||||
if node.hasDeviceMetrics {
|
||||
|
||||
NavigationLink {
|
||||
DeviceMetricsLog(node: node)
|
||||
} label: {
|
||||
|
||||
|
||||
Image(systemName: "flipphone")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
.font(.title)
|
||||
|
||||
|
||||
Text("Device Metrics Log")
|
||||
.font(.title3)
|
||||
}
|
||||
Divider()
|
||||
}
|
||||
if node.hasEnvironmentMetrics {
|
||||
NavigationLink {
|
||||
EnvironmentMetricsLog(node: node)
|
||||
} label: {
|
||||
|
|
|
|||
|
|
@ -75,23 +75,24 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
mapView.showsBuildings = true
|
||||
mapView.showsScale = true
|
||||
mapView.showsTraffic = true
|
||||
#if targetEnvironment(macCatalyst)
|
||||
// Show the default always visible compass and the mac only controls
|
||||
mapView.showsCompass = true
|
||||
mapView.showsZoomControls = true
|
||||
mapView.showsPitchControl = true
|
||||
#else
|
||||
#if os(iOS)
|
||||
// Move the default compass under the mapbuttons control
|
||||
|
||||
mapView.showsCompass = false
|
||||
let compass = MKCompassButton(mapView: mapView)
|
||||
compass.translatesAutoresizingMaskIntoConstraints = false
|
||||
#if targetEnvironment(macCatalyst)
|
||||
// Show the default always visible compass and the mac only controls
|
||||
compass.compassVisibility = .visible
|
||||
mapView.addSubview(compass)
|
||||
mapView.showsZoomControls = true
|
||||
mapView.showsPitchControl = true
|
||||
compass.trailingAnchor.constraint(equalTo: mapView.trailingAnchor, constant: -115).isActive = true
|
||||
compass.bottomAnchor.constraint(equalTo: mapView.bottomAnchor, constant: -5).isActive = true
|
||||
#else
|
||||
compass.compassVisibility = .adaptive
|
||||
mapView.addSubview(compass)
|
||||
compass.trailingAnchor.constraint(equalTo: mapView.trailingAnchor, constant: -5).isActive = true
|
||||
compass.topAnchor.constraint(equalTo: mapView.topAnchor, constant: 145).isActive = true
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
private func setMapBaseLayer(mapView: MKMapView) {
|
||||
// Avoid refreshing UI if selectedLayer has not changed
|
||||
|
|
@ -251,18 +252,17 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
mapView.removeAnnotations(mapView.annotations)
|
||||
mapView.addAnnotations(waypoints)
|
||||
}
|
||||
mapView.addAnnotations(showNodeHistory ? positions : latest)
|
||||
if userTrackingMode == MKUserTrackingMode.none {
|
||||
mapView.showsUserLocation = false
|
||||
if UserDefaults.enableMapRecentering {
|
||||
if latest.count == 1 {
|
||||
mapView.fit(annotations: showNodeHistory ? positions : latest, andShow: true)
|
||||
} else {
|
||||
mapView.addAnnotations(showNodeHistory ? positions : latest)
|
||||
mapView.fitAllAnnotations()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mapView.addAnnotations(showNodeHistory ? positions : latest)
|
||||
mapView.showsUserLocation = true
|
||||
}
|
||||
mapView.setUserTrackingMode(userTrackingMode, animated: true)
|
||||
|
|
@ -290,11 +290,11 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
annotationView.tag = -1
|
||||
annotationView.canShowCallout = true
|
||||
if positionAnnotation.latest {
|
||||
annotationView.markerTintColor = .systemRed
|
||||
annotationView.markerTintColor = UIColor(hex: UInt32(positionAnnotation.nodePosition?.num ?? 0)).darker()
|
||||
annotationView.displayPriority = .required
|
||||
annotationView.titleVisibility = .visible
|
||||
} else {
|
||||
annotationView.markerTintColor = UIColor(hex: UInt32(positionAnnotation.nodePosition?.num ?? 0))
|
||||
annotationView.markerTintColor = UIColor(hex: UInt32(positionAnnotation.nodePosition?.num ?? 0)).lighter()
|
||||
annotationView.displayPriority = .defaultHigh
|
||||
annotationView.titleVisibility = .adaptive
|
||||
}
|
||||
|
|
@ -409,9 +409,7 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
}
|
||||
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
|
||||
switch view.annotation {
|
||||
case let positionAnnotation as PositionEntity:
|
||||
print(positionAnnotation)
|
||||
case let waypointAnnotation as WaypointEntity:
|
||||
case _ as WaypointEntity:
|
||||
// Only Allow Edit for waypoint annotations with a id
|
||||
if view.tag > 0 {
|
||||
parent.onWaypointEdit(view.tag)
|
||||
|
|
@ -442,7 +440,7 @@ struct MapViewSwiftUI: UIViewRepresentable {
|
|||
if let routePolyline = overlay as? MKPolyline {
|
||||
let titleString = routePolyline.title ?? "0"
|
||||
let renderer = MKPolylineRenderer(polyline: routePolyline)
|
||||
renderer.strokeColor = UIColor(hex: UInt32(titleString) ?? 0)
|
||||
renderer.strokeColor = UIColor(hex: UInt32(titleString) ?? 0).lighter()
|
||||
renderer.lineWidth = 8
|
||||
return renderer
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import CoreData
|
|||
|
||||
struct ChannelList: View {
|
||||
|
||||
@StateObject var appState = AppState.shared
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
||||
|
|
@ -32,7 +33,7 @@ struct ChannelList: View {
|
|||
ForEach(node!.myInfo!.channels!.array as! [ChannelEntity], id: \.self) { (channel: ChannelEntity) in
|
||||
if channel.name?.lowercased() ?? "" != "admin" && channel.name?.lowercased() ?? "" != "gpio" && channel.name?.lowercased() ?? "" != "serial" {
|
||||
|
||||
NavigationLink(destination: ChannelMessageList(channel: channel)) {
|
||||
NavigationLink(destination: ChannelMessageList(myInfo: node!.myInfo!, channel: channel)) {
|
||||
|
||||
let mostRecent = channel.allPrivateMessages.last(where: { $0.channel == channel.index })
|
||||
let lastMessageTime = Date(timeIntervalSince1970: TimeInterval(Int64((mostRecent?.messageTimestamp ?? 0 ))))
|
||||
|
|
@ -47,7 +48,7 @@ struct ChannelList: View {
|
|||
.foregroundColor(.accentColor)
|
||||
.brightness(0.2)
|
||||
}
|
||||
CircleText(text: String(channel.index), color: .accentColor, circleSize: 45, fontSize: 40)
|
||||
CircleText(text: String(channel.index), color: .accentColor)
|
||||
.brightness(0.2)
|
||||
|
||||
VStack(alignment: .leading){
|
||||
|
|
@ -100,23 +101,6 @@ struct ChannelList: View {
|
|||
}
|
||||
.frame(height: 62)
|
||||
.contextMenu {
|
||||
Button {
|
||||
channel.mute = !channel.mute
|
||||
|
||||
do {
|
||||
try context.save()
|
||||
// Would rather not do this but the merge changes on
|
||||
// A single object is only working on mac GVH
|
||||
context.refreshAllObjects()
|
||||
// context.refresh(channel, mergeChanges: true)
|
||||
} catch {
|
||||
context.rollback()
|
||||
print("💥 Save Channel Mute Error")
|
||||
}
|
||||
} label: {
|
||||
Label(channel.mute ? "Show Alerts" : "Hide Alerts", systemImage: channel.mute ? "bell" : "bell.slash")
|
||||
}
|
||||
|
||||
if channel.allPrivateMessages.count > 0 {
|
||||
Button(role: .destructive) {
|
||||
isPresentingDeleteChannelMessagesConfirm = true
|
||||
|
|
@ -134,6 +118,7 @@ struct ChannelList: View {
|
|||
Button(role: .destructive) {
|
||||
deleteChannelMessages(channel: channelSelection!, context: context)
|
||||
context.refresh(node!.myInfo!, mergeChanges: true)
|
||||
UIApplication.shared.applicationIconBadgeNumber = appState.unreadChannelMessages + appState.unreadDirectMessages
|
||||
channelSelection = nil
|
||||
} label: {
|
||||
Text("delete")
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import CoreData
|
|||
|
||||
struct ChannelMessageList: View {
|
||||
|
||||
@StateObject var appState = AppState.shared
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
||||
|
|
@ -23,6 +24,7 @@ struct ChannelMessageList: View {
|
|||
var maxbytes = 228
|
||||
@FocusState var focusedField: Field?
|
||||
|
||||
@ObservedObject var myInfo: MyInfoEntity
|
||||
@ObservedObject var channel: ChannelEntity
|
||||
@State var showDeleteMessageAlert = false
|
||||
@State private var deleteMessageId: Int64 = 0
|
||||
|
|
@ -56,7 +58,7 @@ struct ChannelMessageList: View {
|
|||
HStack(alignment: .top) {
|
||||
if currentUser { Spacer(minLength: 50) }
|
||||
if !currentUser {
|
||||
CircleText(text: message.fromUser?.shortName ?? "????", color: Color(UIColor(hex: UInt32(message.fromUser?.num ?? 0))), circleSize: 44, fontSize: 14, textColor: UIColor(hex: UInt32(message.fromUser?.num ?? 0)).isLight() ? .black : .white)
|
||||
CircleText(text: message.fromUser?.shortName ?? "?", color: Color(UIColor(hex: UInt32(message.fromUser?.num ?? 0))), circleSize: 44)
|
||||
.padding(.all, 5)
|
||||
.offset(y: -5)
|
||||
}
|
||||
|
|
@ -170,7 +172,7 @@ struct ChannelMessageList: View {
|
|||
VStack {
|
||||
let image = tapback.messagePayload!.image(fontSize: 20)
|
||||
Image(uiImage: image!).font(.caption)
|
||||
Text("\(tapback.fromUser?.shortName ?? "????")")
|
||||
Text("\(tapback.fromUser?.shortName ?? "?")")
|
||||
.font(.caption2)
|
||||
.foregroundColor(.gray)
|
||||
.fixedSize()
|
||||
|
|
@ -229,10 +231,12 @@ struct ChannelMessageList: View {
|
|||
.onAppear {
|
||||
if !message.read {
|
||||
message.read = true
|
||||
message.toUser?.objectWillChange.send()
|
||||
do {
|
||||
try context.save()
|
||||
print("Read message \(message.messageId) ")
|
||||
appState.unreadChannelMessages = myInfo.unreadMessages
|
||||
UIApplication.shared.applicationIconBadgeNumber = appState.unreadChannelMessages + appState.unreadDirectMessages
|
||||
context.refresh(myInfo, mergeChanges: true)
|
||||
} catch {
|
||||
print("Failed to read message \(message.messageId)")
|
||||
}
|
||||
|
|
@ -401,7 +405,7 @@ struct ChannelMessageList: View {
|
|||
.toolbar {
|
||||
ToolbarItem(placement: .principal) {
|
||||
HStack {
|
||||
CircleText(text: String(channel.index), color: .accentColor, circleSize: 44, fontSize: 30).fixedSize()
|
||||
CircleText(text: String(channel.index), color: .accentColor, circleSize: 44).fixedSize()
|
||||
Text(String(channel.name ?? "unknown".localized).camelCaseToWords()).font(.headline)
|
||||
}
|
||||
}
|
||||
|
|
@ -410,7 +414,7 @@ struct ChannelMessageList: View {
|
|||
ConnectedDevice(
|
||||
bluetoothOn: bleManager.isSwitchedOn,
|
||||
deviceConnected: bleManager.connectedPeripheral != nil,
|
||||
name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????")
|
||||
name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<UserEntity>
|
||||
// @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<NSFetchRequestResult> = 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")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
|
@ -10,6 +10,7 @@ import CoreData
|
|||
|
||||
struct Messages: View {
|
||||
|
||||
@StateObject var appState = AppState.shared
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
||||
|
|
@ -38,6 +39,7 @@ struct Messages: View {
|
|||
.brightness(0.2)
|
||||
Text("channels")
|
||||
.font(.title2)
|
||||
.badge(appState.unreadChannelMessages)
|
||||
}
|
||||
NavigationLink {
|
||||
UserList(node: node)
|
||||
|
|
@ -48,9 +50,11 @@ struct Messages: View {
|
|||
.brightness(0.2)
|
||||
Text("direct.messages")
|
||||
.font(.title2)
|
||||
.badge(appState.unreadDirectMessages)
|
||||
}
|
||||
}
|
||||
.navigationTitle("messages")
|
||||
.navigationBarTitleDisplayMode(.large)
|
||||
.navigationBarItems(leading: MeshtasticLogo())
|
||||
.onAppear {
|
||||
self.bleManager.context = context
|
||||
|
|
|
|||
|
|
@ -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<String> {
|
||||
Binding {
|
||||
|
|
@ -19,12 +22,8 @@ struct UserList: View {
|
|||
users.nsPredicate = newValue.isEmpty ? nil : NSPredicate(format: "longName CONTAINS[c] %@ OR shortName CONTAINS[c] %@", newValue, newValue)
|
||||
}
|
||||
}
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
||||
@FetchRequest(
|
||||
sortDescriptors: [NSSortDescriptor(key: "lastMessage", ascending: false), NSSortDescriptor(key: "longName", ascending: true)],
|
||||
sortDescriptors: [NSSortDescriptor(key: "lastMessage", ascending: false), NSSortDescriptor(key: "vip", ascending: false), NSSortDescriptor(key: "longName", ascending: true)],
|
||||
animation: .default)
|
||||
|
||||
private var users: FetchedResults<UserEntity>
|
||||
|
|
@ -56,14 +55,17 @@ struct UserList: View {
|
|||
.brightness(0.2)
|
||||
}
|
||||
|
||||
CircleText(text: user.shortName ?? "???", color: Color(UIColor(hex: UInt32(user.num))), circleSize: 45, fontSize: (user.shortName ?? "???").isEmoji() ? 32 : (user.shortName?.count ?? 0 == 4 ? 14 : (user.shortName?.count ?? 0 == 3 ? 18 : 22)), brightness: 0.0, textColor: UIColor(hex: UInt32(user.num)).isLight() ? .black : .white)
|
||||
CircleText(text: user.shortName ?? "?", color: Color(UIColor(hex: UInt32(user.num))))
|
||||
|
||||
VStack(alignment: .leading){
|
||||
HStack{
|
||||
Text(user.longName ?? "unknown".localized)
|
||||
|
||||
Spacer()
|
||||
|
||||
if user.vip {
|
||||
Image(systemName: "star.fill")
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
if user.messageList.count > 0 {
|
||||
if lastMessageDay == currentDay {
|
||||
Text(lastMessageTime, style: .time )
|
||||
|
|
@ -99,6 +101,17 @@ struct UserList: View {
|
|||
}
|
||||
.frame(height: 62)
|
||||
.contextMenu {
|
||||
Button {
|
||||
user.vip = !user.vip
|
||||
do {
|
||||
try context.save()
|
||||
} catch {
|
||||
context.rollback()
|
||||
print("💥 Save User VIP Error")
|
||||
}
|
||||
} label: {
|
||||
Label(user.vip ? "Un-Favorite" : "Favorite", systemImage: user.vip ? "star.slash.fill" : "star.fill")
|
||||
}
|
||||
Button {
|
||||
user.mute = !user.mute
|
||||
do {
|
||||
|
|
@ -144,6 +157,7 @@ struct UserList: View {
|
|||
Button(role: .destructive) {
|
||||
deleteUserMessages(user: userSelection!, context: context)
|
||||
context.refresh(node!.user!, mergeChanges: true)
|
||||
UIApplication.shared.applicationIconBadgeNumber = appState.unreadChannelMessages + appState.unreadDirectMessages
|
||||
} label: {
|
||||
Text("delete")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import CoreData
|
|||
|
||||
struct UserMessageList: View {
|
||||
|
||||
@StateObject var appState = AppState.shared
|
||||
@Environment(\.managedObjectContext) var context
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
|
||||
|
|
@ -160,7 +161,7 @@ struct UserMessageList: View {
|
|||
VStack {
|
||||
let image = tapback.messagePayload!.image(fontSize: 20)
|
||||
Image(uiImage: image!).font(.caption)
|
||||
Text("\(tapback.fromUser?.shortName ?? "????")")
|
||||
Text("\(tapback.fromUser?.shortName ?? "?")")
|
||||
.font(.caption2)
|
||||
.foregroundColor(.gray)
|
||||
.fixedSize()
|
||||
|
|
@ -219,10 +220,12 @@ struct UserMessageList: View {
|
|||
.onAppear {
|
||||
if !message.read {
|
||||
message.read = true
|
||||
message.toUser?.objectWillChange.send()
|
||||
do {
|
||||
try context.save()
|
||||
print("Read message \(message.messageId) ")
|
||||
appState.unreadDirectMessages = user.unreadMessages
|
||||
UIApplication.shared.applicationIconBadgeNumber = appState.unreadChannelMessages + appState.unreadDirectMessages
|
||||
|
||||
} catch {
|
||||
print("Failed to read message \(message.messageId)")
|
||||
}
|
||||
|
|
@ -364,7 +367,7 @@ struct UserMessageList: View {
|
|||
.toolbar {
|
||||
ToolbarItem(placement: .principal) {
|
||||
HStack {
|
||||
CircleText(text: user.shortName ?? "???", color: Color(UIColor(hex: UInt32(user.num))), circleSize: 44, fontSize: (user.shortName ?? "???").isEmoji() ? 32 : (user.shortName?.count ?? 0 == 4 ? 14 : (user.shortName?.count ?? 0 == 3 ? 18 : 22)), brightness: 0.0, textColor: UIColor(hex: UInt32(user.num)).isLight() ? .black : .white)
|
||||
CircleText(text: user.shortName ?? "?", color: Color(UIColor(hex: UInt32(user.num))), circleSize: 44)
|
||||
}
|
||||
}
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
|
|
@ -372,7 +375,7 @@ struct UserMessageList: View {
|
|||
ConnectedDevice(
|
||||
bluetoothOn: bleManager.isSwitchedOn,
|
||||
deviceConnected: bleManager.connectedPeripheral != nil,
|
||||
name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????")
|
||||
name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ struct DeviceMetricsLog: View {
|
|||
.font(.caption)
|
||||
Text("\(String(format: "%.2f", dm.airUtilTx))%")
|
||||
.font(.caption)
|
||||
Text(dm.time?.formattedDate(format: dateFormatString) ?? "Unknown time")
|
||||
Text(dm.time?.formattedDate(format: dateFormatString) ?? "unknown.age".localized)
|
||||
.font(.caption)
|
||||
}
|
||||
}
|
||||
|
|
@ -208,7 +208,7 @@ struct DeviceMetricsLog: View {
|
|||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationBarItems(trailing:
|
||||
ZStack {
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????")
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?")
|
||||
})
|
||||
.onAppear {
|
||||
self.bleManager.context = context
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -16,9 +16,7 @@ struct NodeDetail: View {
|
|||
@AppStorage("meshMapType") private var meshMapType = 0
|
||||
@AppStorage("meshMapShowNodeHistory") private var meshMapShowNodeHistory = false
|
||||
@AppStorage("meshMapShowRouteLines") private var meshMapShowRouteLines = false
|
||||
//@State private var mapType: MKMapType = .standard
|
||||
@State private var selectedMapLayer: MapLayer = .standard
|
||||
@State var mapRect: MKMapRect = MKMapRect()
|
||||
@State var waypointCoordinate: WaypointCoordinate?
|
||||
@State var editingWaypoint: Int = 0
|
||||
@State private var loadedWeather: Bool = false
|
||||
|
|
@ -26,15 +24,12 @@ struct NodeDetail: View {
|
|||
@State private var showingForecast = false
|
||||
@State private var showingShutdownConfirm: Bool = false
|
||||
@State private var showingRebootConfirm: Bool = false
|
||||
@State private var showOverlays: Bool = true
|
||||
@State private var customMapOverlay: MapViewSwiftUI.CustomMapOverlay? = MapViewSwiftUI.CustomMapOverlay(
|
||||
mapName: "offlinemap",
|
||||
tileType: "png",
|
||||
canReplaceMapContent: true
|
||||
)
|
||||
|
||||
mapName: "offlinemap",
|
||||
tileType: "png",
|
||||
canReplaceMapContent: true
|
||||
)
|
||||
var node: NodeInfoEntity
|
||||
|
||||
@FetchRequest(sortDescriptors: [NSSortDescriptor(key: "name", ascending: false)],
|
||||
predicate: NSPredicate(
|
||||
format: "expire == nil || expire >= %@", Date() as NSDate
|
||||
|
|
@ -49,13 +44,14 @@ struct NodeDetail: View {
|
|||
|
||||
@State private var attributionLink: URL?
|
||||
@State private var attributionLogo: URL?
|
||||
|
||||
var body: some View {
|
||||
|
||||
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral?.num ?? -1, context: context)
|
||||
NavigationStack {
|
||||
GeometryReader { bounds in
|
||||
VStack {
|
||||
if node.positions?.count ?? 0 > 0 {
|
||||
if node.hasPositions {
|
||||
ZStack {
|
||||
let positionArray = node.positions?.array as? [PositionEntity] ?? []
|
||||
let lastTenThousand = Array(positionArray.prefix(10000))
|
||||
|
|
@ -68,11 +64,9 @@ struct NodeDetail: View {
|
|||
waypointCoordinate = WaypointCoordinate(id: .init(), coordinate: nil, waypointId: Int64(wpId))
|
||||
}
|
||||
},
|
||||
// visibleMapRect: $mapRect,
|
||||
selectedMapLayer: selectedMapLayer,
|
||||
positions: lastTenThousand,
|
||||
waypoints: Array(waypoints),
|
||||
// mapViewType: mapType,
|
||||
userTrackingMode: MKUserTrackingMode.none,
|
||||
showNodeHistory: meshMapShowNodeHistory,
|
||||
showRouteLines: meshMapShowRouteLines,
|
||||
|
|
@ -223,16 +217,12 @@ struct NodeDetail: View {
|
|||
ConnectedDevice(
|
||||
bluetoothOn: bleManager.isSwitchedOn,
|
||||
deviceConnected: bleManager.connectedPeripheral != nil,
|
||||
name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????")
|
||||
name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?")
|
||||
})
|
||||
.onAppear {
|
||||
self.bleManager.context = context
|
||||
// mapType = .standard// MeshMapTypes(rawValue: meshMapType)?.MKMapTypeValue() ?? .standard
|
||||
}
|
||||
.task(id: node.num) {
|
||||
if !loadedWeather {
|
||||
do {
|
||||
if node.positions?.count ?? 0 > 0 {
|
||||
if node.hasPositions {
|
||||
let mostRecent = node.positions?.lastObject as? PositionEntity
|
||||
let weather = try await WeatherService.shared.weather(for: mostRecent?.nodeLocation ?? CLLocation(latitude: LocationHelper.currentLocation.latitude, longitude: LocationHelper.currentLocation.longitude))
|
||||
condition = weather.currentWeather.condition
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ struct NodeList: View {
|
|||
LazyVStack(alignment: .leading) {
|
||||
HStack {
|
||||
VStack(alignment: .leading) {
|
||||
CircleText(text: node.user?.shortName ?? "???", color: Color(UIColor(hex: UInt32(node.num))), circleSize: 65, fontSize: (node.user?.shortName ?? "???").isEmoji() ? 44 : (node.user?.shortName?.count ?? 0 == 4 ? 19 : 26), brightness: 0.0, textColor: UIColor(hex: UInt32(node.num)).isLight() ? .black : .white)
|
||||
CircleText(text: node.user?.shortName ?? "?", color: Color(UIColor(hex: UInt32(node.num))), circleSize: 65)
|
||||
.padding(.trailing, 5)
|
||||
let deviceMetrics = node.telemetries?.filtered(using: NSPredicate(format: "metricsType == 0"))
|
||||
if deviceMetrics?.count ?? 0 >= 1 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<PositionEntity>] = [
|
||||
.init(\.latitude, order: SortOrder.forward)
|
||||
]
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
let localeDateFormat = DateFormatter.dateFormat(fromTemplate: "yyMMddjmma", options: 0, locale: Locale.current)
|
||||
|
|
@ -27,7 +31,7 @@ struct PositionLog: View {
|
|||
if UIDevice.current.userInterfaceIdiom == .pad && !useGrid || UIDevice.current.userInterfaceIdiom == .mac {
|
||||
// Add a table for mac and ipad
|
||||
let positions = node.positions?.reversed() as? [PositionEntity] ?? []
|
||||
Table(positions) {
|
||||
Table(positions, sortOrder: $sortOrder) {
|
||||
TableColumn("Latitude") { position in
|
||||
Text(String(format: "%.5f", position.latitude ?? 0))
|
||||
}
|
||||
|
|
@ -58,6 +62,7 @@ struct PositionLog: View {
|
|||
}
|
||||
.width(min: 180)
|
||||
}
|
||||
|
||||
} else {
|
||||
ScrollView {
|
||||
// Use a grid on iOS as a table only shows a single column
|
||||
|
|
@ -97,7 +102,7 @@ struct PositionLog: View {
|
|||
.font(.caption2)
|
||||
Text(altitude.formatted())
|
||||
.font(.caption2)
|
||||
Text(mappin.time?.formattedDate(format: dateFormatString) ?? "Unknown time")
|
||||
Text(mappin.time?.formattedDate(format: dateFormatString) ?? "unknown.age".localized)
|
||||
.font(.caption2)
|
||||
}
|
||||
}
|
||||
|
|
@ -159,7 +164,7 @@ struct PositionLog: View {
|
|||
.navigationTitle("Position Log \(node.positions?.count ?? 0) Points")
|
||||
.navigationBarItems(trailing:
|
||||
ZStack {
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????")
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?")
|
||||
})
|
||||
.onAppear {
|
||||
self.bleManager.context = context
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -64,8 +64,9 @@ struct Channels: View {
|
|||
}) {
|
||||
VStack(alignment: .leading) {
|
||||
HStack {
|
||||
CircleText(text: String(channel.index), color: .accentColor, circleSize: 45, fontSize: 36, brightness: 0.1)
|
||||
CircleText(text: String(channel.index), color: .accentColor, circleSize: 45)
|
||||
.padding(.trailing, 5)
|
||||
.brightness(0.1)
|
||||
VStack {
|
||||
HStack {
|
||||
if channel.name?.isEmpty ?? false {
|
||||
|
|
@ -281,7 +282,7 @@ struct Channels: View {
|
|||
.navigationTitle("channels")
|
||||
.navigationBarItems(trailing:
|
||||
ZStack {
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????")
|
||||
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?")
|
||||
})
|
||||
.onAppear {
|
||||
bleManager.context = context
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue