diff --git a/Meshtastic Apple.xcodeproj/project.pbxproj b/Meshtastic Apple.xcodeproj/project.pbxproj index 13e26e66..6c012ab5 100644 --- a/Meshtastic Apple.xcodeproj/project.pbxproj +++ b/Meshtastic Apple.xcodeproj/project.pbxproj @@ -16,6 +16,8 @@ DD17E5DE277D49D400010EC2 /* storeforward.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD17E5DC277D49D400010EC2 /* storeforward.pb.swift */; }; DD1BF2F92776FE2E008C8D2F /* UserMessageList.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1BF2F82776FE2E008C8D2F /* UserMessageList.swift */; }; DD23A50F26FD1B4400D9B90C /* PeripheralModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD23A50E26FD1B4400D9B90C /* PeripheralModel.swift */; }; + DD2553572855B02500E55709 /* LoRaConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2553562855B02500E55709 /* LoRaConfig.swift */; }; + DD2553592855B52700E55709 /* PositionConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2553582855B52700E55709 /* PositionConfig.swift */; }; DD2E65262767A01F00E45FC5 /* NodeDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2E65252767A01F00E45FC5 /* NodeDetail.swift */; }; DD3501892852FC3B000FC853 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD3501882852FC3B000FC853 /* Settings.swift */; }; DD35018B2852FC79000FC853 /* UserSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD35018A2852FC79000FC853 /* UserSettings.swift */; }; @@ -35,7 +37,7 @@ DD8169FF272476C700F4AB02 /* LogDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8169FE272476C700F4AB02 /* LogDocument.swift */; }; DD836AE726F6B38600ABCC23 /* Connect.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD836AE626F6B38600ABCC23 /* Connect.swift */; }; DD882F5D2772E4640005BF05 /* Contacts.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD882F5C2772E4640005BF05 /* Contacts.swift */; }; - DD8EBF43285058FA00426DCA /* DeviceSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8EBF42285058FA00426DCA /* DeviceSettings.swift */; }; + DD8EBF43285058FA00426DCA /* DisplayConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8EBF42285058FA00426DCA /* DisplayConfig.swift */; }; DD90860C26F684AF00DC5189 /* BatteryIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD90860B26F684AF00DC5189 /* BatteryIcon.swift */; }; DD90860E26F69BAE00DC5189 /* NodeMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD90860D26F69BAE00DC5189 /* NodeMap.swift */; }; DD913639270DFF4C00D7ACF3 /* LocalNotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD913638270DFF4C00D7ACF3 /* LocalNotificationManager.swift */; }; @@ -94,6 +96,8 @@ DD17E5DC277D49D400010EC2 /* storeforward.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = storeforward.pb.swift; sourceTree = ""; }; DD1BF2F82776FE2E008C8D2F /* UserMessageList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserMessageList.swift; sourceTree = ""; }; DD23A50E26FD1B4400D9B90C /* PeripheralModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeripheralModel.swift; sourceTree = ""; }; + DD2553562855B02500E55709 /* LoRaConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoRaConfig.swift; sourceTree = ""; }; + DD2553582855B52700E55709 /* PositionConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionConfig.swift; sourceTree = ""; }; DD2E65252767A01F00E45FC5 /* NodeDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeDetail.swift; sourceTree = ""; }; DD3501882852FC3B000FC853 /* Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = ""; }; DD35018A2852FC79000FC853 /* UserSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSettings.swift; sourceTree = ""; }; @@ -113,7 +117,7 @@ DD8169FE272476C700F4AB02 /* LogDocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogDocument.swift; sourceTree = ""; }; DD836AE626F6B38600ABCC23 /* Connect.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Connect.swift; sourceTree = ""; }; DD882F5C2772E4640005BF05 /* Contacts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Contacts.swift; sourceTree = ""; }; - DD8EBF42285058FA00426DCA /* DeviceSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceSettings.swift; sourceTree = ""; }; + DD8EBF42285058FA00426DCA /* DisplayConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayConfig.swift; sourceTree = ""; }; DD90860A26F645B700DC5189 /* MeshtasticApple.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MeshtasticApple.entitlements; sourceTree = ""; }; DD90860B26F684AF00DC5189 /* BatteryIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryIcon.swift; sourceTree = ""; }; DD90860D26F69BAE00DC5189 /* NodeMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeMap.swift; sourceTree = ""; }; @@ -221,12 +225,14 @@ DD4A911C2708C57100501B7E /* Settings */ = { isa = PBXGroup; children = ( + DD3501882852FC3B000FC853 /* Settings.swift */, DD4A911D2708C65400501B7E /* AppSettings.swift */, - DD8EBF42285058FA00426DCA /* DeviceSettings.swift */, + DD6B85A728009258000ACD6B /* ShareChannel.swift */, + DD8EBF42285058FA00426DCA /* DisplayConfig.swift */, + DD2553562855B02500E55709 /* LoRaConfig.swift */, + DD2553582855B52700E55709 /* PositionConfig.swift */, DD8169FA271F1F3A00F4AB02 /* MeshLog.swift */, DD8169FE272476C700F4AB02 /* LogDocument.swift */, - DD6B85A728009258000ACD6B /* ShareChannel.swift */, - DD3501882852FC3B000FC853 /* Settings.swift */, ); path = Settings; sourceTree = ""; @@ -234,7 +240,6 @@ DD8EDE9226F97A2B00A5A10B /* Frameworks */ = { isa = PBXGroup; children = ( - DDCA31312826009C00207175 /* PassKit.framework */, ); name = Frameworks; sourceTree = ""; @@ -593,12 +598,14 @@ C9A7BC1027759A9600760B50 /* PositionAnnotationView.swift in Sources */, DD882F5D2772E4640005BF05 /* Contacts.swift in Sources */, DD47E3CE26F103C600029299 /* NodeList.swift in Sources */, - DD8EBF43285058FA00426DCA /* DeviceSettings.swift in Sources */, + DD8EBF43285058FA00426DCA /* DisplayConfig.swift in Sources */, DD47E3D626F17ED900029299 /* CircleText.swift in Sources */, DDC2E18F26CE25FE0042C5E4 /* ContentView.swift in Sources */, DD17E5DE277D49D400010EC2 /* storeforward.pb.swift in Sources */, + DD2553572855B02500E55709 /* LoRaConfig.swift in Sources */, DDD9E4E4284B208E003777C5 /* UserEntityExtension.swift in Sources */, C9A88B55278B503C00BD810A /* MapViewModule.swift in Sources */, + DD2553592855B52700E55709 /* PositionConfig.swift in Sources */, DDAF8C6326ED0A230058C060 /* admin.pb.swift in Sources */, C9483F6D2773017500998F6B /* MapView.swift in Sources */, DDAF8C5826ED07FD0058C060 /* mesh.pb.swift in Sources */, diff --git a/MeshtasticApple/Helpers/BLEManager.swift b/MeshtasticApple/Helpers/BLEManager.swift index a3be6103..badd164e 100644 --- a/MeshtasticApple/Helpers/BLEManager.swift +++ b/MeshtasticApple/Helpers/BLEManager.swift @@ -444,7 +444,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph case .routingApp: routingPacket(packet: decodedInfo.packet, meshLogging: meshLoggingEnabled, context: context!) case .adminApp: - if meshLoggingEnabled { MeshLogger.log("ℹ️ MESH PACKET received for Admin App UNHANDLED \(try! decodedInfo.packet.jsonString())") } + adminAppPacket(packet: decodedInfo.packet, meshLogging: meshLoggingEnabled, context: context!) case .replyApp: if meshLoggingEnabled { MeshLogger.log("ℹ️ MESH PACKET received for Reply App UNHANDLED \(try! decodedInfo.packet.jsonString())") } case .ipTunnelApp: @@ -799,13 +799,42 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph return false } - public func getConfig(destNum: Int64, wantResponse: Bool) -> Bool { - var newConfig = Config.LoRaConfig() - var channel = ChannelSettings() - // var newPrefs = (value.loraConfig).toBuilder() -// newConfig. + var adminPacket = AdminMessage() + adminPacket.getConfigRequest = AdminMessage.ConfigType.deviceConfig + + adminPacket.variant = AdminMessage.OneOf_Variant.getConfigRequest(AdminMessage.ConfigType.loraConfig) + + + var meshPacket: MeshPacket = MeshPacket() + meshPacket.to = UInt32(connectedPeripheral.num) + meshPacket.from = UInt32(connectedPeripheral.num) + meshPacket.id = UInt32.random(in: UInt32(UInt8.max).. MyInfoEntity? { @@ -322,6 +324,49 @@ func nodeInfoAppPacket (packet: MeshPacket, meshLogging: Bool, context: NSManage } } +func adminAppPacket (packet: MeshPacket, meshLogging: Bool, context: NSManagedObjectContext) { + + if let deviceConfig = try? MeshtasticApple.Config.DeviceConfig(serializedData: packet.decoded.payload) { + + print(try! deviceConfig.jsonString()) + + } else if let displayConfig = try? MeshtasticApple.Config.DisplayConfig(serializedData: packet.decoded.payload) { + + print(try! displayConfig.jsonUTF8Data()) + print(displayConfig.gpsFormat) + + } else if let loraConfig = try? MeshtasticApple.Config.LoRaConfig(serializedData: packet.decoded.payload) { + + print(try! loraConfig.jsonUTF8Data()) + print(loraConfig.region) + + } else if let positionConfig = try? MeshtasticApple.Config.PositionConfig(serializedData: packet.decoded.payload) { + + print(try! positionConfig.jsonUTF8Data()) + print(positionConfig.positionBroadcastSecs) + + } else if let powerConfig = try? MeshtasticApple.Config.PowerConfig(serializedData: packet.decoded.payload) { + + print(try! powerConfig.jsonUTF8Data()) + print(powerConfig.meshSdsTimeoutSecs) + + } + + + if meshLogging { MeshLogger.log("ℹ️ MESH PACKET received for Admin App UNHANDLED \(try! packet.jsonString())") } + + //PowerConfig + //WiFiConfig + + + + + //if let loraConfig = try? MeshtasticApple.Config.LoRaConfig(serializedData: packet.serializedData) { + + // print(loraConfig) + //} +} + func positionPacket (packet: MeshPacket, meshLogging: Bool, context: NSManagedObjectContext) { let fetchNodePositionRequest: NSFetchRequest = NSFetchRequest.init(entityName: "NodeInfoEntity") diff --git a/MeshtasticApple/MeshtasticApple.entitlements b/MeshtasticApple/MeshtasticApple.entitlements index bb7ecb1e..0d89936b 100644 --- a/MeshtasticApple/MeshtasticApple.entitlements +++ b/MeshtasticApple/MeshtasticApple.entitlements @@ -4,7 +4,7 @@ com.apple.developer.associated-domains - applinks:meshtastic.org + applinks:meshtastic.org/e/ com.apple.security.app-sandbox diff --git a/MeshtasticApple/MeshtasticAppleApp.swift b/MeshtasticApple/MeshtasticAppleApp.swift index ec691a5c..681c36a6 100644 --- a/MeshtasticApple/MeshtasticAppleApp.swift +++ b/MeshtasticApple/MeshtasticAppleApp.swift @@ -1,3 +1,5 @@ +// Copyright (C) 2022 Garth Vander Houwen + import SwiftUI import CoreData diff --git a/MeshtasticApple/Protobufs/config.pb.swift b/MeshtasticApple/Protobufs/config.pb.swift index 4a1bff42..10c67cc5 100644 --- a/MeshtasticApple/Protobufs/config.pb.swift +++ b/MeshtasticApple/Protobufs/config.pb.swift @@ -416,13 +416,6 @@ struct Config { /// 0 for default of 1 minute var waitBluetoothSecs: UInt32 = 0 - /// - /// Power management state machine option. - /// See [power management](/docs/software/other/power) for details. - /// 0 for default of 15 minutes - /// IMPORTANT NOTE FOR DEVICE CLIENTS: YOU MUST SEND SOME SORT OF PACKET TO THE PHONE AT LEAST THIS OFTEN OR THE DEVICE WILL DECIDE YOU ARE GONE! - var phoneTimeoutSecs: UInt32 = 0 - /// /// Power management state machine option. /// See [power management](/docs/software/other/power) for details. @@ -1363,7 +1356,6 @@ extension Config.PowerConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImple 5: .standard(proto: "is_power_saving"), 6: .standard(proto: "adc_multiplier_override"), 7: .standard(proto: "wait_bluetooth_secs"), - 8: .standard(proto: "phone_timeout_secs"), 9: .standard(proto: "mesh_sds_timeout_secs"), 10: .standard(proto: "sds_secs"), 11: .standard(proto: "ls_secs"), @@ -1383,7 +1375,6 @@ extension Config.PowerConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImple case 5: try { try decoder.decodeSingularBoolField(value: &self.isPowerSaving) }() case 6: try { try decoder.decodeSingularFloatField(value: &self.adcMultiplierOverride) }() case 7: try { try decoder.decodeSingularUInt32Field(value: &self.waitBluetoothSecs) }() - case 8: try { try decoder.decodeSingularUInt32Field(value: &self.phoneTimeoutSecs) }() case 9: try { try decoder.decodeSingularUInt32Field(value: &self.meshSdsTimeoutSecs) }() case 10: try { try decoder.decodeSingularUInt32Field(value: &self.sdsSecs) }() case 11: try { try decoder.decodeSingularUInt32Field(value: &self.lsSecs) }() @@ -1415,9 +1406,6 @@ extension Config.PowerConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImple if self.waitBluetoothSecs != 0 { try visitor.visitSingularUInt32Field(value: self.waitBluetoothSecs, fieldNumber: 7) } - if self.phoneTimeoutSecs != 0 { - try visitor.visitSingularUInt32Field(value: self.phoneTimeoutSecs, fieldNumber: 8) - } if self.meshSdsTimeoutSecs != 0 { try visitor.visitSingularUInt32Field(value: self.meshSdsTimeoutSecs, fieldNumber: 9) } @@ -1441,7 +1429,6 @@ extension Config.PowerConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImple if lhs.isPowerSaving != rhs.isPowerSaving {return false} if lhs.adcMultiplierOverride != rhs.adcMultiplierOverride {return false} if lhs.waitBluetoothSecs != rhs.waitBluetoothSecs {return false} - if lhs.phoneTimeoutSecs != rhs.phoneTimeoutSecs {return false} if lhs.meshSdsTimeoutSecs != rhs.meshSdsTimeoutSecs {return false} if lhs.sdsSecs != rhs.sdsSecs {return false} if lhs.lsSecs != rhs.lsSecs {return false} diff --git a/MeshtasticApple/Protobufs/telemetry.pb.swift b/MeshtasticApple/Protobufs/telemetry.pb.swift index 3dfbd79e..0188c1c2 100644 --- a/MeshtasticApple/Protobufs/telemetry.pb.swift +++ b/MeshtasticApple/Protobufs/telemetry.pb.swift @@ -26,44 +26,52 @@ enum TelemetrySensorType: SwiftProtobuf.Enum { typealias RawValue = Int /// - /// No external telemetry sensor + /// No external telemetry sensor explicitly set case notSet // = 0 /// - /// TODO: REPLACE + /// Moderate accuracy temperature case dht11 // = 1 /// - /// TODO: REPLACE + /// High accuracy temperature case ds18B20 // = 2 /// - /// TODO: REPLACE + /// Moderate accuracy temperature and humidity case dht12 // = 3 /// - /// TODO: REPLACE + /// Moderate accuracy temperature and humidity case dht21 // = 4 /// - /// TODO: REPLACE + /// Moderate accuracy temperature and humidity case dht22 // = 5 /// - /// TODO: REPLACE + /// High accuracy temperature, pressure, humidity case bme280 // = 6 /// - /// TODO: REPLACE + /// High accuracy temperature, pressure, humidity, and air resistance case bme680 // = 7 /// - /// TODO: REPLACE + /// Very high accuracy temperature case mcp9808 // = 8 /// - /// TODO: REPLACE + /// Moderate accuracy temperature and humidity case shtc3 // = 9 + + /// + /// Moderate accuracy current and voltage + case ina260 // = 10 + + /// + /// Moderate accuracy current and voltage + case ina219 // = 11 case UNRECOGNIZED(Int) init() { @@ -82,6 +90,8 @@ enum TelemetrySensorType: SwiftProtobuf.Enum { case 7: self = .bme680 case 8: self = .mcp9808 case 9: self = .shtc3 + case 10: self = .ina260 + case 11: self = .ina219 default: self = .UNRECOGNIZED(rawValue) } } @@ -98,6 +108,8 @@ enum TelemetrySensorType: SwiftProtobuf.Enum { case .bme680: return 7 case .mcp9808: return 8 case .shtc3: return 9 + case .ina260: return 10 + case .ina219: return 11 case .UNRECOGNIZED(let i): return i } } @@ -119,6 +131,8 @@ extension TelemetrySensorType: CaseIterable { .bme680, .mcp9808, .shtc3, + .ina260, + .ina219, ] } @@ -280,6 +294,8 @@ extension TelemetrySensorType: SwiftProtobuf._ProtoNameProviding { 7: .same(proto: "BME680"), 8: .same(proto: "MCP9808"), 9: .same(proto: "SHTC3"), + 10: .same(proto: "INA260"), + 11: .same(proto: "INA219"), ] } diff --git a/MeshtasticApple/Views/ContentView.swift b/MeshtasticApple/Views/ContentView.swift index db8610ae..fec35163 100644 --- a/MeshtasticApple/Views/ContentView.swift +++ b/MeshtasticApple/Views/ContentView.swift @@ -1,5 +1,5 @@ /* -Abstract: Default App View +Copyright (c) Garth Vander Houwen 2021 */ import SwiftUI @@ -36,7 +36,7 @@ struct ContentView: View { // .tag(Tab.messages) Connect() .tabItem { - Label("Bluetooth", systemImage: "dot.radiowaves.left.and.right") + Label("Bluetooth", systemImage: "antenna.radiowaves.left.and.right") .symbolRenderingMode(.hierarchical) .symbolVariant(.none) } @@ -55,7 +55,7 @@ struct ContentView: View { .symbolVariant(.none) } .tag(Tab.map) - AppSettings() + Settings() .tabItem { Label("Settings", systemImage: "gear") .symbolRenderingMode(.hierarchical) diff --git a/MeshtasticApple/Views/Messages/UserMessageList.swift b/MeshtasticApple/Views/Messages/UserMessageList.swift index d3e2abb5..f651f8cf 100644 --- a/MeshtasticApple/Views/Messages/UserMessageList.swift +++ b/MeshtasticApple/Views/Messages/UserMessageList.swift @@ -36,10 +36,6 @@ struct UserMessageList: View { var body: some View { - let firmwareVersion = bleManager.lastConnnectionVersion - let minimumVersion = "1.2.52" - let hasTapbackSupport = minimumVersion.compare(firmwareVersion, options: .numeric) == .orderedAscending || minimumVersion.compare(firmwareVersion, options: .numeric) == .orderedSame - VStack { ScrollViewReader { scrollView in @@ -78,7 +74,7 @@ struct UserMessageList: View { if currentUser { Spacer(minLength:50) } if !currentUser { - CircleText(text: message.fromUser?.shortName ?? "???", color: currentUser ? .accentColor : Color(.darkGray), circleSize: 36, fontSize: 16).padding(.all, 5) + CircleText(text: message.fromUser?.shortName ?? "????", color: currentUser ? .accentColor : Color(.darkGray), circleSize: 36, fontSize: 16).padding(.all, 5) } VStack(alignment: currentUser ? .trailing : .leading) { @@ -90,118 +86,115 @@ struct UserMessageList: View { .cornerRadius(15) .contextMenu { - if hasTapbackSupport { - - Menu("Tapback response") { + Menu("Tapback response") { + + Button(action: { - Button(action: { + if bleManager.sendMessage(message: "❤️", toUserNum: user.num, isEmoji: true, replyID: message.messageId) { - if bleManager.sendMessage(message: "❤️", toUserNum: user.num, isEmoji: true, replyID: message.messageId) { - - print("Sent ❤️ Tapback") - self.context.refresh(user, mergeChanges: true) - - } else { print("❤️ Tapback Failed") } + print("Sent ❤️ Tapback") + self.context.refresh(user, mergeChanges: true) - }) { - Text("Heart") - let image = "❤️".image() - Image(uiImage: image!) - } - Button(action: { - - if bleManager.sendMessage(message: "👍", toUserNum: user.num, isEmoji: true, replyID: message.messageId) { - - print("Sent 👍 Tapback") - self.context.refresh(user, mergeChanges: true) - - } else { print("👍 Tapback Failed")} - - }) { - Text("Thumbs Up") - let image = "👍".image() - Image(uiImage: image!) - } - Button(action: { - - if bleManager.sendMessage(message: "👎", toUserNum: user.num, isEmoji: true, replyID: message.messageId) { - - print("Sent 👎 Tapback") - self.context.refresh(user, mergeChanges: true) - - } else { print("👎 Tapback Failed") } - - }) { - Text("Thumbs Down") - let image = "👎".image() - Image(uiImage: image!) - } - Button(action: { - - if bleManager.sendMessage(message: "🤣", toUserNum: user.num, isEmoji: true, replyID: message.messageId) { - - print("Sent 🤣 Tapback") - self.context.refresh(user, mergeChanges: true) - - } else { print("🤣 Tapback Failed") } - - }) { - Text("HaHa") - let image = "🤣".image() - Image(uiImage: image!) - } - Button(action: { - - if bleManager.sendMessage(message: "‼️", toUserNum: user.num, isEmoji: true, replyID: message.messageId) { - - print("Sent ‼️ Tapback") - self.context.refresh(user, mergeChanges: true) - - } else { print("‼️ Tapback Failed") } - - }) { - Text("Exclamation Mark") - let image = "‼️".image() - Image(uiImage: image!) - } - Button(action: { - - if bleManager.sendMessage(message: "❓", toUserNum: user.num, isEmoji: true, replyID: message.messageId) { - - print("Sent ❓ Tapback") - self.context.refresh(user, mergeChanges: true) - - } else { print("❓ Tapback Failed") } - - }) { - Text("Question Mark") - let image = "❓".image() - Image(uiImage: image!) - } - Button(action: { + } else { print("❤️ Tapback Failed") } - if bleManager.sendMessage(message: "💩", toUserNum: user.num, isEmoji: true, replyID: message.messageId) { - - print("Sent 💩 Tapback") - self.context.refresh(user, mergeChanges: true) - - } else { print("💩 Tapback Failed") } - - }) { - Text("Poop") - let image = "💩".image() - Image(uiImage: image!) - } + }) { + Text("Heart") + let image = "❤️".image() + Image(uiImage: image!) } Button(action: { - self.replyMessageId = message.messageId - self.focusedField = .messageText - - print("I want to reply to \(message.messageId)") + + if bleManager.sendMessage(message: "👍", toUserNum: user.num, isEmoji: true, replyID: message.messageId) { + + print("Sent 👍 Tapback") + self.context.refresh(user, mergeChanges: true) + + } else { print("👍 Tapback Failed")} + }) { - Text("Reply") - Image(systemName: "arrowshape.turn.up.left.2.fill") + Text("Thumbs Up") + let image = "👍".image() + Image(uiImage: image!) } + Button(action: { + + if bleManager.sendMessage(message: "👎", toUserNum: user.num, isEmoji: true, replyID: message.messageId) { + + print("Sent 👎 Tapback") + self.context.refresh(user, mergeChanges: true) + + } else { print("👎 Tapback Failed") } + + }) { + Text("Thumbs Down") + let image = "👎".image() + Image(uiImage: image!) + } + Button(action: { + + if bleManager.sendMessage(message: "🤣", toUserNum: user.num, isEmoji: true, replyID: message.messageId) { + + print("Sent 🤣 Tapback") + self.context.refresh(user, mergeChanges: true) + + } else { print("🤣 Tapback Failed") } + + }) { + Text("HaHa") + let image = "🤣".image() + Image(uiImage: image!) + } + Button(action: { + + if bleManager.sendMessage(message: "‼️", toUserNum: user.num, isEmoji: true, replyID: message.messageId) { + + print("Sent ‼️ Tapback") + self.context.refresh(user, mergeChanges: true) + + } else { print("‼️ Tapback Failed") } + + }) { + Text("Exclamation Mark") + let image = "‼️".image() + Image(uiImage: image!) + } + Button(action: { + + if bleManager.sendMessage(message: "❓", toUserNum: user.num, isEmoji: true, replyID: message.messageId) { + + print("Sent ❓ Tapback") + self.context.refresh(user, mergeChanges: true) + + } else { print("❓ Tapback Failed") } + + }) { + Text("Question Mark") + let image = "❓".image() + Image(uiImage: image!) + } + Button(action: { + + if bleManager.sendMessage(message: "💩", toUserNum: user.num, isEmoji: true, replyID: message.messageId) { + + print("Sent 💩 Tapback") + self.context.refresh(user, mergeChanges: true) + + } else { print("💩 Tapback Failed") } + + }) { + Text("Poop") + let image = "💩".image() + Image(uiImage: image!) + } + } + Button(action: { + self.replyMessageId = message.messageId + self.focusedField = .messageText + + print("I want to reply to \(message.messageId)") + }) { + Text("Reply") + Image(systemName: "arrowshape.turn.up.left.2.fill") } Button(action: { UIPasteboard.general.string = message.messagePayload @@ -250,37 +243,33 @@ struct UserMessageList: View { } } - if hasTapbackSupport { + let tapbacks = message.value(forKey: "tapbacks") as! [MessageEntity] - let tapbacks = message.value(forKey: "tapbacks") as! [MessageEntity] + if tapbacks.count > 0 { - - if tapbacks.count > 0 { - - VStack (alignment: .trailing) { + VStack (alignment: .trailing) { - HStack { - - ForEach( tapbacks ) { (tapback: MessageEntity) in - - VStack { - - let image = tapback.messagePayload!.image(fontSize: 20) - Image(uiImage: image!).font(.caption) - Text("\(tapback.fromUser?.shortName ?? "???")") - .font(.caption2) - .foregroundColor(.gray) - .fixedSize() - .padding(.bottom, 1) - } + HStack { + + ForEach( tapbacks ) { (tapback: MessageEntity) in + + VStack { + + let image = tapback.messagePayload!.image(fontSize: 20) + Image(uiImage: image!).font(.caption) + Text("\(tapback.fromUser?.shortName ?? "????")") + .font(.caption2) + .foregroundColor(.gray) + .fixedSize() + .padding(.bottom, 1) } } - .padding(10) - .overlay( - RoundedRectangle(cornerRadius: 18) - .stroke(Color.gray, lineWidth: 1) - ) } + .padding(10) + .overlay( + RoundedRectangle(cornerRadius: 18) + .stroke(Color.gray, lineWidth: 1) + ) } } @@ -291,7 +280,6 @@ struct UserMessageList: View { Text("Acknowledged").font(.caption2).foregroundColor(.gray) } } - } .padding(.bottom) .id(user.messageList.firstIndex(of: message)) diff --git a/MeshtasticApple/Views/Settings/AppSettings.swift b/MeshtasticApple/Views/Settings/AppSettings.swift index 59910fac..e5c14e1a 100644 --- a/MeshtasticApple/Views/Settings/AppSettings.swift +++ b/MeshtasticApple/Views/Settings/AppSettings.swift @@ -91,110 +91,97 @@ struct AppSettings: View { var body: some View { - NavigationView { + VStack { - GeometryReader { _ in + Form { + Section(header: Text("USER DETAILS")) { - Form { - Section(header: Text("USER DETAILS")) { - - HStack { - Label("Name", systemImage: "person.crop.rectangle.fill") - TextField("Username", text: $userSettings.meshtasticUsername) - .foregroundColor(.gray) - } - .keyboardType(.asciiCapable) - .disableAutocorrection(true) - .listRowSeparator(.visible) - - HStack { - Label("Radio", systemImage: "flipphone") - Text(userSettings.preferredPeripheralName) - .foregroundColor(.gray) - - } - Text("This option is set via the preferred radio toggle for the connected device on the bluetooth tab.") - .font(.caption) - .listRowSeparator(.hidden) - Text("The preferred radio will automatically reconnect if it becomes disconnected and is still within range.") - .font(.caption2) + HStack { + Label("Name", systemImage: "person.crop.rectangle.fill") + TextField("Username", text: $userSettings.meshtasticUsername) .foregroundColor(.gray) - } - Section(header: Text("LOCATION OPTIONS")) { + .keyboardType(.asciiCapable) + .disableAutocorrection(true) + .listRowSeparator(.visible) + + HStack { + Label("Radio", systemImage: "flipphone") + Text(userSettings.preferredPeripheralName) + .foregroundColor(.gray) - Toggle(isOn: $userSettings.provideLocation) { - - Label("Provide location to mesh", systemImage: "location.circle.fill") - } - .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - - if userSettings.provideLocation { - - Picker(" Update Interval", selection: $userSettings.provideLocationInterval) { - ForEach(LocationUpdateInterval.allCases) { lu in - Text(lu.description) - } - } - .pickerStyle(DefaultPickerStyle()) - - Text("How frequently your phone will send your location to the device, location updates to the mesh are managed by the device.") - .font(.caption) - .listRowSeparator(.visible) - } } - Section(header: Text("MESH OPTIONS")) { - - NavigationLink(destination: ShareChannel()) { - Text("Share Your Channel vis QR Code") - } - - } - Section(header: Text("MESSAGING OPTIONS")) { + Text("This option is set via the preferred radio toggle for the connected device on the bluetooth tab.") + .font(.caption) + .listRowSeparator(.hidden) + Text("The preferred radio will automatically reconnect if it becomes disconnected and is still within range.") + .font(.caption2) + .foregroundColor(.gray) - Picker("Keyboard Type", selection: $userSettings.keyboardType) { - ForEach(KeyboardType.allCases) { kb in - Text(kb.description) + } + Section(header: Text("OPTIONS")) { + + Toggle(isOn: $userSettings.provideLocation) { + + Label("Provide location to mesh", systemImage: "location.circle.fill") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + + if userSettings.provideLocation { + + Picker(" Update Interval", selection: $userSettings.provideLocationInterval) { + ForEach(LocationUpdateInterval.allCases) { lu in + Text(lu.description) } } .pickerStyle(DefaultPickerStyle()) - } - Section(header: Text("MAP OPTIONS")) { - Picker("Map Type", selection: $userSettings.meshMapType) { - ForEach(MeshMapType.allCases) { map in - Text(map.description) - } - } - .pickerStyle(DefaultPickerStyle()) - // TextField("Custom Tile Server", text: $userSettings.meshMapCustomTileServer) - } - Section(header: Text("DEBUG OPTIONS")) { - 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") - } + + Text("How frequently your phone will send your location to the device, location updates to the mesh are managed by the device.") + .font(.caption) .listRowSeparator(.visible) + } + + Picker("Keyboard Type", selection: $userSettings.keyboardType) { + ForEach(KeyboardType.allCases) { kb in + Text(kb.description) } } + .pickerStyle(DefaultPickerStyle()) + + Picker("Map Type", selection: $userSettings.meshMapType) { + ForEach(MeshMapType.allCases) { map in + Text(map.description) + } + } + .pickerStyle(DefaultPickerStyle()) + + } + 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) + } } } - .navigationTitle("App Settings") - .navigationBarItems(trailing: + } + .navigationTitle("App Settings") + .navigationBarItems(trailing: - ZStack { + ZStack { - ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.lastFourCode : "????") - }) - .onAppear { + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.lastFourCode : "????") + }) + .onAppear { - self.bleManager.context = context - } - } + self.bleManager.context = context + } .navigationViewStyle(StackNavigationViewStyle()) } } diff --git a/MeshtasticApple/Views/Settings/DeviceSettings.swift b/MeshtasticApple/Views/Settings/DeviceSettings.swift deleted file mode 100644 index 9874e317..00000000 --- a/MeshtasticApple/Views/Settings/DeviceSettings.swift +++ /dev/null @@ -1,8 +0,0 @@ -// -// DeviceSettings.swift -// MeshtasticApple -// -// Created by Garth Vander Houwen on 6/7/22. -// - -import Foundation diff --git a/MeshtasticApple/Views/Settings/DisplayConfig.swift b/MeshtasticApple/Views/Settings/DisplayConfig.swift new file mode 100644 index 00000000..aefc89ac --- /dev/null +++ b/MeshtasticApple/Views/Settings/DisplayConfig.swift @@ -0,0 +1,171 @@ +// +// DeviceSettings.swift +// Meshtastic Apple +// +// Copyright (c) Garth Vander Houwen 6/7/22. +// + +import SwiftUI + +enum GpsFormat: Int, CaseIterable, Identifiable { + + case gpsFormatDec = 0 + case gpsFormatDms = 1 + case gpsFormatUtm = 2 + case gpsFormatMgrs = 3 + case gpsFormatOlc = 4 + case gpsFormatOsgr = 5 + + var id: Int { self.rawValue } + var description: String { + get { + switch self { + case .gpsFormatDec: + return "Decimal Degrees Format" + case .gpsFormatDms: + return "Degrees Minutes Seconds" + case .gpsFormatUtm: + return "Universal Transverse Mercator" + case .gpsFormatMgrs: + return "Military Grid Reference System" + case .gpsFormatOlc: + return "Open Location Code (aka Plus Codes)" + case .gpsFormatOsgr: + return "Ordnance Survey Grid Reference" + } + } + } +} + +// Default of 0 is One Minute +enum ScreenOnSeconds: Int, CaseIterable, Identifiable { + + case fifteenSeconds = 15 + case thirtySeconds = 30 + case oneMinute = 0 + case fiveMinutes = 300 + case tenMinutes = 600 + case fifteenMinutes = 900 + + var id: Int { self.rawValue } + var description: String { + get { + switch self { + case .fifteenSeconds: + return "Fifteen Seconds" + 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" + } + } + } +} + +// Default of 0 is off +enum ScreenCarouselSeconds: Int, CaseIterable, Identifiable { + + case off = 0 + case fifteenSeconds = 15 + 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 .fifteenSeconds: + return "Fifteen Seconds" + 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 DisplayConfig: View { + + @Environment(\.managedObjectContext) var context + @EnvironmentObject var bleManager: BLEManager + + @State var isConnected: Bool = false + + @State var gpsFormat: Config.DisplayConfig.GpsCoordinateFormat = .gpsFormatDec + + var body: some View { + + VStack { + + Form { + Section(header: Text("Timing")) { + + Picker("Screen on for", selection: $gpsFormat ) { + ForEach(ScreenOnSeconds.allCases) { lu in + Text(lu.description) + } + } + .pickerStyle(DefaultPickerStyle()) + + Text("The number of seconds the screen remains on after the user button is pressed or messages are received.") + .font(.caption) + .listRowSeparator(.visible) + + Picker("Carousel Interval", selection: $gpsFormat ) { + ForEach(ScreenCarouselSeconds.allCases) { lu in + Text(lu.description) + } + } + .pickerStyle(DefaultPickerStyle()) + + Text("Automatically toggles to the next page on the screen like a carousel, based the specified interval.") + .font(.caption) + .listRowSeparator(.visible) + + } + Section(header: Text("Format")) { + Picker("GPS Format", selection: $gpsFormat ) { + ForEach(GpsFormat.allCases) { lu in + Text(lu.description) + } + } + .pickerStyle(DefaultPickerStyle()) + + Text("The format used to display GPS coordinates on the screen.") + .font(.caption) + .listRowSeparator(.visible) + } + } + } + .navigationTitle("Display Config") + .navigationBarItems(trailing: + + ZStack { + + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.lastFourCode : "????") + }) + .onAppear { + + self.bleManager.context = context + } + .navigationViewStyle(StackNavigationViewStyle()) + } +} diff --git a/MeshtasticApple/Views/Settings/LoRaConfig.swift b/MeshtasticApple/Views/Settings/LoRaConfig.swift new file mode 100644 index 00000000..526dbe4d --- /dev/null +++ b/MeshtasticApple/Views/Settings/LoRaConfig.swift @@ -0,0 +1,150 @@ +// +// LoRaConfig.swift +// Meshtastic Apple +// +// Copyright (c) by Garth Vander Houwen 6/11/22. +// + +import SwiftUI + + +enum RegionCodes : Int, CaseIterable, Identifiable { + + case unset = 0 + case us = 1 + case eu433 = 2 + case eu868 = 3 + case cn = 4 + case jp = 5 + case anz = 6 + case kr = 7 + case tw = 8 + case ru = 9 + //case in = 10 + case nz865 + case th + + var id: Int { self.rawValue } + var description: String { + get { + switch self { + case .unset: + return "UNSET - Please set a Region" + case .us: + return "United States" + case .eu433: + return "European Union 433mhz" + case .eu868: + return "European Union 868mhz" + case .cn: + return "China" + case .jp: + return "Japan" + case .anz: + return "Australia / New Zealand" + case .kr: + return "Korea" + case .tw: + return "Taiwan" + case .ru: + return "Russia" + case .nz865: + return "New Zealand 865mhz" + case .th: + return "TH" + } + } + } +} + +enum ModemPresets : Int, CaseIterable, Identifiable { + + case LongFast = 0 + case LongSlow = 1 + case VLongSlow = 2 + case MidSlow = 3 + case MidFast = 4 + case ShortSlow = 5 + case ShortFast = 6 + + var id: Int { self.rawValue } + var description: String { + get { + switch self { + + case .LongFast: + return "Long Fast" + case .LongSlow: + return "Long Slow" + case .VLongSlow: + return "Very Long Slow" + case .MidSlow: + return "Mid Slow" + case .MidFast: + return "Mid Fast" + case .ShortSlow: + return "Short Slow" + case .ShortFast: + return "Short Fast" + } + } + } +} + +struct LoRaConfig: View { + + @Environment(\.managedObjectContext) var context + @EnvironmentObject var bleManager: BLEManager + + @State var region: Config.LoRaConfig.RegionCode = .us + + @State var modemPreset: Config.LoRaConfig.ModemPreset = .longFast + + var body: some View { + + VStack { + + Form { + Section(header: Text("Region")) { + + Picker("Region", selection: $region ) { + ForEach(RegionCodes.allCases) { r in + Text(r.description) + } + } + .pickerStyle(DefaultPickerStyle()) + Text("The region where you will be using your Meshtastic LoRa radios.") + .font(.caption) + .listRowSeparator(.visible) + .listRowSeparator(.visible) + } + Section(header: Text("Modem")) { + Picker("Presets", selection: $region ) { + ForEach(ModemPresets.allCases) { m in + Text(m.description) + } + } + .pickerStyle(DefaultPickerStyle()) + Text("Available modem presets.") + .font(.caption) + .listRowSeparator(.visible) + .listRowSeparator(.visible) + } + + } + + } + .navigationTitle("LoRa Config") + .navigationBarItems(trailing: + + ZStack { + + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.lastFourCode : "????") + }) + .onAppear { + + self.bleManager.context = context + } + .navigationViewStyle(StackNavigationViewStyle()) + } +} diff --git a/MeshtasticApple/Views/Settings/PositionConfig.swift b/MeshtasticApple/Views/Settings/PositionConfig.swift new file mode 100644 index 00000000..fc5c5c22 --- /dev/null +++ b/MeshtasticApple/Views/Settings/PositionConfig.swift @@ -0,0 +1,217 @@ +// +// PositionConfig.swift +// Meshtastic Apple +// +// Copyright (c) Garth Vander Houwen 6/11/22. +// + +import SwiftUI + +enum GpsUpdateIntervals: Int, CaseIterable, Identifiable { + + case thirtySeconds = 0 + case oneMinute = 60 + case fiveMinutes = 300 + case tenMinutes = 600 + case fifteenMinutes = 900 + case thirtyMinutes = 1800 + case oneHour = 3600 + case maxInt32 = 2147483647 + + var id: Int { self.rawValue } + var description: String { + get { + switch self { + + 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" + case .thirtyMinutes: + return "Thirty Minutes" + case .oneHour: + return "One Hour" + case .maxInt32: + return "On Boot Only" + } + } + } +} + +enum GpsAttemptTimes: Int, CaseIterable, Identifiable { + + case thirtySeconds = 0 + case oneMinute = 60 + case fiveMinutes = 300 + case tenMinutes = 600 + case fifteenMinutes = 900 + + var id: Int { self.rawValue } + var description: String { + get { + switch self { + + 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" + } + } + } +} + +enum PositionBroadcastIntervals: Int, CaseIterable, Identifiable { + + case thirtySeconds = 30 + case oneMinute = 60 + case fiveMinutes = 300 + case tenMinutes = 600 + case fifteenMinutes = 0 + case thirtyMinutes = 1800 + case oneHour = 3600 + + var id: Int { self.rawValue } + var description: String { + get { + switch self { + + 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" + case .thirtyMinutes: + return "Thirty Minutes" + case .oneHour: + return "One Hour" + } + } + } +} + +struct PositionConfig: View { + + @Environment(\.managedObjectContext) var context + @EnvironmentObject var bleManager: BLEManager + + @State var smartPositionEnabled = true + @State var deviceGpsEnabled = true + @State var fixedPosition = false + @State var gpsUpdateInterval: Int32 = 0 + @State var gpsAttemptTime: Int32 = 0 + @State var positionBroadcastSeconds: Int32 = 0 + + var body: some View { + + VStack { + + Form { + + Section(header: Text("Device GPS Options")) { + + Toggle(isOn: $deviceGpsEnabled) { + + Label("Device GPS Enabled", systemImage: "location") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + if deviceGpsEnabled { + + Picker("GPS Update Interval", selection: $gpsUpdateInterval) { + ForEach(GpsUpdateIntervals.allCases) { ui in + Text(ui.description) + } + } + .pickerStyle(DefaultPickerStyle()) + + Text("How often should we try to get a GPS position.") + .font(.caption) + .listRowSeparator(.visible) + + Picker("GPS Attempt Time", selection: $gpsAttemptTime) { + ForEach(GpsAttemptTimes.allCases) { at in + Text(at.description) + } + } + .pickerStyle(DefaultPickerStyle()) + + Text("How long should we try to get our position during each GPS Update Interval attempt?") + .font(.caption) + .listRowSeparator(.visible) + + + } else { + + Toggle(isOn: $fixedPosition) { + + Label("Fixed Position", systemImage: "location.square.fill") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + if fixedPosition { + + Text("Set to current location here") + .font(.caption) + .listRowSeparator(.visible) + } + + } + } + Section(header: Text("Position Packet Options")) { + + Toggle(isOn: $smartPositionEnabled) { + + Label("Smart Position Broadcast", systemImage: "location.fill.viewfinder") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + if !smartPositionEnabled { + + Picker("Position Broadcast Interval", selection: $positionBroadcastSeconds) { + ForEach(PositionBroadcastIntervals.allCases) { at in + Text(at.description) + } + } + .pickerStyle(DefaultPickerStyle()) + + Text("We should send our position this often (but only if it has changed significantly)") + .font(.caption) + .listRowSeparator(.visible) + + } + } + Section(header: Text("Position Flags")) { + Text("TODO") + .font(.caption) + .listRowSeparator(.visible) + } + } + } + .navigationTitle("Position Config") + .navigationBarItems(trailing: + + ZStack { + + ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.lastFourCode : "????") + }) + .onAppear { + + self.bleManager.context = context + } + .navigationViewStyle(StackNavigationViewStyle()) + } +} diff --git a/MeshtasticApple/Views/Settings/Settings.swift b/MeshtasticApple/Views/Settings/Settings.swift index 079ef225..4db480ad 100644 --- a/MeshtasticApple/Views/Settings/Settings.swift +++ b/MeshtasticApple/Views/Settings/Settings.swift @@ -2,7 +2,123 @@ // Settings.swift // MeshtasticApple // -// Created by Garth Vander Houwen on 6/9/22. +// Copyright (c) Garth Vander Houwen 6/9/22. // -import Foundation +import SwiftUI + +struct Settings: View { + + @Environment(\.managedObjectContext) var context + @EnvironmentObject var bleManager: BLEManager + @EnvironmentObject var userSettings: UserSettings + + var body: some View { + NavigationView { + List { + + Section("General") { + NavigationLink { + AppSettings() + } label: { + + Image(systemName: "gearshape") + .symbolRenderingMode(.hierarchical) + Text("App Settings") + } + NavigationLink { + ShareChannel() + } label: { + Image(systemName: "qrcode") + .symbolRenderingMode(.hierarchical) + Text("Share Channel QR Code") + } + } + + Section("Radio Configuration (Non-Functional Interaction Previews)") { + + NavigationLink { + DisplayConfig() + } label: { + + Image(systemName: "display") + .symbolRenderingMode(.hierarchical) + Text("Display (Device Screen)") + } + NavigationLink { + LoRaConfig() + } label: { + + Image(systemName: "dot.radiowaves.left.and.right") + .symbolRenderingMode(.hierarchical) + + Text("LoRa") + } + NavigationLink { + PositionConfig() + } label: { + + Image(systemName: "location") + .symbolRenderingMode(.hierarchical) + + Text("Position") + } + NavigationLink { + PositionConfig() + } label: { + + Image(systemName: "bolt") + .symbolRenderingMode(.hierarchical) + + Text("Power") + } + .disabled(true) + + } + Section("Module Configuration") { + + NavigationLink { + DisplayConfig() + } label: { + + Image(systemName: "list.bullet.rectangle.fill") + .symbolRenderingMode(.hierarchical) + + Text("Canned Messages") + } + .disabled(true) + NavigationLink { + DisplayConfig() + } label: { + + Image(systemName: "point.3.connected.trianglepath.dotted") + .symbolRenderingMode(.hierarchical) + + Text("Range Test") + } + .disabled(true) + NavigationLink { + DisplayConfig() + } label: { + + Image(systemName: "chart.xyaxis.line") + .symbolRenderingMode(.hierarchical) + + Text("Telemetry (Sensors)") + } + .disabled(true) + } + // Not Implemented: + // Device Config - No interesting settings for end users + // Power Config - All confusion, should delete most and have sensible defaults + // External Notifications - Not Working + // Serial Config - Not sure what the point is + // Store Forward Config - Not Working + // WiFi Config - Would break connection to device + // MQTT Config - Part of WiFi + } + .listStyle(GroupedListStyle()) + .navigationTitle("Settings") + } + } +} diff --git a/MeshtasticApple/Views/Settings/ShareChannel.swift b/MeshtasticApple/Views/Settings/ShareChannel.swift index 4cacb29e..c42bd2fe 100644 --- a/MeshtasticApple/Views/Settings/ShareChannel.swift +++ b/MeshtasticApple/Views/Settings/ShareChannel.swift @@ -38,7 +38,7 @@ struct ShareChannel: View { let channelSet = ChannelSet() - @State private var text = "https://meshtastic.org/e/#" + @State private var text = "https://meshtastic.org/e/#test" var qrCodeImage = QrCodeImage() var body: some View { @@ -52,11 +52,9 @@ struct ShareChannel: View { ScrollView { VStack { - Text("Scan the QR code below with the Apple or Android device you would like to share with your channel settings with.") + Text("Scan the QR code below with the Apple or Android device you would like to share your channel settings with.") .fixedSize(horizontal: false, vertical: true) .font(.callout) - .padding() - Spacer() let image = qrCodeImage.generateQRCode(from: text) Image(uiImage: image)