فهرست منبع

TextFieldWithToolBar updates

* Updated various `NumberFormatter`s
* Remove `maxValue` from `TextFieldWithToolBar` calls
  * Otherwise, if `maxCarbs` is `250` and you accidentally type `300` instead of `30`, it would autocorrect to `250`
* Now does not change TextField at all if `newValue` exceeds `maximumFractionDigits` or `maximumIntegerDigits `
* Tapping the recommendation now updates the bolus textfield
* Disable AddMealPreset button if the dish's name and amount aren't entered
  * Limit length of dish name to 25 chars
* Only show decimal separator for manual glucose entry when using mmol/L
Mike Plante 1 سال پیش
والد
کامیت
f741c9b0e3

+ 20 - 1
Trio/Sources/Modules/Calibrations/View/CalibrationsRootView.swift

@@ -16,6 +16,21 @@ extension Calibrations {
             return formatter
         }
 
+        private var manualGlucoseFormatter: NumberFormatter {
+            let formatter = NumberFormatter()
+            formatter.numberStyle = .decimal
+            if state.units == .mgdL {
+                formatter.maximumIntegerDigits = 3
+                formatter.maximumFractionDigits = 0
+            } else {
+                formatter.maximumIntegerDigits = 2
+                formatter.minimumFractionDigits = 0
+                formatter.maximumFractionDigits = 1
+            }
+            formatter.roundingMode = .halfUp
+            return formatter
+        }
+
         private var dateFormatter: DateFormatter {
             let formatter = DateFormatter()
             formatter.timeStyle = .short
@@ -30,7 +45,11 @@ extension Calibrations {
                         HStack {
                             Text("Meter glucose")
                             Spacer()
-                            TextFieldWithToolBar(text: $state.newCalibration, placeholder: "0", numberFormatter: formatter)
+                            TextFieldWithToolBar(
+                                text: $state.newCalibration,
+                                placeholder: "0",
+                                numberFormatter: manualGlucoseFormatter
+                            )
                             Text(state.units.rawValue).foregroundColor(.secondary)
                         }
                         Button {

+ 11 - 3
Trio/Sources/Modules/DataTable/View/CarbEntryEditorView.swift

@@ -40,6 +40,14 @@ struct CarbEntryEditorView: View {
         _editedDate = State(initialValue: Date())
     }
 
+    private var mealFormatter: NumberFormatter {
+        let formatter = NumberFormatter()
+        formatter.numberStyle = .decimal
+        formatter.maximumIntegerDigits = 3
+        formatter.maximumFractionDigits = 0
+        return formatter
+    }
+
     private var carbLimitExceeded: Bool {
         editedCarbs > state.settingsManager.settings.maxCarbs
     }
@@ -130,7 +138,7 @@ struct CarbEntryEditorView: View {
                             text: $editedCarbs,
                             placeholder: "0",
                             keyboardType: .numberPad,
-                            numberFormatter: Formatter.decimalFormatterWithOneFractionDigit
+                            numberFormatter: mealFormatter
                         )
                         Text("g").foregroundStyle(.secondary)
                     }
@@ -142,7 +150,7 @@ struct CarbEntryEditorView: View {
                                 text: $editedProtein,
                                 placeholder: "0",
                                 keyboardType: .numberPad,
-                                numberFormatter: Formatter.decimalFormatterWithOneFractionDigit
+                                numberFormatter: mealFormatter
                             )
                             Text("g").foregroundStyle(.secondary)
                         }
@@ -153,7 +161,7 @@ struct CarbEntryEditorView: View {
                                 text: $editedFat,
                                 placeholder: "0",
                                 keyboardType: .numberPad,
-                                numberFormatter: Formatter.decimalFormatterWithOneFractionDigit
+                                numberFormatter: mealFormatter
                             )
                             Text("g").foregroundStyle(.secondary)
                         }

+ 6 - 3
Trio/Sources/Modules/DataTable/View/DataTableRootView.swift

@@ -61,8 +61,11 @@ extension DataTable {
         private var manualGlucoseFormatter: NumberFormatter {
             let formatter = NumberFormatter()
             formatter.numberStyle = .decimal
-            formatter.maximumFractionDigits = 0
-            if state.units == .mmolL {
+            if state.units == .mgdL {
+                formatter.maximumIntegerDigits = 3
+                formatter.maximumFractionDigits = 0
+            } else {
+                formatter.maximumIntegerDigits = 2
                 formatter.minimumFractionDigits = 0
                 formatter.maximumFractionDigits = 1
             }
@@ -433,7 +436,7 @@ extension DataTable {
                                 TextFieldWithToolBar(
                                     text: $state.manualGlucose,
                                     placeholder: " ... ",
-                                    maxValue: limitHigh,
+                                    keyboardType: state.units == .mgdL ? .numberPad : .decimalPad,
                                     numberFormatter: manualGlucoseFormatter,
                                     initialFocus: true
                                 )

+ 1 - 0
Trio/Sources/Modules/ManualTempBasal/View/ManualTempBasalRootView.swift

@@ -12,6 +12,7 @@ extension ManualTempBasal {
         private var formatter: NumberFormatter {
             let formatter = NumberFormatter()
             formatter.numberStyle = .decimal
+            formatter.maximumIntegerDigits = 2
             formatter.maximumFractionDigits = 2
             return formatter
         }

+ 13 - 3
Trio/Sources/Modules/Treatments/View/MealPreset/AddMealPresetView.swift

@@ -18,15 +18,24 @@ struct AddMealPresetView: View {
     private var mealFormatter: NumberFormatter {
         let formatter = NumberFormatter()
         formatter.numberStyle = .decimal
-        formatter.maximumFractionDigits = 1
+        formatter.maximumIntegerDigits = 3
+        formatter.maximumFractionDigits = 0
         return formatter
     }
 
+    private var isFormValid: Bool {
+        !dish.isEmpty && (presetCarbs > 0 || presetProtein > 0 || presetFat > 0)
+    }
+
     var body: some View {
         NavigationStack {
             Form {
                 Section {
-                    TextField("Name Of Dish", text: $dish)
+                    TextFieldWithToolBarString(
+                        text: $dish,
+                        placeholder: String(localized: "Name Of Dish"),
+                        maxLength: 25
+                    )
                 } header: {
                     Text("New Preset")
                 }
@@ -107,8 +116,9 @@ struct AddMealPresetView: View {
                 .foregroundStyle(Color.white)
                 .frame(maxWidth: .infinity, alignment: .center)
         }
-        .listRowBackground(Color(.systemBlue))
+        .listRowBackground(isFormValid ? Color(.systemBlue) : Color(.systemGray))
         .shadow(radius: 3)
         .clipShape(RoundedRectangle(cornerRadius: 8))
+        .disabled(!isFormValid)
     }
 }

+ 8 - 6
Trio/Sources/Modules/Treatments/View/TreatmentsRootView.swift

@@ -36,6 +36,7 @@ extension Treatments {
         private var formatter: NumberFormatter {
             let formatter = NumberFormatter()
             formatter.numberStyle = .decimal
+            formatter.maximumIntegerDigits = 2
             formatter.maximumFractionDigits = 2
             return formatter
         }
@@ -43,7 +44,8 @@ extension Treatments {
         private var mealFormatter: NumberFormatter {
             let formatter = NumberFormatter()
             formatter.numberStyle = .decimal
-            formatter.maximumFractionDigits = 1
+            formatter.maximumIntegerDigits = 3
+            formatter.maximumFractionDigits = 0
             return formatter
         }
 
@@ -51,8 +53,12 @@ extension Treatments {
             let formatter = NumberFormatter()
             formatter.numberStyle = .decimal
             if state.units == .mmolL {
+                formatter.maximumIntegerDigits = 2
                 formatter.maximumFractionDigits = 1
-            } else { formatter.maximumFractionDigits = 0 }
+            } else {
+                formatter.maximumIntegerDigits = 3
+                formatter.maximumFractionDigits = 0
+            }
             return formatter
         }
 
@@ -84,7 +90,6 @@ extension Treatments {
                         text: $state.protein,
                         placeholder: "0",
                         keyboardType: .numberPad,
-                        maxValue: state.maxProtein,
                         numberFormatter: mealFormatter,
                         showArrows: true,
                         previousTextField: { focusedField = previousField(from: .protein) },
@@ -102,7 +107,6 @@ extension Treatments {
                         text: $state.fat,
                         placeholder: "0",
                         keyboardType: .numberPad,
-                        maxValue: state.maxFat,
                         numberFormatter: mealFormatter,
                         showArrows: true,
                         previousTextField: { focusedField = previousField(from: .fat) },
@@ -122,7 +126,6 @@ extension Treatments {
                     text: $state.carbs,
                     placeholder: "0",
                     keyboardType: .numberPad,
-                    maxValue: state.maxCarbs,
                     numberFormatter: mealFormatter,
                     showArrows: true,
                     previousTextField: { focusedField = previousField(from: .carbs) },
@@ -317,7 +320,6 @@ extension Treatments {
                                     placeholder: "0",
                                     textColor: colorScheme == .dark ? .white : .blue,
                                     maxLength: 5,
-                                    maxValue: state.maxExternal,
                                     numberFormatter: formatter,
                                     showArrows: true,
                                     previousTextField: { focusedField = previousField(from: .bolus) },

+ 30 - 7
Trio/Sources/Views/TextFieldWithToolBar.swift

@@ -106,8 +106,15 @@ public struct TextFieldWithToolBar: View {
                     }
                 }
             }
-            .onChange(of: localText) { _, newValue in
-                handleTextChange(newValue)
+            .onChange(of: localText) { oldValue, newValue in
+                handleTextChange(oldValue, newValue)
+            }
+            .onChange(of: text) {
+                if text != 0 {
+                    localText = numberFormatter.string(from: text as NSNumber) ?? ""
+                } else if localText != "" {
+                    localText = ""
+                }
             }
             .onAppear {
                 if text != 0 {
@@ -118,7 +125,7 @@ public struct TextFieldWithToolBar: View {
             }
     }
 
-    private func handleTextChange(_ newValue: String) {
+    private func handleTextChange(_ oldValue: String, _ newValue: String) {
         // Handle empty string
         if newValue.isEmpty {
             text = 0
@@ -133,7 +140,7 @@ public struct TextFieldWithToolBar: View {
         if decimalSeparatorCount > 1 {
             // If there's already a decimal separator, prevent adding another one
             // by removing the last character (which would be the second decimal separator)
-            localText = String(newValue.dropLast())
+            localText = oldValue
             return
         }
 
@@ -150,6 +157,24 @@ public struct TextFieldWithToolBar: View {
             processedText = "0" + processedText
         }
 
+        // Check if the new value exceeds digit limits
+        let components = processedText.components(separatedBy: currentDecimalSeparator)
+
+        // Count integer digits (before decimal separator)
+        let integerDigits = components[0].filter { $0.isNumber }.count
+
+        // Count fraction digits (after decimal separator)
+        let fractionDigits = components.count > 1 ? components[1].filter { $0.isNumber }.count : 0
+
+        // Check against the number formatter limits
+        if integerDigits > numberFormatter.maximumIntegerDigits ||
+            (allowDecimalSeparator && fractionDigits > numberFormatter.maximumFractionDigits)
+        {
+            // Exceeds limits, don't update the text
+            localText = oldValue
+            return
+        }
+
         // Update if valid decimal
         if let decimal = Decimal(string: processedText, locale: numberFormatter.locale) {
             if let maxValue = maxValue, decimal > maxValue {
@@ -159,8 +184,6 @@ public struct TextFieldWithToolBar: View {
                 text = decimal
                 textDidChange?(decimal)
             }
-//            text = decimal
-//            textDidChange?(decimal)
 
             // If the processed text is different from the input, update the field
             if processedText != newValue {
@@ -168,7 +191,7 @@ public struct TextFieldWithToolBar: View {
             }
         } else {
             // If not a valid decimal, keep the old value
-            localText = numberFormatter.string(from: text as NSNumber) ?? ""
+            localText = oldValue
         }
     }
 }