mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Merge pull request #106 from meshtastic/feature/user_settings
More settings updates
This commit is contained in:
commit
a94cc8f11e
14 changed files with 254 additions and 74 deletions
|
|
@ -847,7 +847,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.3.21;
|
||||
MARKETING_VERSION = 1.3.22;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
|
|
@ -879,7 +879,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.3.21;
|
||||
MARKETING_VERSION = 1.3.22;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
|
|
|
|||
|
|
@ -55,13 +55,13 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
let FROMRADIO_UUID = CBUUID(string: "0x8BA2BCC2-EE02-4A55-A531-C525C5E454D5")
|
||||
let FROMNUM_UUID = CBUUID(string: "0xED9DA18C-A800-4F66-A670-AA7547E34453")
|
||||
|
||||
private var meshLoggingEnabled: Bool = false
|
||||
private var meshLoggingEnabled: Bool = true
|
||||
let meshLog = documentsFolder.appendingPathComponent("meshlog.txt")
|
||||
|
||||
// MARK: init BLEManager
|
||||
override init() {
|
||||
|
||||
self.meshLoggingEnabled = UserDefaults.standard.object(forKey: "meshActivityLog") as? Bool ?? false
|
||||
//self.meshLoggingEnabled = UserDefaults.standard.object(forKey: "meshActivityLog") as? Bool ?? false
|
||||
self.lastConnectionError = ""
|
||||
self.lastConnnectionVersion = "0.0.0"
|
||||
super.init()
|
||||
|
|
@ -443,10 +443,17 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
|
||||
localConfig(config: decodedInfo.config, meshlogging: meshLoggingEnabled, context: context!, nodeNum: self.connectedPeripheral.num, nodeLongName: self.connectedPeripheral.longName)
|
||||
|
||||
} else if decodedInfo.moduleConfig.isInitialized {
|
||||
|
||||
moduleConfig(config: decodedInfo.moduleConfig, meshlogging: meshLoggingEnabled, context: context!, nodeNum: self.connectedPeripheral.num, nodeLongName: self.connectedPeripheral.longName)
|
||||
|
||||
} else {
|
||||
|
||||
if decodedInfo.configCompleteID == 0 {
|
||||
|
||||
if meshLoggingEnabled { MeshLogger.log("ℹ️ MESH PACKET received for App UNHANDLED \(try! decodedInfo.packet.jsonString())") }
|
||||
} else {
|
||||
|
||||
if meshLoggingEnabled { MeshLogger.log("ℹ️ MESH PACKET received for Unknown App UNHANDLED \(try! decodedInfo.packet.jsonString())") }
|
||||
}
|
||||
}
|
||||
|
|
@ -1038,10 +1045,12 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
|
|||
return false
|
||||
}
|
||||
|
||||
public func getModuleConfig (configType: AdminMessage.ModuleConfigType, destNum: Int64, wantResponse: Bool) -> Bool {
|
||||
public func getChannelSet (destNum: Int64, wantResponse: Bool) -> Bool {
|
||||
|
||||
var adminPacket = AdminMessage()
|
||||
adminPacket.getModuleConfigRequest = configType
|
||||
adminPacket.getChannelRequest = 1
|
||||
|
||||
|
||||
|
||||
var meshPacket: MeshPacket = MeshPacket()
|
||||
meshPacket.to = UInt32(connectedPeripheral.num)
|
||||
|
|
|
|||
|
|
@ -328,9 +328,81 @@ func localConfig (config: Config, meshlogging: Bool, context:NSManagedObjectCont
|
|||
}
|
||||
}
|
||||
|
||||
func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObject, nodeNum: Int64, nodeLongName: String) {
|
||||
|
||||
func moduleConfig (config: ModuleConfig, meshlogging: Bool, context:NSManagedObjectContext, nodeNum: Int64, nodeLongName: String) {
|
||||
|
||||
// We don't care about any of the WiFi related MQTT settings
|
||||
if config.payloadVariant == ModuleConfig.OneOf_PayloadVariant.rangeTest(config.rangeTest) {
|
||||
|
||||
var isDefault = false
|
||||
|
||||
if (try! config.rangeTest.jsonString()) == "{}" {
|
||||
|
||||
isDefault = true
|
||||
print("⛰️ Default Range Test Module config")
|
||||
}
|
||||
|
||||
let fetchNodeInfoRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "NodeInfoEntity")
|
||||
fetchNodeInfoRequest.predicate = NSPredicate(format: "num == %lld", Int64(nodeNum))
|
||||
|
||||
do {
|
||||
|
||||
let fetchedNode = try context.fetch(fetchNodeInfoRequest) as! [NodeInfoEntity]
|
||||
// Found a node, save Device Config
|
||||
if !fetchedNode.isEmpty {
|
||||
|
||||
if fetchedNode[0].rangeTestConfig == nil {
|
||||
|
||||
let newRangeTestConfig = RangeTestConfigEntity(context: context)
|
||||
|
||||
if isDefault {
|
||||
|
||||
newRangeTestConfig.sender = 0
|
||||
newRangeTestConfig.enabled = false
|
||||
newRangeTestConfig.save = false
|
||||
|
||||
} else {
|
||||
|
||||
newRangeTestConfig.sender = Int32(config.rangeTest.sender)
|
||||
newRangeTestConfig.enabled = !config.rangeTest.enabled
|
||||
newRangeTestConfig.save = config.rangeTest.save
|
||||
}
|
||||
|
||||
fetchedNode[0].rangeTestConfig = newRangeTestConfig
|
||||
|
||||
} else {
|
||||
|
||||
if isDefault {
|
||||
|
||||
fetchedNode[0].rangeTestConfig?.sender = 0
|
||||
fetchedNode[0].rangeTestConfig?.enabled = false
|
||||
fetchedNode[0].rangeTestConfig?.save = false
|
||||
|
||||
} else {
|
||||
// Client default protobuf value of 0
|
||||
fetchedNode[0].rangeTestConfig?.sender = Int32(config.rangeTest.sender)
|
||||
fetchedNode[0].rangeTestConfig?.enabled = !config.rangeTest.enabled
|
||||
fetchedNode[0].rangeTestConfig?.save = config.rangeTest.save
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
|
||||
try context.save()
|
||||
if meshlogging { MeshLogger.log("💾 Updated Range Test Config for node number: \(String(nodeNum))") }
|
||||
|
||||
} catch {
|
||||
|
||||
context.rollback()
|
||||
|
||||
let nsError = error as NSError
|
||||
print("💥 Error Updating Core Data RangeTestConfigEntity: \(nsError)")
|
||||
}
|
||||
}
|
||||
|
||||
} catch {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func myInfoPacket (myInfo: MyNodeInfo, meshLogging: Bool, context: NSManagedObjectContext) -> MyInfoEntity? {
|
||||
|
|
@ -782,16 +854,20 @@ func routingPacket (packet: MeshPacket, meshLogging: Bool, context: NSManagedObj
|
|||
|
||||
do {
|
||||
|
||||
let fetchedMessage = try context.fetch(fetchMessageRequest)[0] as? MessageEntity
|
||||
let fetchedMessage = try context.fetch(fetchMessageRequest) as? [MessageEntity]
|
||||
|
||||
if fetchedMessage != nil {
|
||||
if fetchedMessage?.count ?? 0 > 0 {
|
||||
|
||||
fetchedMessage!.receivedACK = true
|
||||
fetchedMessage!.ackSNR = packet.rxSnr
|
||||
fetchedMessage!.ackTimestamp = Int32(packet.rxTime)
|
||||
fetchedMessage!.objectWillChange.send()
|
||||
fetchedMessage!.fromUser?.objectWillChange.send()
|
||||
fetchedMessage!.toUser?.objectWillChange.send()
|
||||
fetchedMessage![0].receivedACK = true
|
||||
fetchedMessage![0].ackSNR = packet.rxSnr
|
||||
fetchedMessage![0].ackTimestamp = Int32(packet.rxTime)
|
||||
fetchedMessage![0].objectWillChange.send()
|
||||
fetchedMessage![0].fromUser?.objectWillChange.send()
|
||||
fetchedMessage![0].toUser?.objectWillChange.send()
|
||||
|
||||
} else {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
try context.save()
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<dict>
|
||||
<key>com.apple.developer.associated-domains</key>
|
||||
<array>
|
||||
<string>applinks:meshtastic.org/*</string>
|
||||
<string>applinks:meshtastic.org/e/*</string>
|
||||
</array>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
|
|
|
|||
|
|
@ -20,21 +20,17 @@ struct MeshtasticAppleApp: App {
|
|||
.environmentObject(bleManager)
|
||||
.environmentObject(userSettings)
|
||||
.onContinueUserActivity(NSUserActivityTypeBrowsingWeb) { userActivity in
|
||||
|
||||
print("Continue activity \(userActivity)")
|
||||
|
||||
print("QR Code URL received from the Camera \(userActivity)")
|
||||
guard let url = userActivity.webpageURL else {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
print("User wants to open URL: \(url)")
|
||||
// TODO same handling as done in onOpenURL()
|
||||
|
||||
}
|
||||
.onOpenURL(perform: { (url) in
|
||||
|
||||
print("URL OPENED")
|
||||
print(url)
|
||||
|
||||
//we are expecting a .mbtiles map file that contains raster data
|
||||
//save it to the documents directory, and name it offline_map.mbtiles
|
||||
let fileManager = FileManager.default
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ class UserSettings: ObservableObject {
|
|||
self.provideLocation = UserDefaults.standard.object(forKey: "provideLocation") as? Bool ?? false
|
||||
self.provideLocationInterval = UserDefaults.standard.object(forKey: "provideLocationInterval") as? Int ?? 900
|
||||
self.keyboardType = UserDefaults.standard.object(forKey: "keyboardType") as? Int ?? 0
|
||||
self.meshActivityLog = UserDefaults.standard.object(forKey: "meshActivityLog") as? Bool ?? false
|
||||
self.meshActivityLog = UserDefaults.standard.object(forKey: "meshActivityLog") as? Bool ?? true
|
||||
self.meshMapType = UserDefaults.standard.string(forKey: "meshMapType") ?? "hybrid"
|
||||
self.meshMapCustomTileServer = UserDefaults.standard.string(forKey: "meshMapCustomTileServer") ?? ""
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1855,6 +1855,16 @@ struct FromRadio {
|
|||
set {_uniqueStorage()._payloadVariant = .rebooted(newValue)}
|
||||
}
|
||||
|
||||
///
|
||||
/// Include module config
|
||||
var moduleConfig: ModuleConfig {
|
||||
get {
|
||||
if case .moduleConfig(let v)? = _storage._payloadVariant {return v}
|
||||
return ModuleConfig()
|
||||
}
|
||||
set {_uniqueStorage()._payloadVariant = .moduleConfig(newValue)}
|
||||
}
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
///
|
||||
|
|
@ -1889,6 +1899,9 @@ struct FromRadio {
|
|||
/// Not used on all transports, currently just used for the serial console.
|
||||
/// NOTE: This ID must not change - to keep (minimal) compatibility with <1.2 version of android apps.
|
||||
case rebooted(Bool)
|
||||
///
|
||||
/// Include module config
|
||||
case moduleConfig(ModuleConfig)
|
||||
|
||||
#if !swift(>=4.1)
|
||||
static func ==(lhs: FromRadio.OneOf_PayloadVariant, rhs: FromRadio.OneOf_PayloadVariant) -> Bool {
|
||||
|
|
@ -1924,6 +1937,10 @@ struct FromRadio {
|
|||
guard case .rebooted(let l) = lhs, case .rebooted(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.moduleConfig, .moduleConfig): return {
|
||||
guard case .moduleConfig(let l) = lhs, case .moduleConfig(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
|
|
@ -3297,6 +3314,7 @@ extension FromRadio: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementation
|
|||
7: .standard(proto: "log_record"),
|
||||
8: .standard(proto: "config_complete_id"),
|
||||
9: .same(proto: "rebooted"),
|
||||
10: .same(proto: "moduleConfig"),
|
||||
]
|
||||
|
||||
fileprivate class _StorageClass {
|
||||
|
|
@ -3397,6 +3415,19 @@ extension FromRadio: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementation
|
|||
_storage._payloadVariant = .rebooted(v)
|
||||
}
|
||||
}()
|
||||
case 10: try {
|
||||
var v: ModuleConfig?
|
||||
var hadOneofValue = false
|
||||
if let current = _storage._payloadVariant {
|
||||
hadOneofValue = true
|
||||
if case .moduleConfig(let m) = current {v = m}
|
||||
}
|
||||
try decoder.decodeSingularMessageField(value: &v)
|
||||
if let v = v {
|
||||
if hadOneofValue {try decoder.handleConflictingOneOf()}
|
||||
_storage._payloadVariant = .moduleConfig(v)
|
||||
}
|
||||
}()
|
||||
case 11: try {
|
||||
var v: MeshPacket?
|
||||
var hadOneofValue = false
|
||||
|
|
@ -3450,6 +3481,10 @@ extension FromRadio: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementation
|
|||
guard case .rebooted(let v)? = _storage._payloadVariant else { preconditionFailure() }
|
||||
try visitor.visitSingularBoolField(value: v, fieldNumber: 9)
|
||||
}()
|
||||
case .moduleConfig?: try {
|
||||
guard case .moduleConfig(let v)? = _storage._payloadVariant else { preconditionFailure() }
|
||||
try visitor.visitSingularMessageField(value: v, fieldNumber: 10)
|
||||
}()
|
||||
case .packet?: try {
|
||||
guard case .packet(let v)? = _storage._payloadVariant else { preconditionFailure() }
|
||||
try visitor.visitSingularMessageField(value: v, fieldNumber: 11)
|
||||
|
|
|
|||
|
|
@ -27,13 +27,6 @@ struct ContentView: View {
|
|||
|
||||
}
|
||||
.tag(Tab.contacts)
|
||||
// Channels()
|
||||
// .tabItem {
|
||||
// Label("Messages", systemImage: "text.bubble")
|
||||
// .symbolRenderingMode(.hierarchical)
|
||||
// .symbolVariant(.none)
|
||||
// }
|
||||
// .tag(Tab.messages)
|
||||
Connect()
|
||||
.tabItem {
|
||||
Label("Bluetooth", systemImage: "antenna.radiowaves.left.and.right")
|
||||
|
|
|
|||
|
|
@ -157,16 +157,9 @@ struct AppSettings: View {
|
|||
|
||||
}
|
||||
Section(header: Text("DEBUG")) {
|
||||
Toggle(isOn: $userSettings.meshActivityLog) {
|
||||
|
||||
Label("Log all Mesh activity", systemImage: "network")
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
if userSettings.meshActivityLog {
|
||||
NavigationLink(destination: MeshLog()) {
|
||||
Text("View Mesh Log")
|
||||
}
|
||||
.listRowSeparator(.visible)
|
||||
NavigationLink(destination: MeshLog()) {
|
||||
Text("View Mesh Log")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ enum GpsFormats: Int, CaseIterable, Identifiable {
|
|||
}
|
||||
|
||||
// Default of 0 is One Minute
|
||||
enum ScreenOnSeconds: Int, CaseIterable, Identifiable {
|
||||
enum ScreenOnIntervals: Int, CaseIterable, Identifiable {
|
||||
|
||||
case fifteenSeconds = 15
|
||||
case thirtySeconds = 30
|
||||
|
|
@ -90,7 +90,7 @@ enum ScreenOnSeconds: Int, CaseIterable, Identifiable {
|
|||
}
|
||||
|
||||
// Default of 0 is off
|
||||
enum ScreenCarouselSeconds: Int, CaseIterable, Identifiable {
|
||||
enum ScreenCarouselIntervals: Int, CaseIterable, Identifiable {
|
||||
|
||||
case off = 0
|
||||
case fifteenSeconds = 15
|
||||
|
|
@ -145,8 +145,8 @@ struct DisplayConfig: View {
|
|||
Section(header: Text("Timing")) {
|
||||
|
||||
Picker("Screen on for", selection: $screenOnSeconds ) {
|
||||
ForEach(ScreenOnSeconds.allCases) { sos in
|
||||
Text(sos.description)
|
||||
ForEach(ScreenOnIntervals.allCases) { soi in
|
||||
Text(soi.description)
|
||||
}
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
|
|
@ -155,8 +155,8 @@ struct DisplayConfig: View {
|
|||
.font(.caption)
|
||||
|
||||
Picker("Carousel Interval", selection: $screenCarouselInterval ) {
|
||||
ForEach(ScreenCarouselSeconds.allCases) { scs in
|
||||
Text(scs.description)
|
||||
ForEach(ScreenCarouselIntervals.allCases) { sci in
|
||||
Text(sci.description)
|
||||
}
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
|
|
|
|||
|
|
@ -6,6 +6,46 @@
|
|||
//
|
||||
import SwiftUI
|
||||
|
||||
enum OutputIntervals: Int, CaseIterable, Identifiable {
|
||||
|
||||
case oneSecond = 0
|
||||
case twoSeconds = 2000
|
||||
case threeSeconds = 3000
|
||||
case fourSeconds = 4000
|
||||
case fiveSeconds = 5000
|
||||
case tenSeconds = 10000
|
||||
case fifteenSeconds = 15000
|
||||
case thirtySeconds = 30000
|
||||
case oneMinute = 60000
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
|
||||
case .oneSecond:
|
||||
return "One Second"
|
||||
case .twoSeconds:
|
||||
return "Two Seconds"
|
||||
case .threeSeconds:
|
||||
return "Three Seconds"
|
||||
case .fourSeconds:
|
||||
return "Four Seconds"
|
||||
case .fiveSeconds:
|
||||
return "Five Seconds"
|
||||
case .tenSeconds:
|
||||
return "Ten Seconds"
|
||||
case .fifteenSeconds:
|
||||
return "Fifteen Seconds"
|
||||
case .thirtySeconds:
|
||||
return "Thirty Seconds"
|
||||
case .oneMinute:
|
||||
return "One Minute"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ExternalNotificationConfig: View {
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
|
|
@ -75,6 +115,16 @@ struct ExternalNotificationConfig: View {
|
|||
Text("Specifies the GPIO that your external circuit is attached to on the device.")
|
||||
.font(.caption)
|
||||
.listRowSeparator(.visible)
|
||||
|
||||
Picker("GPIO Output Duration", selection: $outputMilliseconds ) {
|
||||
ForEach(OutputIntervals.allCases) { oi in
|
||||
Text(oi.description)
|
||||
}
|
||||
}
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
Text("Specifies how long the monitored GPIO should output.")
|
||||
.font(.caption)
|
||||
.listRowSeparator(.visible)
|
||||
}
|
||||
}
|
||||
.navigationTitle("External Notification Config")
|
||||
|
|
|
|||
|
|
@ -6,6 +6,37 @@
|
|||
//
|
||||
import SwiftUI
|
||||
|
||||
// Default of 0 is off
|
||||
enum SenderIntervals: Int, CaseIterable, Identifiable {
|
||||
|
||||
case off = 0
|
||||
case thirtySeconds = 30
|
||||
case oneMinute = 60
|
||||
case fiveMinutes = 300
|
||||
case tenMinutes = 600
|
||||
case fifteenMinutes = 900
|
||||
|
||||
var id: Int { self.rawValue }
|
||||
var description: String {
|
||||
get {
|
||||
switch self {
|
||||
case .off:
|
||||
return "Off"
|
||||
case .thirtySeconds:
|
||||
return "Thirty Seconds"
|
||||
case .oneMinute:
|
||||
return "One Minute"
|
||||
case .fiveMinutes:
|
||||
return "Five Minutes"
|
||||
case .tenMinutes:
|
||||
return "Ten Minutes"
|
||||
case .fifteenMinutes:
|
||||
return "Fifteen Minutes"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct RangeTestConfig: View {
|
||||
|
||||
@Environment(\.managedObjectContext) var context
|
||||
|
|
@ -18,7 +49,7 @@ struct RangeTestConfig: View {
|
|||
@State var hasChanges = false
|
||||
|
||||
@State var enabled = false
|
||||
@State var sender = false
|
||||
@State var sender = 0
|
||||
@State var save = false
|
||||
|
||||
var body: some View {
|
||||
|
|
@ -35,12 +66,13 @@ struct RangeTestConfig: View {
|
|||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
|
||||
Toggle(isOn: $sender) {
|
||||
|
||||
Label("Sender", systemImage: "paperplane")
|
||||
Picker("Sender Interval", selection: $sender ) {
|
||||
ForEach(SenderIntervals.allCases) { sci in
|
||||
Text(sci.description)
|
||||
}
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
Text("This device will send out range test messages.")
|
||||
.pickerStyle(DefaultPickerStyle())
|
||||
Text("This device will send out range test messages on the selected interval.")
|
||||
.font(.caption)
|
||||
|
||||
Toggle(isOn: $save) {
|
||||
|
|
@ -52,8 +84,8 @@ struct RangeTestConfig: View {
|
|||
Text("Saves a CSV with the range test message details, only available on ESP32 devices with a web server.")
|
||||
.font(.caption)
|
||||
}
|
||||
|
||||
}
|
||||
.disabled(!(node.myInfo?.hasWifi ?? false))
|
||||
|
||||
Button {
|
||||
|
||||
|
|
@ -63,7 +95,7 @@ struct RangeTestConfig: View {
|
|||
|
||||
Label("Save", systemImage: "square.and.arrow.down")
|
||||
}
|
||||
.disabled(bleManager.connectedPeripheral == nil || !hasChanges)
|
||||
.disabled(bleManager.connectedPeripheral == nil || !hasChanges || !(node.myInfo?.hasWifi ?? false))
|
||||
.buttonStyle(.bordered)
|
||||
.buttonBorderShape(.capsule)
|
||||
.controlSize(.large)
|
||||
|
|
@ -78,7 +110,7 @@ struct RangeTestConfig: View {
|
|||
var rtc = ModuleConfig.RangeTestConfig()
|
||||
rtc.enabled = enabled
|
||||
rtc.save = save
|
||||
rtc.sender = sender ? 1 : 0
|
||||
rtc.sender = UInt32(sender)
|
||||
|
||||
if bleManager.saveRangeTestModuleConfig(config: rtc, destNum: bleManager.connectedPeripheral.num, wantResponse: false) {
|
||||
|
||||
|
|
@ -104,34 +136,26 @@ struct RangeTestConfig: View {
|
|||
if self.initialLoad{
|
||||
|
||||
self.bleManager.context = context
|
||||
// self.enabled = node.rangeTestConfig?.enabled ?? false
|
||||
// self.save = node.rangeTestConfig?.save ?? false
|
||||
//
|
||||
// if node.rangeTestConfig?.sender != nil {
|
||||
//
|
||||
// self.sender = node.rangeTestConfig!.sender == 1 ? true : false
|
||||
//
|
||||
// } else {
|
||||
// self.sender = false
|
||||
// }
|
||||
// self.sender = node.rangeTestConfig?.sender != nil
|
||||
self.enabled = node.rangeTestConfig?.enabled ?? false
|
||||
self.save = node.rangeTestConfig?.save ?? false
|
||||
self.sender = Int(node.rangeTestConfig?.sender ?? 0)
|
||||
self.hasChanges = false
|
||||
self.initialLoad = false
|
||||
}
|
||||
}
|
||||
.onChange(of: enabled) { newEnabled in
|
||||
|
||||
//if newEnabled != node.rangeTestConfig!.enabled {
|
||||
if newEnabled != node.rangeTestConfig!.enabled {
|
||||
|
||||
hasChanges = true
|
||||
//}
|
||||
}
|
||||
}
|
||||
.onChange(of: save) { newSave in
|
||||
|
||||
//if newSave != node.rangeTestConfig!.save {
|
||||
if newSave != node.rangeTestConfig!.save {
|
||||
|
||||
hasChanges = true
|
||||
//}
|
||||
}
|
||||
}
|
||||
.onChange(of: sender) { newSender in
|
||||
|
||||
|
|
|
|||
|
|
@ -43,12 +43,13 @@ struct Settings: View {
|
|||
Section("Radio Configuration") {
|
||||
|
||||
NavigationLink {
|
||||
ShareChannel()
|
||||
ShareChannel(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity())
|
||||
} label: {
|
||||
Image(systemName: "qrcode")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text("Share Channel QR Code")
|
||||
}
|
||||
.disabled(bleManager.connectedPeripheral == nil)
|
||||
|
||||
Text("Radio config views will be be enabled when there is a connected node. Save buttons will be enabled when there are config changes to save.")
|
||||
.font(.caption)
|
||||
|
|
@ -144,8 +145,9 @@ struct Settings: View {
|
|||
|
||||
Text("Range Test (ESP32 Only)")
|
||||
}
|
||||
.disabled(!(bleManager.connectedPeripheral == nil))
|
||||
//nodes.first(where: { $0.num == connectedNodeNum })?.myInfo?.hasWifi ?? true) ||
|
||||
.disabled(true)
|
||||
//.disabled(bleManager.connectedPeripheral == nil)
|
||||
//nodes.first(where: { $0.num == connectedNodeNum })?.myInfo?.hasWifi ?? true)//||
|
||||
// nodes.first(where: { $0.num == connectedNodeNum })!.rangeTestConfig != nil)
|
||||
|
||||
NavigationLink {
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ struct ShareChannel: View {
|
|||
@EnvironmentObject var bleManager: BLEManager
|
||||
@EnvironmentObject var userSettings: UserSettings
|
||||
|
||||
var node: NodeInfoEntity
|
||||
|
||||
@State private var text = "https://meshtastic.org/e/#test"
|
||||
var qrCodeImage = QrCodeImage()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue