Meshtastic-Apple/Meshtastic/Views/Settings/UserConfig.swift

215 lines
7.2 KiB
Swift
Raw Normal View History

//
// User.swift
// Meshtastic Apple
//
// Copyright (c) Garth Vander Houwen 6/27/22.
//
import SwiftUI
2023-02-09 22:59:39 -08:00
import CoreData
struct UserConfig: View {
2023-03-06 10:33:18 -08:00
@Environment(\.managedObjectContext) var context
@EnvironmentObject var bleManager: BLEManager
@Environment(\.dismiss) private var goBack
2023-03-06 10:33:18 -08:00
var node: NodeInfoEntity?
2023-03-06 10:33:18 -08:00
2023-02-09 22:59:39 -08:00
enum Field: Hashable {
case frequencyOverride
}
2023-03-06 10:33:18 -08:00
@State private var isPresentingFactoryResetConfirm: Bool = false
@State private var isPresentingSaveConfirm: Bool = false
@State var hasChanges = false
@State var shortName = ""
@State var longName = ""
@State var isLicensed = false
2023-02-08 09:42:07 -08:00
@State var overrideDutyCycle = false
2023-02-09 22:59:39 -08:00
@State var overrideFrequency: Float = 0.0
2023-02-08 09:42:07 -08:00
@State var txPower = 0
2023-03-06 10:33:18 -08:00
2023-02-09 22:59:39 -08:00
@FocusState var focusedField: Field?
2023-03-06 10:33:18 -08:00
2023-02-09 22:59:39 -08:00
let floatFormatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
return formatter
}()
2023-03-06 10:33:18 -08:00
var body: some View {
2023-03-06 10:33:18 -08:00
VStack {
Form {
Section(header: Text("User Details")) {
2023-02-09 22:59:39 -08:00
HStack {
Label(isLicensed ? "Call Sign" : "Long Name", systemImage: "person.crop.rectangle.fill")
TextField("Long Name", text: $longName)
2023-03-06 10:33:18 -08:00
.onChange(of: longName, perform: { _ in
2023-02-09 22:59:39 -08:00
let totalBytes = longName.utf8.count
// Only mess with the value if it is too big
if totalBytes > (isLicensed ? 8 : 36) {
let firstNBytes = Data(longName.utf8.prefix(isLicensed ? 8 : 36))
2023-02-09 22:59:39 -08:00
if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) {
// Set the longName back to the last place where it was the right size
longName = maxBytesString
}
}
2023-02-09 22:59:39 -08:00
})
}
.keyboardType(.default)
.disableAutocorrection(true)
Text("\(String(isLicensed ? "Call Sign" : "Long Name")) can be up to \(isLicensed ? "8" : "36") bytes long.")
2023-02-10 09:38:01 -08:00
.font(.caption2)
2023-03-06 10:33:18 -08:00
2022-08-02 09:44:59 -07:00
HStack {
Label("Short Name", systemImage: "circlebadge.fill")
2023-02-09 22:59:39 -08:00
TextField("Short Name", text: $shortName)
2022-08-02 09:44:59 -07:00
.foregroundColor(.gray)
2023-03-06 10:33:18 -08:00
.onChange(of: shortName, perform: { _ in
let totalBytes = shortName.utf8.count
// Only mess with the value if it is too big
if totalBytes > 4 {
let firstNBytes = Data(shortName.utf8.prefix(4))
if let maxBytesString = String(data: firstNBytes, encoding: String.Encoding.utf8) {
// Set the shortName back to the last place where it was the right size
shortName = maxBytesString
}
}
})
.foregroundColor(.gray)
}
2023-05-05 17:13:35 -07:00
.keyboardType(.default)
.disableAutocorrection(true)
2023-02-10 09:38:01 -08:00
Text("The last 4 of the device MAC address will be appended to the short name to set the device's BLE Name. Short name can be up to 4 bytes long.")
.font(.caption2)
2023-03-06 10:33:18 -08:00
2023-02-08 09:42:07 -08:00
// Only manage ham mode for the locally connected node
2023-03-06 10:33:18 -08:00
if node?.num ?? 0 > 0 && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0 {
2023-02-08 09:42:07 -08:00
Toggle(isOn: $isLicensed) {
2023-02-09 22:59:39 -08:00
Label("Licensed Operator", systemImage: "person.text.rectangle")
2023-02-08 09:42:07 -08:00
}
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
2023-02-09 22:59:39 -08:00
if isLicensed {
2023-03-06 10:33:18 -08:00
2023-02-09 22:59:39 -08:00
Text("Onboarding for licensed operators requires firmware 2.0.20 or greater. Make sure to refer to your local regulations and contact the local amateur frequency coordinators with questions.")
.font(.caption2)
Text("What licensed operator mode does:\n* Sets the node name to your call sign \n* Broadcasts node info every 10 minutes \n* Overrides frequency, dutycycle and tx power \n* Disables encryption")
.font(.caption2)
2023-03-06 10:33:18 -08:00
2023-02-09 22:59:39 -08:00
HStack {
Label("Frequency", systemImage: "waveform.path.ecg")
Spacer()
TextField("Frequency Override", value: $overrideFrequency, formatter: floatFormatter)
.toolbar {
ToolbarItemGroup(placement: .keyboard) {
Button("dismiss.keyboard") {
focusedField = nil
}
.font(.subheadline)
}
}
.keyboardType(.decimalPad)
.scrollDismissesKeyboard(.immediately)
.focused($focusedField, equals: .frequencyOverride)
}
HStack {
Image(systemName: "antenna.radiowaves.left.and.right")
.foregroundColor(.accentColor)
Stepper("\(txPower)db Transmit Power", value: $txPower, in: 0...30, step: 1)
.padding(5)
}
}
}
}
}
.disabled(bleManager.connectedPeripheral == nil)
HStack {
Button {
isPresentingSaveConfirm = true
} label: {
2022-12-12 20:35:38 -08:00
Label("save", systemImage: "square.and.arrow.down")
}
.disabled(bleManager.connectedPeripheral == nil || !hasChanges)
.buttonStyle(.bordered)
.buttonBorderShape(.capsule)
.controlSize(.large)
.padding()
.confirmationDialog(
2022-12-30 11:08:59 -08:00
"are.you.sure",
2022-09-23 21:41:07 -07:00
isPresented: $isPresentingSaveConfirm,
titleVisibility: .visible
) {
Button("Save User Config to \(node?.user?.longName ?? "Unknown")?") {
2023-03-06 10:33:18 -08:00
2023-03-04 08:52:40 -08:00
let connectedUser = getUser(id: bleManager.connectedPeripheral?.num ?? -1, context: context)
let connectedNode = getNodeInfo(id: bleManager.connectedPeripheral?.num ?? -1, context: context)
2023-03-05 14:40:07 -08:00
if node != nil && connectedNode != nil {
2023-03-06 10:33:18 -08:00
2023-02-09 22:59:39 -08:00
if !isLicensed {
var u = User()
u.shortName = shortName
u.longName = longName
let adminMessageId = bleManager.saveUser(config: u, fromUser: connectedUser, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
if adminMessageId > 0 {
hasChanges = false
goBack()
}
} else {
var ham = HamParameters()
2023-03-06 10:33:18 -08:00
// ham.shortName = shortName
2023-02-09 22:59:39 -08:00
ham.callSign = longName
ham.txPower = Int32(txPower)
ham.frequency = overrideFrequency
let adminMessageId = bleManager.saveLicensedUser(ham: ham, fromUser: connectedUser, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
if adminMessageId > 0 {
hasChanges = false
goBack()
}
}
}
}
2022-09-23 21:41:07 -07:00
} message: {
2022-12-30 11:08:59 -08:00
Text("config.save.confirm")
}
}
Spacer()
}
.navigationTitle("User Config")
.navigationBarItems(trailing:
2023-02-09 22:59:39 -08:00
ZStack {
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "????")
})
.onAppear {
2022-11-12 08:48:01 -08:00
self.bleManager.context = context
2023-02-09 22:59:39 -08:00
self.shortName = node?.user?.shortName ?? ""
self.longName = node?.user?.longName ?? ""
self.isLicensed = node?.user?.isLicensed ?? false
self.txPower = Int(node?.loRaConfig?.txPower ?? 0)
self.overrideFrequency = node?.loRaConfig?.overrideFrequency ?? 0.00
2022-11-12 08:48:01 -08:00
self.hasChanges = false
}
.onChange(of: shortName) { newShort in
2022-07-11 15:43:25 -07:00
if node != nil && node!.user != nil {
if newShort != node?.user!.shortName { hasChanges = true }
}
}
.onChange(of: longName) { newLong in
2022-07-11 15:43:25 -07:00
if node != nil && node!.user != nil {
if newLong != node?.user!.longName { hasChanges = true }
}
}
2023-02-08 09:42:07 -08:00
.onChange(of: isLicensed) { newIsLicensed in
2023-02-09 22:59:39 -08:00
if node != nil && node!.user != nil {
if newIsLicensed != node?.user!.isLicensed { hasChanges = true }
2023-02-08 09:42:07 -08:00
}
}
2023-03-06 10:33:18 -08:00
.onChange(of: overrideFrequency) { _ in
// hasChanges = true
2023-02-09 22:59:39 -08:00
}
2023-03-06 10:33:18 -08:00
.onChange(of: txPower) { _ in
// hasChanges = true
2023-02-09 22:59:39 -08:00
}
}
}