From 6f05bbe7f3fdd43acdaf0eb5b884a70bfa1a2e6e Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Mon, 1 May 2023 10:39:49 -0700 Subject: [PATCH] Assorted updates --- Meshtastic/Enums/PositionConfigEnums.swift | 6 + .../Protobufs/meshtastic/clientonly.pb.swift | 163 ++++++++++++++++++ .../Protobufs/meshtastic/deviceonly.pb.swift | 21 +++ Meshtastic/Protobufs/meshtastic/mesh.pb.swift | 128 ++++++++++++++ .../meshtastic/module_config.pb.swift | 11 ++ .../Protobufs/meshtastic/portnums.pb.swift | 8 + .../Views/Map/Custom/MapViewSwiftUI.swift | 17 +- .../Views/Settings/Config/DeviceConfig.swift | 2 +- de.lproj/Localizable.strings | 1 + en.lproj/Localizable.strings | 1 + zh-Hans.lproj/Localizable.strings | 1 + 11 files changed, 354 insertions(+), 5 deletions(-) create mode 100644 Meshtastic/Protobufs/meshtastic/clientonly.pb.swift diff --git a/Meshtastic/Enums/PositionConfigEnums.swift b/Meshtastic/Enums/PositionConfigEnums.swift index fbfb956a..b4432dc5 100644 --- a/Meshtastic/Enums/PositionConfigEnums.swift +++ b/Meshtastic/Enums/PositionConfigEnums.swift @@ -61,6 +61,7 @@ enum GpsUpdateIntervals: Int, CaseIterable, Identifiable { case twentySeconds = 20 case twentyFiveSeconds = 25 case thirtySeconds = 30 + case fortyFiveSeconds = 45 case oneMinute = 60 case twoMinutes = 120 case fiveMinutes = 300 @@ -90,6 +91,8 @@ enum GpsUpdateIntervals: Int, CaseIterable, Identifiable { return NSLocalizedString("interval.thirty.seconds", comment: "Thirty Seconds") case .oneMinute: return NSLocalizedString("interval.one.minute", comment: "One Minute") + case .fortyFiveSeconds: + return NSLocalizedString("interval.fortyfive.seconds", comment: "Forty Five Seconds") case .twoMinutes: return NSLocalizedString("interval.two.minutes", comment: "Two Minutes") case .fiveMinutes: @@ -123,6 +126,7 @@ enum GpsAttemptTimes: Int, CaseIterable, Identifiable { case twentySeconds = 20 case twentyFiveSeconds = 25 case thirtySeconds = 30 + case fortyFiveSeconds = 45 case oneMinute = 60 case twoMinutes = 120 case fiveMinutes = 300 @@ -146,6 +150,8 @@ enum GpsAttemptTimes: Int, CaseIterable, Identifiable { return NSLocalizedString("interval.twentyfive.seconds", comment: "Twenty Five Seconds") case .thirtySeconds: return NSLocalizedString("interval.thirty.seconds", comment: "Thirty Seconds") + case .fortyFiveSeconds: + return NSLocalizedString("interval.fortyfive.seconds", comment: "Forty Five Seconds") case .oneMinute: return NSLocalizedString("interval.one.minute", comment: "One Minute") case .twoMinutes: diff --git a/Meshtastic/Protobufs/meshtastic/clientonly.pb.swift b/Meshtastic/Protobufs/meshtastic/clientonly.pb.swift new file mode 100644 index 00000000..050c719d --- /dev/null +++ b/Meshtastic/Protobufs/meshtastic/clientonly.pb.swift @@ -0,0 +1,163 @@ +// DO NOT EDIT. +// swift-format-ignore-file +// +// Generated by the Swift generator plugin for the protocol buffer compiler. +// Source: meshtastic/clientonly.proto +// +// For information on using the generated types, please see the documentation: +// https://github.com/apple/swift-protobuf/ + +import Foundation +import SwiftProtobuf + +// If the compiler emits an error on this type, it is because this file +// was generated by a version of the `protoc` Swift plug-in that is +// incompatible with the version of SwiftProtobuf to which you are linking. +// Please ensure that you are building against the same version of the API +// that was used to generate this file. +fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { + struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} + typealias Version = _2 +} + +/// +/// This abstraction is used to contain any configuration for provisioning a node on any client. +/// It is useful for importing and exporting configurations. +struct DeviceProfile { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// + /// Long name for the node + var longName: String { + get {return _longName ?? String()} + set {_longName = newValue} + } + /// Returns true if `longName` has been explicitly set. + var hasLongName: Bool {return self._longName != nil} + /// Clears the value of `longName`. Subsequent reads from it will return its default value. + mutating func clearLongName() {self._longName = nil} + + /// + /// Short name of the node + var shortName: String { + get {return _shortName ?? String()} + set {_shortName = newValue} + } + /// Returns true if `shortName` has been explicitly set. + var hasShortName: Bool {return self._shortName != nil} + /// Clears the value of `shortName`. Subsequent reads from it will return its default value. + mutating func clearShortName() {self._shortName = nil} + + /// + /// The url of the channels from our node + var channelURL: String { + get {return _channelURL ?? String()} + set {_channelURL = newValue} + } + /// Returns true if `channelURL` has been explicitly set. + var hasChannelURL: Bool {return self._channelURL != nil} + /// Clears the value of `channelURL`. Subsequent reads from it will return its default value. + mutating func clearChannelURL() {self._channelURL = nil} + + /// + /// The Config of the node + var config: LocalConfig { + get {return _config ?? LocalConfig()} + set {_config = newValue} + } + /// Returns true if `config` has been explicitly set. + var hasConfig: Bool {return self._config != nil} + /// Clears the value of `config`. Subsequent reads from it will return its default value. + mutating func clearConfig() {self._config = nil} + + /// + /// The ModuleConfig of the node + var moduleConfig: LocalModuleConfig { + get {return _moduleConfig ?? LocalModuleConfig()} + set {_moduleConfig = newValue} + } + /// Returns true if `moduleConfig` has been explicitly set. + var hasModuleConfig: Bool {return self._moduleConfig != nil} + /// Clears the value of `moduleConfig`. Subsequent reads from it will return its default value. + mutating func clearModuleConfig() {self._moduleConfig = nil} + + var unknownFields = SwiftProtobuf.UnknownStorage() + + init() {} + + fileprivate var _longName: String? = nil + fileprivate var _shortName: String? = nil + fileprivate var _channelURL: String? = nil + fileprivate var _config: LocalConfig? = nil + fileprivate var _moduleConfig: LocalModuleConfig? = nil +} + +#if swift(>=5.5) && canImport(_Concurrency) +extension DeviceProfile: @unchecked Sendable {} +#endif // swift(>=5.5) && canImport(_Concurrency) + +// MARK: - Code below here is support for the SwiftProtobuf runtime. + +fileprivate let _protobuf_package = "meshtastic" + +extension DeviceProfile: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = _protobuf_package + ".DeviceProfile" + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "long_name"), + 2: .standard(proto: "short_name"), + 3: .standard(proto: "channel_url"), + 4: .same(proto: "config"), + 5: .standard(proto: "module_config"), + ] + + mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularStringField(value: &self._longName) }() + case 2: try { try decoder.decodeSingularStringField(value: &self._shortName) }() + case 3: try { try decoder.decodeSingularStringField(value: &self._channelURL) }() + case 4: try { try decoder.decodeSingularMessageField(value: &self._config) }() + case 5: try { try decoder.decodeSingularMessageField(value: &self._moduleConfig) }() + default: break + } + } + } + + func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._longName { + try visitor.visitSingularStringField(value: v, fieldNumber: 1) + } }() + try { if let v = self._shortName { + try visitor.visitSingularStringField(value: v, fieldNumber: 2) + } }() + try { if let v = self._channelURL { + try visitor.visitSingularStringField(value: v, fieldNumber: 3) + } }() + try { if let v = self._config { + try visitor.visitSingularMessageField(value: v, fieldNumber: 4) + } }() + try { if let v = self._moduleConfig { + try visitor.visitSingularMessageField(value: v, fieldNumber: 5) + } }() + try unknownFields.traverse(visitor: &visitor) + } + + static func ==(lhs: DeviceProfile, rhs: DeviceProfile) -> Bool { + if lhs._longName != rhs._longName {return false} + if lhs._shortName != rhs._shortName {return false} + if lhs._channelURL != rhs._channelURL {return false} + if lhs._config != rhs._config {return false} + if lhs._moduleConfig != rhs._moduleConfig {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} diff --git a/Meshtastic/Protobufs/meshtastic/deviceonly.pb.swift b/Meshtastic/Protobufs/meshtastic/deviceonly.pb.swift index d0c52075..25a7b5ca 100644 --- a/Meshtastic/Protobufs/meshtastic/deviceonly.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/deviceonly.pb.swift @@ -159,6 +159,19 @@ struct DeviceState { set {_uniqueStorage()._didGpsReset = newValue} } + /// + /// We keep the last received waypoint stored in the device flash, + /// so we can show it on the screen. + /// Might be null + var rxWaypoint: MeshPacket { + get {return _storage._rxWaypoint ?? MeshPacket()} + set {_uniqueStorage()._rxWaypoint = newValue} + } + /// Returns true if `rxWaypoint` has been explicitly set. + var hasRxWaypoint: Bool {return _storage._rxWaypoint != nil} + /// Clears the value of `rxWaypoint`. Subsequent reads from it will return its default value. + mutating func clearRxWaypoint() {_uniqueStorage()._rxWaypoint = nil} + var unknownFields = SwiftProtobuf.UnknownStorage() init() {} @@ -280,6 +293,7 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati 7: .standard(proto: "rx_text_message"), 9: .standard(proto: "no_save"), 11: .standard(proto: "did_gps_reset"), + 12: .standard(proto: "rx_waypoint"), ] fileprivate class _StorageClass { @@ -291,6 +305,7 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati var _rxTextMessage: MeshPacket? = nil var _noSave: Bool = false var _didGpsReset: Bool = false + var _rxWaypoint: MeshPacket? = nil static let defaultInstance = _StorageClass() @@ -305,6 +320,7 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati _rxTextMessage = source._rxTextMessage _noSave = source._noSave _didGpsReset = source._didGpsReset + _rxWaypoint = source._rxWaypoint } } @@ -331,6 +347,7 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati case 8: try { try decoder.decodeSingularUInt32Field(value: &_storage._version) }() case 9: try { try decoder.decodeSingularBoolField(value: &_storage._noSave) }() case 11: try { try decoder.decodeSingularBoolField(value: &_storage._didGpsReset) }() + case 12: try { try decoder.decodeSingularMessageField(value: &_storage._rxWaypoint) }() default: break } } @@ -367,6 +384,9 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati if _storage._didGpsReset != false { try visitor.visitSingularBoolField(value: _storage._didGpsReset, fieldNumber: 11) } + try { if let v = _storage._rxWaypoint { + try visitor.visitSingularMessageField(value: v, fieldNumber: 12) + } }() } try unknownFields.traverse(visitor: &visitor) } @@ -384,6 +404,7 @@ extension DeviceState: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementati if _storage._rxTextMessage != rhs_storage._rxTextMessage {return false} if _storage._noSave != rhs_storage._noSave {return false} if _storage._didGpsReset != rhs_storage._didGpsReset {return false} + if _storage._rxWaypoint != rhs_storage._rxWaypoint {return false} return true } if !storagesAreEqual {return false} diff --git a/Meshtastic/Protobufs/meshtastic/mesh.pb.swift b/Meshtastic/Protobufs/meshtastic/mesh.pb.swift index 03b6c4d1..1e054777 100644 --- a/Meshtastic/Protobufs/meshtastic/mesh.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/mesh.pb.swift @@ -2231,6 +2231,50 @@ struct Compressed { init() {} } +/// +/// Full info on edges for a single node +struct NeighborInfo { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// + /// The node ID of the node sending info on its neighbors + var nodeID: UInt32 = 0 + + /// + /// Field to pass neighbor info for the next sending cycle + var lastSentByID: UInt32 = 0 + + /// + /// The list of out edges from this node + var neighbors: [Neighbor] = [] + + var unknownFields = SwiftProtobuf.UnknownStorage() + + init() {} +} + +/// +/// A single edge in the mesh +struct Neighbor { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// + /// Node ID of neighbor + var nodeID: UInt32 = 0 + + /// + /// SNR of last heard message + var snr: Float = 0 + + var unknownFields = SwiftProtobuf.UnknownStorage() + + init() {} +} + /// /// Device metadata response struct DeviceMetadata { @@ -2307,6 +2351,8 @@ extension FromRadio.OneOf_PayloadVariant: @unchecked Sendable {} extension ToRadio: @unchecked Sendable {} extension ToRadio.OneOf_PayloadVariant: @unchecked Sendable {} extension Compressed: @unchecked Sendable {} +extension NeighborInfo: @unchecked Sendable {} +extension Neighbor: @unchecked Sendable {} extension DeviceMetadata: @unchecked Sendable {} #endif // swift(>=5.5) && canImport(_Concurrency) @@ -3896,6 +3942,88 @@ extension Compressed: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio } } +extension NeighborInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = _protobuf_package + ".NeighborInfo" + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "node_id"), + 2: .standard(proto: "last_sent_by_id"), + 3: .same(proto: "neighbors"), + ] + + mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularUInt32Field(value: &self.nodeID) }() + case 2: try { try decoder.decodeSingularUInt32Field(value: &self.lastSentByID) }() + case 3: try { try decoder.decodeRepeatedMessageField(value: &self.neighbors) }() + default: break + } + } + } + + func traverse(visitor: inout V) throws { + if self.nodeID != 0 { + try visitor.visitSingularUInt32Field(value: self.nodeID, fieldNumber: 1) + } + if self.lastSentByID != 0 { + try visitor.visitSingularUInt32Field(value: self.lastSentByID, fieldNumber: 2) + } + if !self.neighbors.isEmpty { + try visitor.visitRepeatedMessageField(value: self.neighbors, fieldNumber: 3) + } + try unknownFields.traverse(visitor: &visitor) + } + + static func ==(lhs: NeighborInfo, rhs: NeighborInfo) -> Bool { + if lhs.nodeID != rhs.nodeID {return false} + if lhs.lastSentByID != rhs.lastSentByID {return false} + if lhs.neighbors != rhs.neighbors {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Neighbor: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = _protobuf_package + ".Neighbor" + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .standard(proto: "node_id"), + 2: .same(proto: "snr"), + ] + + mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularUInt32Field(value: &self.nodeID) }() + case 2: try { try decoder.decodeSingularFloatField(value: &self.snr) }() + default: break + } + } + } + + func traverse(visitor: inout V) throws { + if self.nodeID != 0 { + try visitor.visitSingularUInt32Field(value: self.nodeID, fieldNumber: 1) + } + if self.snr != 0 { + try visitor.visitSingularFloatField(value: self.snr, fieldNumber: 2) + } + try unknownFields.traverse(visitor: &visitor) + } + + static func ==(lhs: Neighbor, rhs: Neighbor) -> Bool { + if lhs.nodeID != rhs.nodeID {return false} + if lhs.snr != rhs.snr {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + extension DeviceMetadata: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = _protobuf_package + ".DeviceMetadata" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ diff --git a/Meshtastic/Protobufs/meshtastic/module_config.pb.swift b/Meshtastic/Protobufs/meshtastic/module_config.pb.swift index 5c5f151c..87c90e8b 100644 --- a/Meshtastic/Protobufs/meshtastic/module_config.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/module_config.pb.swift @@ -246,6 +246,11 @@ struct ModuleConfig { /// If true, we attempt to establish a secure connection using TLS var tlsEnabled: Bool = false + /// + /// The root topic to use for MQTT messages. Default is "msh". + /// This is useful if you want to use a single MQTT server for multiple meshtastic networks and separate them via ACLs + var root: String = String() + var unknownFields = SwiftProtobuf.UnknownStorage() init() {} @@ -1114,6 +1119,7 @@ extension ModuleConfig.MQTTConfig: SwiftProtobuf.Message, SwiftProtobuf._Message 5: .standard(proto: "encryption_enabled"), 6: .standard(proto: "json_enabled"), 7: .standard(proto: "tls_enabled"), + 8: .same(proto: "root"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -1129,6 +1135,7 @@ extension ModuleConfig.MQTTConfig: SwiftProtobuf.Message, SwiftProtobuf._Message case 5: try { try decoder.decodeSingularBoolField(value: &self.encryptionEnabled) }() case 6: try { try decoder.decodeSingularBoolField(value: &self.jsonEnabled) }() case 7: try { try decoder.decodeSingularBoolField(value: &self.tlsEnabled) }() + case 8: try { try decoder.decodeSingularStringField(value: &self.root) }() default: break } } @@ -1156,6 +1163,9 @@ extension ModuleConfig.MQTTConfig: SwiftProtobuf.Message, SwiftProtobuf._Message if self.tlsEnabled != false { try visitor.visitSingularBoolField(value: self.tlsEnabled, fieldNumber: 7) } + if !self.root.isEmpty { + try visitor.visitSingularStringField(value: self.root, fieldNumber: 8) + } try unknownFields.traverse(visitor: &visitor) } @@ -1167,6 +1177,7 @@ extension ModuleConfig.MQTTConfig: SwiftProtobuf.Message, SwiftProtobuf._Message if lhs.encryptionEnabled != rhs.encryptionEnabled {return false} if lhs.jsonEnabled != rhs.jsonEnabled {return false} if lhs.tlsEnabled != rhs.tlsEnabled {return false} + if lhs.root != rhs.root {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } diff --git a/Meshtastic/Protobufs/meshtastic/portnums.pb.swift b/Meshtastic/Protobufs/meshtastic/portnums.pb.swift index 9b7198a5..d52e3777 100644 --- a/Meshtastic/Protobufs/meshtastic/portnums.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/portnums.pb.swift @@ -135,6 +135,10 @@ enum PortNum: SwiftProtobuf.Enum { /// a certain destination would take on the mesh. case tracerouteApp // = 70 + /// + /// Aggregates edge info for the network by sending out a list of each node's neighbors + case neighborinfoApp // = 71 + /// /// Private applications should use portnums >= 256. /// To simplify initial development and testing you can use "PRIVATE_APP" @@ -175,6 +179,7 @@ enum PortNum: SwiftProtobuf.Enum { case 68: self = .zpsApp case 69: self = .simulatorApp case 70: self = .tracerouteApp + case 71: self = .neighborinfoApp case 256: self = .privateApp case 257: self = .atakForwarder case 511: self = .max @@ -203,6 +208,7 @@ enum PortNum: SwiftProtobuf.Enum { case .zpsApp: return 68 case .simulatorApp: return 69 case .tracerouteApp: return 70 + case .neighborinfoApp: return 71 case .privateApp: return 256 case .atakForwarder: return 257 case .max: return 511 @@ -236,6 +242,7 @@ extension PortNum: CaseIterable { .zpsApp, .simulatorApp, .tracerouteApp, + .neighborinfoApp, .privateApp, .atakForwarder, .max, @@ -271,6 +278,7 @@ extension PortNum: SwiftProtobuf._ProtoNameProviding { 68: .same(proto: "ZPS_APP"), 69: .same(proto: "SIMULATOR_APP"), 70: .same(proto: "TRACEROUTE_APP"), + 71: .same(proto: "NEIGHBORINFO_APP"), 256: .same(proto: "PRIVATE_APP"), 257: .same(proto: "ATAK_FORWARDER"), 511: .same(proto: "MAX"), diff --git a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift index 4d62b487..5c51bd90 100644 --- a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift +++ b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift @@ -315,7 +315,7 @@ struct MapViewSwiftUI: UIViewRepresentable { subtitle.numberOfLines = 0 annotationView.detailCalloutAccessoryView = subtitle let detailsIcon = UIButton(type: .detailDisclosure) - detailsIcon.setImage(UIImage(systemName: "info.square"), for: .normal) + detailsIcon.setImage(UIImage(systemName: "trash"), for: .normal) annotationView.rightCalloutAccessoryView = detailsIcon return annotationView case let waypointAnnotation as WaypointEntity: @@ -365,9 +365,18 @@ struct MapViewSwiftUI: UIViewRepresentable { } func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) { - // Only Allow Edit for waypoint annotations with a id - if view.tag > 0 { - parent.onWaypointEdit(view.tag) + + switch view.annotation { + case let positionAnnotation as PositionEntity: + print(positionAnnotation) + case let waypointAnnotation as WaypointEntity: + // Only Allow Edit for waypoint annotations with a id + if view.tag > 0 { + parent.onWaypointEdit(view.tag) + } + print(waypointAnnotation) + + default: break } } diff --git a/Meshtastic/Views/Settings/Config/DeviceConfig.swift b/Meshtastic/Views/Settings/Config/DeviceConfig.swift index 79398ec3..58fc6150 100644 --- a/Meshtastic/Views/Settings/Config/DeviceConfig.swift +++ b/Meshtastic/Views/Settings/Config/DeviceConfig.swift @@ -309,7 +309,7 @@ struct DeviceConfig: View { self.buttonGPIO = Int(node?.deviceConfig?.buttonGpio ?? 0) self.buzzerGPIO = Int(node?.deviceConfig?.buzzerGpio ?? 0) self.rebroadcastMode = Int(node?.deviceConfig?.rebroadcastMode ?? 0) - self.doubleTapAsButtonPress = node!.deviceConfig?.doubleTapAsButtonPress ?? false + self.doubleTapAsButtonPress = node?.deviceConfig?.doubleTapAsButtonPress ?? false self.hasChanges = false } } diff --git a/de.lproj/Localizable.strings b/de.lproj/Localizable.strings index 851303bc..41c5c20b 100644 --- a/de.lproj/Localizable.strings +++ b/de.lproj/Localizable.strings @@ -113,6 +113,7 @@ "interval.twenty.seconds"="Zwanzig Sekunden"; "interval.twentyfive.seconds"="Fünfundzwanzig Sekunden"; "interval.thirty.seconds"="Dreißig Sekunden"; +"interval.fortyfive.seconds"="Forty Five Seconds"; "interval.one.minute"="Eine Minute"; "interval.two.minutes"="Zwei Minutes"; "interval.five.minutes"="Fünf Minutes"; diff --git a/en.lproj/Localizable.strings b/en.lproj/Localizable.strings index 53253d8f..0f356c64 100644 --- a/en.lproj/Localizable.strings +++ b/en.lproj/Localizable.strings @@ -113,6 +113,7 @@ "interval.twenty.seconds"="Twenty Seconds"; "interval.twentyfive.seconds"="Twenty Five Seconds"; "interval.thirty.seconds"="Thirty Seconds"; +"interval.fortyfive.seconds"="Forty Five Seconds"; "interval.one.minute"="One Minute"; "interval.two.minutes"="Two Minutes"; "interval.five.minutes"="Five Minutes"; diff --git a/zh-Hans.lproj/Localizable.strings b/zh-Hans.lproj/Localizable.strings index 1fad751b..00355830 100644 --- a/zh-Hans.lproj/Localizable.strings +++ b/zh-Hans.lproj/Localizable.strings @@ -113,6 +113,7 @@ "interval.twenty.seconds"="二十秒"; "interval.twentyfive.seconds"="二十五秒"; "interval.thirty.seconds"="三十秒"; +"interval.fortyfive.seconds"="Forty Five Seconds"; "interval.one.minute"="一分钟"; "interval.two.minutes"="两分钟"; "interval.five.minutes"="五分钟";