2022-06-27 08:30:49 -07:00
//
// U s e r . s w i f t
// M e s h t a s t i c A p p l e
//
// C o p y r i g h t ( c ) G a r t h V a n d e r H o u w e n 6 / 2 7 / 2 2 .
//
import SwiftUI
2023-02-09 22:59:39 -08:00
import CoreData
2022-06-27 08:30:49 -07:00
struct UserConfig : View {
2023-03-06 10:33:18 -08:00
2022-06-27 08:30:49 -07:00
@ Environment ( \ . managedObjectContext ) var context
@ EnvironmentObject var bleManager : BLEManager
2022-12-09 18:19:00 -08:00
@ Environment ( \ . dismiss ) private var goBack
2023-03-06 10:33:18 -08:00
2022-07-07 00:29:52 -07: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
2022-06-27 08:30:49 -07:00
@ State private var isPresentingFactoryResetConfirm : Bool = false
@ State private var isPresentingSaveConfirm : Bool = false
@ State var hasChanges = false
@ State var shortName = " "
@ State var longName = " "
2023-02-04 20:44:24 -08:00
@ 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
2022-06-27 08:30:49 -07:00
var body : some View {
2023-03-06 10:33:18 -08:00
2022-06-27 08:30:49 -07:00
VStack {
Form {
2023-02-04 20:44:24 -08:00
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
// O n l y m e s s w i t h t h e v a l u e i f i t i s t o o b i g
2023-02-15 14:52:49 -08:00
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 ) {
// S e t t h e l o n g N a m e b a c k t o t h e l a s t p l a c e w h e r e i t w a s t h e r i g h t s i z e
longName = maxBytesString
}
2022-06-27 08:30:49 -07:00
}
2023-02-09 22:59:39 -08:00
} )
}
. keyboardType ( . default )
. disableAutocorrection ( true )
2023-02-15 14:52:49 -08:00
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
2022-06-27 08:30:49 -07:00
let totalBytes = shortName . utf8 . count
// O n l y m e s s w i t h t h e v a l u e i f i t i s t o o b i g
2022-07-01 10:57:54 -07:00
if totalBytes > 4 {
let firstNBytes = Data ( shortName . utf8 . prefix ( 4 ) )
2022-06-27 08:30:49 -07:00
if let maxBytesString = String ( data : firstNBytes , encoding : String . Encoding . utf8 ) {
// S e t t h e s h o r t N a m e b a c k t o t h e l a s t p l a c e w h e r e i t w a s t h e r i g h t s i z e
shortName = maxBytesString
}
}
} )
. foregroundColor ( . gray )
}
2023-05-05 17:13:35 -07:00
. keyboardType ( . default )
2022-06-27 08:30:49 -07:00
. 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
// O n l y m a n a g e h a m m o d e f o r t h e l o c a l l y c o n n e c t e d n o d e
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 )
}
}
2023-02-04 20:44:24 -08:00
}
2022-06-27 08:30:49 -07:00
}
}
. disabled ( bleManager . connectedPeripheral = = nil )
HStack {
Button {
isPresentingSaveConfirm = true
} label : {
2022-12-12 20:35:38 -08:00
Label ( " save " , systemImage : " square.and.arrow.down " )
2022-06-27 08:30:49 -07:00
}
. 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
2022-06-27 08:30:49 -07:00
) {
2023-01-19 22:04:18 -08:00
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
// h a m . s h o r t N a m e = s h o r t N a m e
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 ( )
}
2023-02-06 18:45:03 -08:00
}
2022-06-27 08:30:49 -07:00
}
}
2022-09-23 21:41:07 -07:00
} message : {
2022-12-30 11:08:59 -08:00
Text ( " config.save.confirm " )
2022-06-27 08:30:49 -07:00
}
}
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 : " ???? " )
2022-06-27 08:30:49 -07:00
} )
. 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
2022-06-27 08:30:49 -07:00
}
. 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 }
2022-06-27 08:30:49 -07:00
}
}
. 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 }
2022-06-27 08:30:49 -07:00
}
}
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
// h a s C h a n g e s = t r u e
2023-02-09 22:59:39 -08:00
}
2023-03-06 10:33:18 -08:00
. onChange ( of : txPower ) { _ in
// h a s C h a n g e s = t r u e
2023-02-09 22:59:39 -08:00
}
2022-06-27 08:30:49 -07:00
}
}