Add User Settings edit screen with long name and short name

This commit is contained in:
Garth Vander Houwen 2022-06-27 08:30:49 -07:00
parent 882b0c3d88
commit 2add04d326
4 changed files with 228 additions and 17 deletions

View file

@ -63,12 +63,13 @@
DDC2E15826CE248E0042C5E4 /* MeshtasticApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC2E15726CE248E0042C5E4 /* MeshtasticApp.swift */; };
DDC2E15C26CE248F0042C5E4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DDC2E15B26CE248F0042C5E4 /* Assets.xcassets */; };
DDC2E15F26CE248F0042C5E4 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DDC2E15E26CE248F0042C5E4 /* Preview Assets.xcassets */; };
DDC2E16F26CE248F0042C5E4 /* MeshtasticAppleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC2E16E26CE248F0042C5E4 /* MeshtasticAppleTests.swift */; };
DDC2E17A26CE248F0042C5E4 /* MeshtasticAppleUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC2E17926CE248F0042C5E4 /* MeshtasticAppleUITests.swift */; };
DDC2E16F26CE248F0042C5E4 /* MeshtasticTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC2E16E26CE248F0042C5E4 /* MeshtasticTests.swift */; };
DDC2E17A26CE248F0042C5E4 /* MeshtasticUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC2E17926CE248F0042C5E4 /* MeshtasticUITests.swift */; };
DDC2E18F26CE25FE0042C5E4 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC2E18E26CE25FE0042C5E4 /* ContentView.swift */; };
DDC2E1A726CEB3400042C5E4 /* LocationHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC2E1A626CEB3400042C5E4 /* LocationHelper.swift */; };
DDC3B274283F411B00AC321C /* LastHeardText.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC3B273283F411B00AC321C /* LastHeardText.swift */; };
DDC4D568275499A500A4208E /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDC4D567275499A500A4208E /* Persistence.swift */; };
DDCE4E2C2869F92900BE9F8F /* UserConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCE4E2B2869F92900BE9F8F /* UserConfig.swift */; };
DDCFF601285453A7005FA625 /* localonly.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCFF600285453A7005FA625 /* localonly.pb.swift */; };
DDD94A502845C8F5004A87A0 /* DateTimeText.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD94A4F2845C8F5004A87A0 /* DateTimeText.swift */; };
DDD9E4E4284B208E003777C5 /* UserEntityExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD9E4E3284B208E003777C5 /* UserEntityExtension.swift */; };
@ -163,6 +164,7 @@
DDC2E1A626CEB3400042C5E4 /* LocationHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationHelper.swift; sourceTree = "<group>"; };
DDC3B273283F411B00AC321C /* LastHeardText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LastHeardText.swift; sourceTree = "<group>"; };
DDC4D567275499A500A4208E /* Persistence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = "<group>"; };
DDCE4E2B2869F92900BE9F8F /* UserConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserConfig.swift; sourceTree = "<group>"; };
DDCFF600285453A7005FA625 /* localonly.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = localonly.pb.swift; sourceTree = "<group>"; };
DDD94A4F2845C8F5004A87A0 /* DateTimeText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateTimeText.swift; sourceTree = "<group>"; };
DDD9E4E3284B208E003777C5 /* UserEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserEntityExtension.swift; sourceTree = "<group>"; };
@ -242,6 +244,7 @@
DD8169FE272476C700F4AB02 /* LogDocument.swift */,
DD6B85A728009258000ACD6B /* ShareChannel.swift */,
DD61937A2863876A00E59241 /* Config */,
DDCE4E2B2869F92900BE9F8F /* UserConfig.swift */,
);
path = Settings;
sourceTree = "<group>";
@ -648,6 +651,7 @@
DDC3B274283F411B00AC321C /* LastHeardText.swift in Sources */,
DD2E65262767A01F00E45FC5 /* NodeDetail.swift in Sources */,
DDA6B2E928419CF2003E8C16 /* MeshPackets.swift in Sources */,
DDCE4E2C2869F92900BE9F8F /* UserConfig.swift in Sources */,
DD6193752862F6E600E59241 /* ExternalNotificationConfig.swift in Sources */,
DDD94A502845C8F5004A87A0 /* DateTimeText.swift in Sources */,
C9A88B57278B559900BD810A /* apponly.pb.swift in Sources */,
@ -668,7 +672,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
DDC2E16F26CE248F0042C5E4 /* MeshtasticAppleTests.swift in Sources */,
DDC2E16F26CE248F0042C5E4 /* MeshtasticTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View file

@ -825,6 +825,41 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph
return false
}
public func saveUser(config: User, destNum: Int64, wantResponse: Bool) -> Bool {
var adminPacket = AdminMessage()
adminPacket.setOwner = config
var meshPacket: MeshPacket = MeshPacket()
meshPacket.to = UInt32(connectedPeripheral.num)
meshPacket.from = 0 //UInt32(connectedPeripheral.num)
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
meshPacket.priority = MeshPacket.Priority.reliable
meshPacket.wantAck = wantResponse
meshPacket.hopLimit = 0
var dataMessage = DataMessage()
dataMessage.payload = try! adminPacket.serializedData()
dataMessage.portnum = PortNum.adminApp
meshPacket.decoded = dataMessage
var toRadio: ToRadio!
toRadio = ToRadio()
toRadio.packet = meshPacket
let binaryData: Data = try! toRadio.serializedData()
if connectedPeripheral!.peripheral.state == CBPeripheralState.connected {
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
return true
}
return false
}
public func sendFactoryReset(destNum: Int64, wantResponse: Bool) -> Bool {
var deviceConfig = Config.DeviceConfig()

View file

@ -13,8 +13,6 @@ struct Settings: View {
@EnvironmentObject var bleManager: BLEManager
@EnvironmentObject var userSettings: UserSettings
@FetchRequest(
sortDescriptors: [NSSortDescriptor(key: "lastHeard", ascending: false)],
animation: .default)
@ -52,6 +50,29 @@ struct Settings: View {
.listRowSeparator(.visible)
.fixedSize(horizontal: false, vertical: true)
NavigationLink {
UserConfig(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity())
} label: {
Image(systemName: "person.crop.rectangle.fill")
.symbolRenderingMode(.hierarchical)
Text("User")
}
.disabled(bleManager.connectedPeripheral == nil)
NavigationLink() {
LoRaConfig(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity())
} label: {
Image(systemName: "dot.radiowaves.left.and.right")
.symbolRenderingMode(.hierarchical)
Text("LoRa")
}
.disabled(bleManager.connectedPeripheral == nil)
NavigationLink {
DeviceConfig(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity())
} label: {
@ -71,18 +92,6 @@ struct Settings: View {
Text("Display (Device Screen)")
}
.disabled(bleManager.connectedPeripheral == nil)
NavigationLink() {
LoRaConfig(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity())
} label: {
Image(systemName: "dot.radiowaves.left.and.right")
.symbolRenderingMode(.hierarchical)
Text("LoRa")
}
.disabled(bleManager.connectedPeripheral == nil)
NavigationLink {
PositionConfig(node: nodes.first(where: { $0.num == connectedNodeNum }) ?? NodeInfoEntity())

View file

@ -0,0 +1,163 @@
//
// User.swift
// Meshtastic Apple
//
// Copyright (c) Garth Vander Houwen 6/27/22.
//
import SwiftUI
struct UserConfig: View {
@Environment(\.managedObjectContext) var context
@EnvironmentObject var bleManager: BLEManager
var node: NodeInfoEntity
@State private var isPresentingFactoryResetConfirm: Bool = false
@State private var isPresentingSaveConfirm: Bool = false
@State var initialLoad: Bool = true
@State var hasChanges = false
@State var shortName = ""
@State var longName = ""
var body: some View {
VStack {
Form {
Section(header: Text("USER DETAILS")) {
HStack {
Label("Long Name", systemImage: "person.crop.rectangle.fill")
TextField("Long Name", text: $longName)
.onChange(of: longName, perform: { value in
let totalBytes = longName.utf8.count
// Only mess with the value if it is too big
if totalBytes > 40 {
let firstNBytes = Data(longName.utf8.prefix(40))
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
}
}
})
.onChange(of: shortName, perform: { value in
let totalBytes = shortName.utf8.count
// Only mess with the value if it is too big
if totalBytes > 5 {
let firstNBytes = Data(shortName.utf8.prefix(5))
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)
}
.keyboardType(.asciiCapable)
.disableAutocorrection(true)
.listRowSeparator(.visible)
HStack {
Label("Short Name", systemImage: "circlebadge.fill")
TextField("Long Name", text: $shortName)
.foregroundColor(.gray)
}
.keyboardType(.asciiCapable)
.disableAutocorrection(true)
.listRowSeparator(.visible)
}
}
.disabled(bleManager.connectedPeripheral == nil)
HStack {
Button {
isPresentingSaveConfirm = true
} label: {
Label("Save", systemImage: "square.and.arrow.down")
}
.disabled(bleManager.connectedPeripheral == nil || !hasChanges)
.buttonStyle(.bordered)
.buttonBorderShape(.capsule)
.controlSize(.large)
.padding()
.confirmationDialog(
"Are you sure?",
isPresented: $isPresentingSaveConfirm
) {
Button("Save User Config to \(bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.longName : "Unknown")?") {
var u = User()
u.shortName = shortName
u.longName = longName
if bleManager.saveUser(config: u, destNum: bleManager.connectedPeripheral.num, wantResponse: false) {
// Should show a saved successfully alert once I know that to be true
// for now just disable the button after a successful save
hasChanges = false
} else {
}
}
}
}
Spacer()
}
.navigationTitle("User Config")
.navigationBarItems(trailing:
ZStack {
ConnectedDevice(bluetoothOn: bleManager.isSwitchedOn, deviceConnected: bleManager.connectedPeripheral != nil, name: (bleManager.connectedPeripheral != nil) ? bleManager.connectedPeripheral.shortName : "?????")
})
.onAppear {
if self.initialLoad{
self.bleManager.context = context
self.shortName = node.user!.shortName ?? ""
self.longName = node.user!.longName ?? ""
self.hasChanges = false
self.initialLoad = false
}
}
.onChange(of: shortName) { newShort in
if newShort != node.user!.shortName {
hasChanges = true
}
}
.onChange(of: longName) { newLong in
if newLong != node.user!.longName {
hasChanges = true
}
}
.navigationViewStyle(StackNavigationViewStyle())
}
}