From a5d4f62ca43760b5bcce84319cfe52aaab139b0f Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Wed, 21 Feb 2024 20:41:27 -0800 Subject: [PATCH] Clean up descriptive text to be the same font and color on all the config views --- Meshtastic/Views/Settings/AppSettings.swift | 2 +- .../Views/Settings/Config/DeviceConfig.swift | 62 +++---- .../Views/Settings/Config/DisplayConfig.swift | 151 ++++++++++-------- .../Views/Settings/Config/LoRaConfig.swift | 97 ++++++----- .../Views/Settings/Config/NetworkConfig.swift | 27 ++-- .../Settings/Config/PositionConfig.swift | 105 +++++++----- .../Views/Settings/Config/PowerConfig.swift | 103 +++++++----- Meshtastic/Views/Settings/UserConfig.swift | 92 ++++++----- en.lproj/Localizable.strings | 2 +- 9 files changed, 364 insertions(+), 277 deletions(-) diff --git a/Meshtastic/Views/Settings/AppSettings.swift b/Meshtastic/Views/Settings/AppSettings.swift index 885379e5..9e559b62 100644 --- a/Meshtastic/Views/Settings/AppSettings.swift +++ b/Meshtastic/Views/Settings/AppSettings.swift @@ -26,7 +26,7 @@ struct AppSettings: View { .toggleStyle(SwitchToggleStyle(tint: .accentColor)) if provideLocation { Toggle(isOn: $enableSmartPosition) { - Label("appsettings.smartposition", systemImage: "brain.fill") + Label("appsettings.smartposition", systemImage: "brain") } .toggleStyle(SwitchToggleStyle(tint: .accentColor)) VStack { diff --git a/Meshtastic/Views/Settings/Config/DeviceConfig.swift b/Meshtastic/Views/Settings/Config/DeviceConfig.swift index d6971ba1..6a06d0ed 100644 --- a/Meshtastic/Views/Settings/Config/DeviceConfig.swift +++ b/Meshtastic/Views/Settings/Config/DeviceConfig.swift @@ -33,26 +33,28 @@ struct DeviceConfig: View { ConfigHeader(title: "Device", config: \.deviceConfig, node: node, onAppear: setDeviceValues) Section(header: Text("options")) { - Picker("Device Role", selection: $deviceRole ) { - ForEach(DeviceRoles.allCases) { dr in - Text(dr.name) + VStack(alignment: .leading) { + Picker("Device Role", selection: $deviceRole ) { + ForEach(DeviceRoles.allCases) { dr in + Text(dr.name) + } } + .pickerStyle(DefaultPickerStyle()) + Text(DeviceRoles(rawValue: deviceRole)?.description ?? "") + .foregroundColor(.gray) + .font(.caption) } - .pickerStyle(DefaultPickerStyle()) - .padding(.top, 10) - Text(DeviceRoles(rawValue: deviceRole)?.description ?? "") - .foregroundColor(.gray) - .font(.caption) - Picker("Rebroadcast Mode", selection: $rebroadcastMode ) { - ForEach(RebroadcastModes.allCases) { rm in - Text(rm.name) + VStack(alignment: .leading) { + Picker("Rebroadcast Mode", selection: $rebroadcastMode ) { + ForEach(RebroadcastModes.allCases) { rm in + Text(rm.name) + } } + .pickerStyle(DefaultPickerStyle()) + Text(RebroadcastModes(rawValue: rebroadcastMode)?.description ?? "") + .foregroundColor(.gray) + .font(.caption) } - .pickerStyle(DefaultPickerStyle()) - .padding(.top, 10) - Text(RebroadcastModes(rawValue: rebroadcastMode)?.description ?? "") - .foregroundColor(.gray) - .font(.caption) Picker("Node Info Broadcast Interval", selection: $nodeInfoBroadcastSecs ) { ForEach(UpdateIntervals.allCases) { ui in if ui.rawValue >= 3600 { @@ -61,20 +63,24 @@ struct DeviceConfig: View { } } .pickerStyle(DefaultPickerStyle()) - .padding(.top, 10) - Toggle(isOn: $doubleTapAsButtonPress) { - Label("Double Tap as Button", systemImage: "hand.tap") + VStack(alignment: .leading) { + Toggle(isOn: $doubleTapAsButtonPress) { + Label("Double Tap as Button", systemImage: "hand.tap") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + Text("Treat double tap on supported accelerometers as a user button press.") + .foregroundColor(.gray) + .font(.caption) } - .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - Text("Treat double tap on supported accelerometers as a user button press.") - .font(.caption) - - Toggle(isOn: $isManaged) { - Label("Managed Device", systemImage: "gearshape.arrow.triangle.2.circlepath") + VStack(alignment: .leading) { + Toggle(isOn: $isManaged) { + Label("Managed Device", systemImage: "gearshape.arrow.triangle.2.circlepath") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + Text("Enabling Managed mode will restrict access to all radio configurations, such as short/long names, regions, channels, modules, etc. and will only be accessible through the Admin channel. To avoid being locked out, make sure the Admin channel is working properly before enabling it.") + .foregroundColor(.gray) + .font(.caption) } - .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - Text("Enabling Managed mode will restrict access to all radio configurations, such as short/long names, regions, channels, modules, etc. and will only be accessible through the Admin channel. To avoid being locked out, make sure the Admin channel is working properly before enabling it.") - .font(.caption) } Section(header: Text("Debug")) { Toggle(isOn: $serialEnabled) { diff --git a/Meshtastic/Views/Settings/Config/DisplayConfig.swift b/Meshtastic/Views/Settings/Config/DisplayConfig.swift index ebdfa9b7..7333fe55 100644 --- a/Meshtastic/Views/Settings/Config/DisplayConfig.swift +++ b/Meshtastic/Views/Settings/Config/DisplayConfig.swift @@ -31,83 +31,108 @@ struct DisplayConfig: View { ConfigHeader(title: "Display", config: \.displayConfig, node: node, onAppear: setDisplayValues) Section(header: Text("Device Screen")) { - Picker("Display Mode", selection: $displayMode ) { - ForEach(DisplayModes.allCases) { dm in - Text(dm.description) + VStack(alignment: .leading) { + Picker("Display Mode", selection: $displayMode ) { + ForEach(DisplayModes.allCases) { dm in + Text(dm.description) + } } + .pickerStyle(DefaultPickerStyle()) + Text("Override automatic OLED screen detection.") + .foregroundColor(.gray) + .font(.caption) } - .pickerStyle(DefaultPickerStyle()) - Text("Override automatic OLED screen detection.") - .font(.caption) - - Toggle(isOn: $compassNorthTop) { - - Label("Always point north", systemImage: "location.north.circle") - } - .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - Text("The compass heading on the screen outside of the circle will always point north.") - .font(.caption) - Toggle(isOn: $wakeOnTapOrMotion) { - Label("Wake Screen on tap or motion", systemImage: "gyroscope") - } - .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - Text("Requires that there be an accelerometer on your device.") - .font(.caption) - Toggle(isOn: $flipScreen) { - - Label("Flip Screen", systemImage: "pip.swap") - } - .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - Text("Flip screen vertically") - .font(.caption) - - Picker("OLED Type", selection: $oledType ) { - ForEach(OledTypes.allCases) { ot in - Text(ot.description) + VStack(alignment: .leading) { + + Toggle(isOn: $compassNorthTop) { + + Label("Always point north", systemImage: "location.north.circle") } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + Text("The compass heading on the screen outside of the circle will always point north.") + .foregroundColor(.gray) + .font(.caption) + } + + VStack(alignment: .leading) { + Toggle(isOn: $wakeOnTapOrMotion) { + Label("Wake Screen on tap or motion", systemImage: "gyroscope") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + Text("Requires that there be an accelerometer on your device.") + .foregroundColor(.gray) + .font(.caption) + } + + VStack(alignment: .leading) { + Toggle(isOn: $flipScreen) { + + Label("Flip Screen", systemImage: "pip.swap") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + Text("Flip screen vertically") + .foregroundColor(.gray) + .font(.caption) + } + + VStack(alignment: .leading) { + Picker("OLED Type", selection: $oledType ) { + ForEach(OledTypes.allCases) { ot in + Text(ot.description) + } + } + .pickerStyle(DefaultPickerStyle()) + Text("Override automatic OLED screen detection.") + .foregroundColor(.gray) + .font(.caption) } - .pickerStyle(DefaultPickerStyle()) - Text("Override automatic OLED screen detection.") - .font(.caption) } Section(header: Text("Timing & Format")) { - Picker("Screen on for", selection: $screenOnSeconds ) { - ForEach(ScreenOnIntervals.allCases) { soi in - Text(soi.description) + VStack(alignment: .leading) { + Picker("Screen on for", selection: $screenOnSeconds ) { + ForEach(ScreenOnIntervals.allCases) { soi in + Text(soi.description) + } } + .pickerStyle(DefaultPickerStyle()) + Text("How long the screen remains on after the user button is pressed or messages are received.") + .foregroundColor(.gray) + .font(.caption) } - .pickerStyle(DefaultPickerStyle()) - Text("How long the screen remains on after the user button is pressed or messages are received.") - .font(.caption) - - Picker("Carousel Interval", selection: $screenCarouselInterval ) { - ForEach(ScreenCarouselIntervals.allCases) { sci in - Text(sci.description) + VStack(alignment: .leading) { + Picker("Carousel Interval", selection: $screenCarouselInterval ) { + ForEach(ScreenCarouselIntervals.allCases) { sci in + Text(sci.description) + } } + .pickerStyle(DefaultPickerStyle()) + Text("Automatically toggles to the next page on the screen like a carousel, based the specified interval.") + .foregroundColor(.gray) + .font(.caption) } - .pickerStyle(DefaultPickerStyle()) - Text("Automatically toggles to the next page on the screen like a carousel, based the specified interval.") - .font(.caption) - - Picker("GPS Format", selection: $gpsFormat ) { - ForEach(GpsFormats.allCases) { lu in - Text(lu.description) + VStack(alignment: .leading) { + Picker("GPS Format", selection: $gpsFormat ) { + ForEach(GpsFormats.allCases) { lu in + Text(lu.description) + } } + .pickerStyle(DefaultPickerStyle()) + + Text("The format used to display GPS coordinates on the device screen.") + .foregroundColor(.gray) + .font(.caption) } - .pickerStyle(DefaultPickerStyle()) - - Text("The format used to display GPS coordinates on the device screen.") - .font(.caption) - .listRowSeparator(.visible) - - Picker("Display Units", selection: $units ) { - ForEach(Units.allCases) { un in - Text(un.description) + VStack(alignment: .leading) { + Picker("Display Units", selection: $units ) { + ForEach(Units.allCases) { un in + Text(un.description) + } } + .pickerStyle(DefaultPickerStyle()) + Text("Units displayed on the device screen") + .foregroundColor(.gray) + .font(.caption) } - .pickerStyle(DefaultPickerStyle()) - Text("Units displayed on the device screen") - .font(.caption) } } .disabled(self.bleManager.connectedPeripheral == nil || node?.displayConfig == nil) diff --git a/Meshtastic/Views/Settings/Config/LoRaConfig.swift b/Meshtastic/Views/Settings/Config/LoRaConfig.swift index e51e3a99..b1e692c0 100644 --- a/Meshtastic/Views/Settings/Config/LoRaConfig.swift +++ b/Meshtastic/Views/Settings/Config/LoRaConfig.swift @@ -57,32 +57,37 @@ struct LoRaConfig: View { Section(header: Text("Options")) { - Picker("Region", selection: $region ) { - ForEach(RegionCodes.allCases) { r in - Text(r.description) + VStack(alignment: .leading) { + Picker("Region", selection: $region ) { + ForEach(RegionCodes.allCases) { r in + Text(r.description) + } } + .pickerStyle(DefaultPickerStyle()) + .fixedSize() + + Text("The region where you will be using your radios.") + .foregroundColor(.gray) + .font(.caption) } - .pickerStyle(DefaultPickerStyle()) - .fixedSize() - - Text("The region where you will be using your radios.") - .font(.caption) - Toggle(isOn: $usePreset) { Label("Use Preset", systemImage: "list.bullet.rectangle") } .toggleStyle(SwitchToggleStyle(tint: .accentColor)) if usePreset { - Picker("Presets", selection: $modemPreset ) { - ForEach(ModemPresets.allCases) { m in - Text(m.description) + VStack(alignment: .leading) { + Picker("Presets", selection: $modemPreset ) { + ForEach(ModemPresets.allCases) { m in + Text(m.description) + } } + .pickerStyle(DefaultPickerStyle()) + .fixedSize() + Text("Available modem presets, default is Long Fast.") + .foregroundColor(.gray) + .font(.caption) } - .pickerStyle(DefaultPickerStyle()) - .fixedSize() - Text("Available modem presets, default is Long Fast.") - .font(.caption) } } Section(header: Text("Advanced")) { @@ -123,36 +128,40 @@ struct LoRaConfig: View { } } } - - Picker("Number of hops", selection: $hopLimit) { - ForEach(1..<8) { - Text("\($0)") - .tag($0 == 0 ? 3 : $0) - } - } - .pickerStyle(DefaultPickerStyle()) - Text("Sets the maximum number of hops, default is 3. Increasing hops also increases congestion and should be used carefully.") - .font(.caption) - - HStack { - Text("Frequency Slot") - .fixedSize() - TextField("Frequency Slot", value: $channelNum, formatter: formatter) - .toolbar { - ToolbarItemGroup(placement: .keyboard) { - Button("dismiss.keyboard") { - focusedField = nil - } - .font(.subheadline) - } + VStack(alignment: .leading) { + Picker("Number of hops", selection: $hopLimit) { + ForEach(1..<8) { + Text("\($0)") + .tag($0 == 0 ? 3 : $0) } - .keyboardType(.decimalPad) - .scrollDismissesKeyboard(.immediately) - .focused($focusedField, equals: .channelNum) - .disabled(overrideFrequency > 0.0) + } + .pickerStyle(DefaultPickerStyle()) + Text("Sets the maximum number of hops, default is 3. Increasing hops also increases congestion and should be used carefully.") + .foregroundColor(.gray) + .font(.caption) + } + VStack(alignment: .leading) { + HStack { + Text("Frequency Slot") + .fixedSize() + TextField("Frequency Slot", value: $channelNum, formatter: formatter) + .toolbar { + ToolbarItemGroup(placement: .keyboard) { + Button("dismiss.keyboard") { + focusedField = nil + } + .font(.subheadline) + } + } + .keyboardType(.decimalPad) + .scrollDismissesKeyboard(.immediately) + .focused($focusedField, equals: .channelNum) + .disabled(overrideFrequency > 0.0) + } + Text("This determines the actual frequency you are transmitting on in the band. If set to 0 this value will be calculated automatically based on the primary channel name.") + .foregroundColor(.gray) + .font(.caption) } - Text("This determines the actual frequency you are transmitting on in the band. If set to 0 this value will be calculated automatically based on the primary channel name.") - .font(.caption) Toggle(isOn: $rxBoostedGain) { Label("RX Boosted Gain", systemImage: "waveform.badge.plus") } diff --git a/Meshtastic/Views/Settings/Config/NetworkConfig.swift b/Meshtastic/Views/Settings/Config/NetworkConfig.swift index 2d4d9457..471b6ca4 100644 --- a/Meshtastic/Views/Settings/Config/NetworkConfig.swift +++ b/Meshtastic/Views/Settings/Config/NetworkConfig.swift @@ -31,10 +31,15 @@ struct NetworkConfig: View { if (node != nil && node?.metadata?.hasWifi ?? false) { Section(header: Text("WiFi Options")) { - Toggle(isOn: $wifiEnabled) { - Label("enabled", systemImage: "wifi") + VStack(alignment: .leading) { + Toggle(isOn: $wifiEnabled) { + Label("enabled", systemImage: "wifi") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + Text("Enabling WiFi will disable the bluetooth connection to the app.") + .foregroundColor(.gray) + .font(.caption) } - .toggleStyle(SwitchToggleStyle(tint: .accentColor)) HStack { Label("ssid", systemImage: "network") TextField("ssid", text: $wifiSsid) @@ -77,18 +82,20 @@ struct NetworkConfig: View { .foregroundColor(.gray) } .keyboardType(.default) - Text("Enabling WiFi will disable the bluetooth connection to the app.") - .font(.callout) } } if (node != nil && node?.metadata?.hasEthernet ?? false) { Section(header: Text("Ethernet Options")) { - Toggle(isOn: $ethEnabled) { - Label("enabled", systemImage: "network") + + VStack(alignment: .leading) { + Toggle(isOn: $ethEnabled) { + Label("enabled", systemImage: "network") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + Text("Enabling Ethernet will disable the bluetooth connection to the app.") + .foregroundColor(.gray) + .font(.caption) } - .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - Text("Enabling Ethernet will disable the bluetooth connection to the app.") - .font(.callout) } } } diff --git a/Meshtastic/Views/Settings/Config/PositionConfig.swift b/Meshtastic/Views/Settings/Config/PositionConfig.swift index 710ad601..ae1ff133 100644 --- a/Meshtastic/Views/Settings/Config/PositionConfig.swift +++ b/Meshtastic/Views/Settings/Config/PositionConfig.swift @@ -79,44 +79,55 @@ struct PositionConfig: View { Section(header: Text("Position Packet")) { - Picker("Interval", selection: $positionBroadcastSeconds) { - ForEach(UpdateIntervals.allCases) { at in - if at.rawValue >= 900 { - Text(at.description) - } - } - } - .pickerStyle(DefaultPickerStyle()) - Text("The maximum interval that can elapse without a node broadcasting a position") - .font(.caption) - Toggle(isOn: $smartPositionEnabled) { - Label("Smart Position", systemImage: "location.fill.viewfinder") - } - .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - if smartPositionEnabled { - Picker("Minimum Broadcast Interval", selection: $broadcastSmartMinimumIntervalSecs) { + VStack(alignment: .leading) { + Picker("Broadcast Interval", selection: $positionBroadcastSeconds) { ForEach(UpdateIntervals.allCases) { at in - Text(at.description) - } - } - .pickerStyle(DefaultPickerStyle()) - Text("The fastest that position updates will be sent if the minimum distance has been satisfied") - .font(.caption) - Picker("Minimum Distance", selection: $broadcastSmartMinimumDistance) { - ForEach(10..<151) { - if $0 == 0 { - Text("unset") - } else { - if $0.isMultiple(of: 5) { - Text("\($0)") - .tag($0) - } + if at.rawValue >= 900 { + Text(at.description) } } } .pickerStyle(DefaultPickerStyle()) - Text("The minimum distance change in meters to be considered for a smart position broadcast.") - .font(.caption) + Text("The maximum interval that can elapse without a node broadcasting a position") + .foregroundColor(.gray) + .font(.caption) + } + + Toggle(isOn: $smartPositionEnabled) { + Label("Smart Position", systemImage: "brain") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + + if smartPositionEnabled { + VStack(alignment: .leading) { + Picker("Minimum Interval", selection: $broadcastSmartMinimumIntervalSecs) { + ForEach(UpdateIntervals.allCases) { at in + Text(at.description) + } + } + .pickerStyle(DefaultPickerStyle()) + Text("The fastest that position updates will be sent if the minimum distance has been satisfied") + .foregroundColor(.gray) + .font(.caption) + } + VStack(alignment: .leading) { + Picker("Minimum Distance", selection: $broadcastSmartMinimumDistance) { + ForEach(10..<151) { + if $0 == 0 { + Text("unset") + } else { + if $0.isMultiple(of: 5) { + Text("\($0)") + .tag($0) + } + } + } + } + .pickerStyle(DefaultPickerStyle()) + Text("The minimum distance change in meters to be considered for a smart position broadcast.") + .foregroundColor(.gray) + .font(.caption) + } } } Section(header: Text("Device GPS")) { @@ -131,27 +142,33 @@ struct PositionConfig: View { .padding(.bottom, 5) if gpsMode == 1 { - Picker("Update Interval", selection: $gpsUpdateInterval) { - ForEach(GpsUpdateIntervals.allCases) { ui in - Text(ui.description) + VStack(alignment: .leading) { + Picker("Update Interval", selection: $gpsUpdateInterval) { + ForEach(GpsUpdateIntervals.allCases) { ui in + Text(ui.description) + } } + Text("How often should we try to get a GPS position.") + .foregroundColor(.gray) + .font(.caption) } - Text("How often should we try to get a GPS position.") - .font(.caption) } else { - Toggle(isOn: $fixedPosition) { - Label("Fixed Position", systemImage: "location.square.fill") + VStack(alignment: .leading) { + Toggle(isOn: $fixedPosition) { + Label("Fixed Position", systemImage: "location.square.fill") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + Text("If enabled your current phone location will be sent to the device and will broadcast over the mesh on the position interval. Fixed position will always use the most recent position the device has.") + .foregroundColor(.gray) + .font(.caption) } - .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - Text("If enabled your current phone location will be sent to the device and will broadcast over the mesh on the position interval. Fixed position will always use the most recent position the device has.") - .font(.caption) } } Section(header: Text("Position Flags")) { Text("Optional fields to include when assembling position messages. the more fields are included, the larger the message will be - leading to longer airtime and a higher risk of packet loss") + .foregroundColor(.gray) .font(.caption) - .listRowSeparator(.visible) Toggle(isOn: $includeAltitude) { Label("Altitude", systemImage: "arrow.up") diff --git a/Meshtastic/Views/Settings/Config/PowerConfig.swift b/Meshtastic/Views/Settings/Config/PowerConfig.swift index 08ca742a..c23d1a28 100644 --- a/Meshtastic/Views/Settings/Config/PowerConfig.swift +++ b/Meshtastic/Views/Settings/Config/PowerConfig.swift @@ -17,7 +17,9 @@ struct PowerConfig: View { @State private var waitBluetoothSecs = 60 @State private var lsSecs = 300 @State private var minWakeSecs = 10 - + + @State private var currentDevice: DeviceHardware? + @State private var hasChanges: Bool = false @FocusState private var isFocused: Bool @@ -25,14 +27,16 @@ struct PowerConfig: View { Form { ConfigHeader(title: "Power", config: \.powerConfig, node: node, onAppear: setPowerValues) - Section(header: Text("power")) { + Section { + Toggle(isOn: $isPowerSaving) { - Text("power.save") + Text("power.solar") } .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - } + Text("For use when powered from a low-current source in addition to the battery, minimizes power consumption as much as possible even if the deviced appears to be powered.") + .foregroundColor(.gray) + .font(.caption) - Section { Toggle(isOn: $shutdownOnPowerLoss) { Text("power.shutdown.on.power.loss") } @@ -47,53 +51,56 @@ struct PowerConfig: View { .pickerStyle(DefaultPickerStyle()) } } header: { - Text("Shutdown") + Text("power") } + if currentDevice?.architecture == .esp32 || currentDevice?.architecture == .esp32S3 { + + Section { + Toggle(isOn: $adcOverride) { + Text("power.adc.override") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - Section { - Toggle(isOn: $adcOverride) { - Text("power.adc.override") - } - .toggleStyle(SwitchToggleStyle(tint: .accentColor)) - - if adcOverride { - HStack { - Text("power.adc.multiplier") - Spacer() - FloatField(title: "power.adc.multiplier", number: $adcMultiplier) { - (2.0 ... 6.0).contains($0) + if adcOverride { + HStack { + Text("power.adc.multiplier") + Spacer() + FloatField(title: "power.adc.multiplier", number: $adcMultiplier) { + (2.0 ... 6.0).contains($0) + } + .focused($isFocused) + Spacer() } - .focused($isFocused) - Spacer() } + } header: { + Text("Battery") } - } header: { - Text("Battery") - } - - Section { - Picker("power.wait.bluetooth.secs", selection: $waitBluetoothSecs) { - ForEach(PowerIntervals.allCases) { - Text($0.description) + + Section { + Picker("power.wait.bluetooth.secs", selection: $waitBluetoothSecs) { + ForEach(PowerIntervals.allCases) { + Text($0.description) + } } - } - .pickerStyle(DefaultPickerStyle()) - - Picker("power.ls.secs", selection: $lsSecs) { - ForEach(PowerIntervals.allCases) { - Text($0.description) + .pickerStyle(DefaultPickerStyle()) + + Picker("power.ls.secs", selection: $lsSecs) { + ForEach(PowerIntervals.allCases) { + Text($0.description) + } } - } - .pickerStyle(DefaultPickerStyle()) - - Picker("power.min.wake.secs", selection: $minWakeSecs) { - ForEach(PowerIntervals.allCases) { - Text($0.description) + .pickerStyle(DefaultPickerStyle()) + + Picker("power.min.wake.secs", selection: $minWakeSecs) { + ForEach(PowerIntervals.allCases) { + Text($0.description) + } } + .pickerStyle(DefaultPickerStyle()) + + } header: { + Text("Sleep") } - .pickerStyle(DefaultPickerStyle()) - } header: { - Text("Sleep") } } .disabled(self.bleManager.connectedPeripheral == nil || node?.powerConfig == nil) @@ -118,6 +125,16 @@ struct PowerConfig: View { if self.bleManager.context == nil { self.bleManager.context = context } + + Api().loadDeviceHardwareData { (hw) in + for device in hw { + let currentHardware = node?.user?.hwModel ?? "UNSET" + let deviceString = device.hwModelSlug.replacingOccurrences(of: "_", with: "") + if deviceString == currentHardware { + currentDevice = device + } + } + } setPowerValues() // Need to request a Power config from the remote node before allowing changes diff --git a/Meshtastic/Views/Settings/UserConfig.swift b/Meshtastic/Views/Settings/UserConfig.swift index 34337470..f7c5b2d9 100644 --- a/Meshtastic/Views/Settings/UserConfig.swift +++ b/Meshtastic/Views/Settings/UserConfig.swift @@ -42,53 +42,59 @@ struct UserConfig: View { VStack { Form { Section(header: Text("User Details")) { - HStack { - Label(isLicensed ? "Call Sign" : "Long Name", systemImage: "person.crop.rectangle.fill") + + VStack(alignment: .leading) { + HStack { + Label(isLicensed ? "Call Sign" : "Long Name", systemImage: "person.crop.rectangle.fill") + + TextField("Long Name", text: $longName) + .onChange(of: longName, perform: { _ in + let totalBytes = longName.utf8.count + // Only mess with the value if it is too big + if totalBytes > (isLicensed ? 6 : 36) { + let firstNBytes = Data(longName.utf8.prefix(isLicensed ? 6 : 36)) + 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 + } + } + }) + } + .keyboardType(.default) + .disableAutocorrection(true) + if longName.isEmpty && isLicensed { + Label("Call Sign must not be empty", systemImage: "exclamationmark.square") + .foregroundColor(.red) + } + Text("\(String(isLicensed ? "Call Sign" : "Long Name")) can be up to \(isLicensed ? "8" : "36") bytes long.") + .foregroundColor(.gray) + .font(.caption) - TextField("Long Name", text: $longName) - .onChange(of: longName, perform: { _ in - let totalBytes = longName.utf8.count - // Only mess with the value if it is too big - if totalBytes > (isLicensed ? 6 : 36) { - let firstNBytes = Data(longName.utf8.prefix(isLicensed ? 6 : 36)) - 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 + } + VStack(alignment: .leading) { + HStack { + Label("Short Name", systemImage: "circlebadge.fill") + TextField("Short Name", text: $shortName) + .foregroundColor(.gray) + .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 + } } - } - }) - } - .keyboardType(.default) - .disableAutocorrection(true) - if longName.isEmpty && isLicensed { - Label("Call Sign must not be empty", systemImage: "exclamationmark.square") - .foregroundColor(.red) - } - Text("\(String(isLicensed ? "Call Sign" : "Long Name")) can be up to \(isLicensed ? "8" : "36") bytes long.") - .font(.caption2) - - HStack { - Label("Short Name", systemImage: "circlebadge.fill") - TextField("Short Name", text: $shortName) - .foregroundColor(.gray) - .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) + } + .keyboardType(.default) + .disableAutocorrection(true) + 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.") .foregroundColor(.gray) + .font(.caption) } - .keyboardType(.default) - .disableAutocorrection(true) - 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) - // Only manage ham mode for the locally connected node if node?.num ?? 0 > 0 && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0 { Toggle(isOn: $isLicensed) { diff --git a/en.lproj/Localizable.strings b/en.lproj/Localizable.strings index f6c422fe..5a4fe9a1 100644 --- a/en.lproj/Localizable.strings +++ b/en.lproj/Localizable.strings @@ -239,7 +239,7 @@ "power.config"="Power Config"; "power.ls.secs"="Light Sleep Interval"; "power.min.wake.secs"="Minimum Wake Interval"; -"power.save"="Power Save"; +"power.solar"="Solar Powered"; "power.shutdown.on.power.loss"="Shutdown on Power Loss"; "power.shutdown.after.secs"="After"; "power.wait.bluetooth.secs"="Bluetooth Off After";