Просмотр исходного кода

Merge pull request #103 from mountrcg/tt+or

merge Override & TT rework
Deniz Cengiz 1 год назад
Родитель
Сommit
d09671a605

+ 11 - 0
FreeAPS/Sources/Modules/Home/HomeStateModel+Setup/TempTargetSetup.swift

@@ -100,4 +100,15 @@ extension Home.StateModel {
             debugPrint("\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to cancel Profile")
         }
     }
+
+    func computeAdjustedPercentage(halfBasalTargetValue: Decimal, tempTargetValue: Decimal) -> Int {
+        let normalTarget: Decimal = 100
+        let deviationFromNormal = halfBasalTargetValue - normalTarget
+
+        let adjustmentFactor = deviationFromNormal + (tempTargetValue - normalTarget)
+        let adjustmentRatio: Decimal = (deviationFromNormal * adjustmentFactor <= 0) ? maxValue : deviationFromNormal /
+            adjustmentFactor
+
+        return Int(Double(min(adjustmentRatio, maxValue) * 100).rounded())
+    }
 }

+ 8 - 0
FreeAPS/Sources/Modules/Home/HomeStateModel.swift

@@ -37,6 +37,10 @@ extension Home {
         var pumpName = ""
         var pumpExpiresAtDate: Date?
         var tempTarget: TempTarget?
+        var highTTraisesSens: Bool = false
+        var lowTTlowersSens: Bool = false
+        var exerciseMode: Bool = false
+        var settingHalfBasalTarget: Decimal = 160
         var setupPump = false
         var errorMessage: String?
         var errorDate: Date?
@@ -352,6 +356,10 @@ extension Home {
             cgmAvailable = fetchGlucoseManager.cgmGlucoseSourceType != CGMType.none
             showCarbsRequiredBadge = settingsManager.settings.showCarbsRequiredBadge
             forecastDisplayType = settingsManager.settings.forecastDisplayType
+            highTTraisesSens = settingsManager.preferences.highTemptargetRaisesSensitivity
+            lowTTlowersSens = settingsManager.preferences.lowTemptargetLowersSensitivity
+            exerciseMode = settingsManager.preferences.exerciseMode
+            settingHalfBasalTarget = settingsManager.preferences.halfBasalExerciseTarget
         }
 
         func addPump(_ type: PumpConfig.PumpType) {

+ 16 - 7
FreeAPS/Sources/Modules/Home/View/HomeRootView.swift

@@ -13,6 +13,7 @@ extension Home {
         @State var showCancelAlert = false
         @State var showCancelConfirmDialog = false
         @State var isConfirmStopOverrideShown = false
+        @State var isConfirmStopOverridePresented = false
         @State var isConfirmStopTempTargetShown = false
         @State var isMenuPresented = false
         @State var showTreatments = false
@@ -243,8 +244,6 @@ extension Home {
             guard let latestTempTarget = latestTempTarget.first else {
                 return nil
             }
-
-            let name = latestTempTarget.name
             let duration = latestTempTarget.duration
             let addedMinutes = Int(truncating: duration ?? 0)
             let date = latestTempTarget.date ?? Date()
@@ -253,11 +252,21 @@ extension Home {
                 0
             )
             var durationString = ""
+            var percentageString = ""
             var target = (latestTempTarget.target ?? 100) as Decimal
+            var halfBasalTarget: Decimal = 160
+            if latestTempTarget.halfBasalTarget != nil {
+                halfBasalTarget = latestTempTarget.halfBasalTarget as! Decimal
+            } else { halfBasalTarget = state.settingHalfBasalTarget }
+            var showPercentage = false
+            if target > 100, state.exerciseMode || state.highTTraisesSens { showPercentage = true }
+            if target < 100, state.lowTTlowersSens { showPercentage = true }
+            if showPercentage {
+                percentageString =
+                    " \(state.computeAdjustedPercentage(halfBasalTargetValue: halfBasalTarget, tempTargetValue: target))%" }
             target = state.units == .mmolL ? target.asMmolL : target
-            var targetString = target == 0 ? "" : (fetchedTargetFormatter.string(from: target as NSNumber) ?? "") + " " +
-                state.units
-                .rawValue
+            let targetString = target == 0 ? "" : (fetchedTargetFormatter.string(from: target as NSNumber) ?? "") + " " +
+                state.units.rawValue + percentageString
 
             if newDuration >= 1 {
                 durationString =
@@ -580,7 +589,7 @@ extension Home {
                 .font(.system(size: 24))
                 .confirmationDialog(
                     "Stop the Override \"\(latestOverride.first?.name ?? "")\"?",
-                    isPresented: $isConfirmStopOverrideShown,
+                    isPresented: $isConfirmStopOverridePresented,
                     titleVisibility: .visible
                 ) {
                     Button("Stop", role: .destructive) {
@@ -594,7 +603,7 @@ extension Home {
                 .padding(.trailing, 8)
                 .onTapGesture {
                     if !latestOverride.isEmpty {
-                        isConfirmStopOverrideShown = true
+                        isConfirmStopOverridePresented = true
                     }
                 }
         }

+ 92 - 51
FreeAPS/Sources/Modules/OverrideConfig/OverrideStateModel.swift

@@ -42,8 +42,6 @@ extension OverrideConfig {
         var currentActiveTempTarget: TempTargetStored?
         var showOverrideEditSheet = false
         var showTempTargetEditSheet = false
-        var showInvalidTargetAlert = false
-
         var units: GlucoseUnits = .mgdL
 
         // temp target stuff
@@ -55,7 +53,7 @@ extension OverrideConfig {
         var date = Date()
         var newPresetName = ""
         var tempTargetPresets: [TempTargetStored] = []
-        var percentage = 100.0
+        var percentage: Double = 100
         var maxValue: Decimal = 1.2
         var minValue: Decimal = 0.15
         var viewPercantage = false
@@ -74,11 +72,6 @@ extension OverrideConfig {
         var isHelpSheetPresented: Bool = false
         var helpSheetDetent = PresentationDetent.large
 
-        var alertMessage: String {
-            let target: String = units == .mgdL ? "70-270 mg/dl" : "4-15 mmol/l"
-            return "Please enter a valid target between" + " \(target)."
-        }
-
         private var cancellables = Set<AnyCancellable>()
 
         override func subscribe() {
@@ -145,7 +138,7 @@ extension OverrideConfig {
 
                 if now >= entryStartTime, now < entryEndTime {
                     await MainActor.run {
-                        currentGlucoseTarget = units == .mgdL ? entry.value : entry.value.asMmolL
+                        currentGlucoseTarget = entry.value
                         target = currentGlucoseTarget
                     }
                     return
@@ -161,29 +154,11 @@ extension OverrideConfig {
             minValue = settingsManager.preferences.autosensMin
             settingHalfBasalTarget = settingsManager.preferences.halfBasalExerciseTarget
             halfBasalTarget = settingsManager.preferences.halfBasalExerciseTarget
-            percentage = Double(computeAdjustedPercentage() * 100)
+            percentage = computeAdjustedPercentage()
             Task {
                 await getCurrentGlucoseTarget()
             }
         }
-
-        func isInputInvalid(target: Decimal) -> Bool {
-            guard target != 0 else { return false }
-
-            if units == .mgdL,
-               target < 80 || target > 270 // in oref min lowTT = 80!
-            {
-                showInvalidTargetAlert = true
-                return true
-            } else if units == .mmolL,
-                      target < 4.4 || target > 15 // in oref min lowTT = 80!
-            {
-                showInvalidTargetAlert = true
-                return true
-            } else {
-                return false
-            }
-        }
     }
 }
 
@@ -896,20 +871,16 @@ extension OverrideConfig.StateModel {
         tempTargetTarget = 100
         tempTargetDuration = 0
         percentage = 100
-        halfBasalTarget = settingsManager.preferences.halfBasalExerciseTarget
+        halfBasalTarget = settingHalfBasalTarget
     }
 
     func handleAdjustSensToggle() {
         if !didAdjustSens {
             halfBasalTarget = settingHalfBasalTarget
-            percentage = Double(computeAdjustedPercentage(usingHBT: settingHalfBasalTarget) * 100)
+            percentage = computeAdjustedPercentage(usingHBT: settingHalfBasalTarget)
         }
     }
 
-    func isAdjustSensEnabled(usingTarget initialTarget: Decimal? = nil) -> Bool {
-        computeSliderHigh(usingTarget: initialTarget) > computeSliderLow(usingTarget: initialTarget)
-    }
-
     func computeHalfBasalTarget(
         usingTarget initialTarget: Decimal? = nil,
         usingPercentage initialPercentage: Double? = nil
@@ -925,35 +896,34 @@ extension OverrideConfig.StateModel {
         return round(Double(halfBasalTargetValue))
     }
 
-    func computeSliderLow(usingTarget initialTarget: Decimal? = nil) -> Double {
-        let calcTarget = initialTarget ?? tempTargetTarget
-        guard calcTarget != 0 else { return 15 }
-
+    func isAdjustSensEnabled(usingTarget initialTarget: Decimal? = nil) -> Bool {
         let shouldRaiseSensitivity = settingsManager.preferences.highTemptargetRaisesSensitivity
         let isExerciseModeActive = settingsManager.preferences.exerciseMode
-        let isTargetNormalOrLower = calcTarget <= normalTarget
-
-        let minSens = (isTargetNormalOrLower || (!shouldRaiseSensitivity && !isExerciseModeActive)) ? 100 : 15
+        let shouldlowerSensitivity = settingsManager.preferences.lowTemptargetLowersSensitivity
+        let target = initialTarget ?? tempTargetTarget
+        if target < normalTarget, shouldlowerSensitivity { return true }
+        if target > normalTarget, shouldRaiseSensitivity || isExerciseModeActive { return true }
+        return false
+    }
 
+    func computeSliderLow(usingTarget initialTarget: Decimal? = nil) -> Double {
+        let calcTarget = initialTarget ?? tempTargetTarget
+        guard calcTarget != 0 else { return 15 } // oref defined maximum sensitivity
+        let minSens = calcTarget < normalTarget ? 105 : 15
         return Double(max(0, minSens))
     }
 
     func computeSliderHigh(usingTarget initialTarget: Decimal? = nil) -> Double {
         let calcTarget = initialTarget ?? tempTargetTarget
-        guard calcTarget != 0 else { return Double(maxValue * 100) }
-
-        let shouldLowerSensitivity = settingsManager.preferences.lowTemptargetLowersSensitivity
-        let isTargetNormalOrHigher = calcTarget >= normalTarget
-
-        let maxSens = (isTargetNormalOrHigher || !shouldLowerSensitivity) ? 100 : Double(maxValue * 100)
-
+        guard calcTarget != 0 else { return Double(maxValue * 100) } // oref defined limit for increased insulin delivery
+        let maxSens = calcTarget > normalTarget ? 95 : Double(maxValue * 100)
         return maxSens
     }
 
     func computeAdjustedPercentage(
         usingHBT initialHalfBasalTarget: Decimal? = nil,
         usingTarget initialTarget: Decimal? = nil
-    ) -> Decimal {
+    ) -> Double {
         let halfBasalTargetValue = initialHalfBasalTarget ?? halfBasalTarget
         let calcTarget = initialTarget ?? tempTargetTarget
         let deviationFromNormal = halfBasalTargetValue - normalTarget
@@ -962,7 +932,7 @@ extension OverrideConfig.StateModel {
         let adjustmentRatio: Decimal = (deviationFromNormal * adjustmentFactor <= 0) ? maxValue : deviationFromNormal /
             adjustmentFactor
 
-        return min(adjustmentRatio, maxValue)
+        return Double(min(adjustmentRatio, maxValue) * 100).rounded()
     }
 }
 
@@ -1003,7 +973,7 @@ extension PickerSettingsProvider {
 
 enum TempTargetSensitivityAdjustmentType: String, CaseIterable {
     case standard = "Standard"
-    case slider = "Customized"
+    case slider = "Custom"
 }
 
 enum IsfAndOrCrOptions: String, CaseIterable {
@@ -1079,6 +1049,11 @@ func formatHrMin(_ durationInMinutes: Int) -> String {
     }
 }
 
+func convertToMinutes(_ hours: Int, _ minutes: Int) -> Decimal {
+    let totalMinutes = (hours * 60) + minutes
+    return Decimal(max(0, totalMinutes))
+}
+
 struct RadioButton: View {
     var isSelected: Bool
     var label: String
@@ -1096,3 +1071,69 @@ struct RadioButton: View {
         .buttonStyle(PlainButtonStyle())
     }
 }
+
+struct TargetPicker: View {
+    let label: String
+    @Binding var selection: Decimal
+    let options: [Decimal]
+    let units: GlucoseUnits
+    var hasChanges: Binding<Bool>?
+    @Binding var targetStep: Decimal
+    @Binding var displayPickerTarget: Bool
+    var toggleScrollWheel: (_ picker: Bool) -> Bool
+
+    var body: some View {
+        HStack {
+            Text(label)
+            Spacer()
+            Text(
+                (units == .mgdL ? selection.description : selection.formattedAsMmolL) + " " + units.rawValue
+            )
+            .foregroundColor(!displayPickerTarget ? .primary : .accentColor)
+        }
+        .onTapGesture {
+            displayPickerTarget = toggleScrollWheel(displayPickerTarget)
+        }
+        if displayPickerTarget {
+            HStack {
+                // Radio buttons and text on the left side
+                VStack(alignment: .leading) {
+                    // Radio buttons for step iteration
+                    let stepChoices: [Decimal] = units == .mgdL ? [1, 5] : [1, 9]
+                    ForEach(stepChoices, id: \.self) { step in
+                        let label = (units == .mgdL ? step.description : step.formattedAsMmolL) + " " +
+                            units.rawValue
+                        RadioButton(
+                            isSelected: targetStep == step,
+                            label: label
+                        ) {
+                            targetStep = step
+                            selection = OverrideConfig.StateModel.roundTargetToStep(selection, step)
+                        }
+                        .padding(.top, 10)
+                    }
+                }
+                .frame(maxWidth: .infinity)
+
+                Spacer()
+
+                // Picker on the right side
+                Picker(selection: Binding(
+                    get: { OverrideConfig.StateModel.roundTargetToStep(selection, targetStep) },
+                    set: {
+                        selection = $0
+                        hasChanges?.wrappedValue = true // This safely updates if hasChanges is provided
+                    }
+                ), label: Text("")) {
+                    ForEach(options, id: \.self) { option in
+                        Text((units == .mgdL ? option.description : option.formattedAsMmolL) + " " + units.rawValue)
+                            .tag(option)
+                    }
+                }
+                .pickerStyle(WheelPickerStyle())
+                .frame(maxWidth: .infinity)
+            }
+            .listRowSeparator(.hidden, edges: .top)
+        }
+    }
+}

+ 34 - 88
FreeAPS/Sources/Modules/OverrideConfig/View/AddOverrideForm.swift

@@ -5,9 +5,7 @@ struct AddOverrideForm: View {
     @Environment(\.presentationMode) var presentationMode
     @Environment(\.colorScheme) var colorScheme
     @Environment(\.dismiss) var dismiss
-
     @Bindable var state: OverrideConfig.StateModel
-
     @State private var selectedIsfCrOption: IsfAndOrCrOptions = .isfAndCr
     @State private var selectedDisableSmbOption: DisableSmbOptions = .dontDisable
     @State private var percentageStep: Int = 5
@@ -23,28 +21,22 @@ struct AddOverrideForm: View {
     @State private var didPressSave = false
 
     var color: LinearGradient {
-        colorScheme == .dark ? LinearGradient(
-            gradient: Gradient(colors: [
-                Color.bgDarkBlue,
-                Color.bgDarkerDarkBlue
-            ]),
-            startPoint: .top,
-            endPoint: .bottom
-        ) :
-            LinearGradient(
+        colorScheme == .dark
+            ? LinearGradient(
+                gradient: Gradient(colors: [
+                    Color.bgDarkBlue,
+                    Color.bgDarkerDarkBlue
+                ]),
+                startPoint: .top,
+                endPoint: .bottom
+            )
+            : LinearGradient(
                 gradient: Gradient(colors: [Color.gray.opacity(0.1)]),
                 startPoint: .top,
                 endPoint: .bottom
             )
     }
 
-    private var formatter: NumberFormatter {
-        let formatter = NumberFormatter()
-        formatter.numberStyle = .decimal
-        formatter.maximumFractionDigits = 0
-        return formatter
-    }
-
     var body: some View {
         NavigationView {
             List {
@@ -136,7 +128,7 @@ struct AddOverrideForm: View {
                             .pickerStyle(WheelPickerStyle())
                             .frame(maxWidth: .infinity)
                             .onChange(of: durationHours) {
-                                state.overrideDuration = Decimal(totalDurationInMinutes())
+                                state.overrideDuration = convertToMinutes(durationHours, durationMinutes)
                             }
 
                             Picker("Minutes", selection: $durationMinutes) {
@@ -147,7 +139,7 @@ struct AddOverrideForm: View {
                             .pickerStyle(WheelPickerStyle())
                             .frame(maxWidth: .infinity)
                             .onChange(of: durationMinutes) {
-                                state.overrideDuration = Decimal(totalDurationInMinutes())
+                                state.overrideDuration = convertToMinutes(durationHours, durationMinutes)
                             }
                         }
                         .listRowSeparator(.hidden, edges: .top)
@@ -238,73 +230,32 @@ struct AddOverrideForm: View {
 
             Section {
                 Toggle(isOn: $state.shouldOverrideTarget) {
-                    Text("Override Profile Target")
+                    Text("Override Target")
                 }
 
                 if state.shouldOverrideTarget {
-                    HStack {
-                        Text("Target Glucose")
-                        Spacer()
-                        Text(
-                            (state.units == .mgdL ? state.target.description : state.target.formattedAsMmolL) + " " + state
-                                .units.rawValue
-                        )
-                        .foregroundColor(!displayPickerTarget ? .primary : .accentColor)
-                    }
-                    .onTapGesture {
-                        displayPickerTarget = toggleScrollWheel(displayPickerTarget)
-                    }
-
-                    if displayPickerTarget {
-                        HStack {
-                            // Radio buttons and text on the left side
-                            VStack(alignment: .leading) {
-                                // Radio buttons for step iteration
-                                let stepChoices: [Decimal] = state.units == .mgdL ? [1, 5] : [1, 9]
-                                ForEach(stepChoices, id: \.self) { step in
-                                    let label = (state.units == .mgdL ? step.description : step.formattedAsMmolL) + " " +
-                                        state.units.rawValue
-
-                                    RadioButton(
-                                        isSelected: targetStep == step,
-                                        label: label
-                                    ) {
-                                        targetStep = step
-                                        state.target = OverrideConfig.StateModel.roundTargetToStep(state.target, targetStep)
-                                    }
-                                    .padding(.top, 10)
-                                }
-                            }
-                            .frame(maxWidth: .infinity)
-
-                            Spacer()
-
-                            // Picker on the right side
-                            let settingsProvider = PickerSettingsProvider.shared
-                            let glucoseSetting = PickerSetting(value: 0, step: targetStep, min: 72, max: 270, type: .glucose)
-                            Picker(selection: Binding(
-                                get: { OverrideConfig.StateModel.roundTargetToStep(state.target, targetStep) },
-                                set: { state.target = $0 }
-                            ), label: Text("")) {
-                                ForEach(
-                                    settingsProvider.generatePickerValues(
-                                        from: glucoseSetting,
-                                        units: state.units,
-                                        roundMinToStep: true
-                                    ),
-                                    id: \.self
-                                ) { glucose in
-                                    Text(
-                                        (state.units == .mgdL ? glucose.description : glucose.formattedAsMmolL) + " " + state
-                                            .units.rawValue
-                                    )
-                                    .tag(glucose)
-                                }
-                            }
-                            .pickerStyle(WheelPickerStyle())
-                            .frame(maxWidth: .infinity)
+                    let settingsProvider = PickerSettingsProvider.shared
+                    let glucoseSetting = PickerSetting(value: 0, step: targetStep, min: 72, max: 270, type: .glucose)
+                    TargetPicker(
+                        label: "Target Glucose",
+                        selection: Binding(
+                            get: { state.target },
+                            set: { state.target = $0 }
+                        ),
+                        options: settingsProvider.generatePickerValues(
+                            from: glucoseSetting,
+                            units: state.units,
+                            roundMinToStep: true
+                        ),
+                        units: state.units,
+                        targetStep: $targetStep,
+                        displayPickerTarget: $displayPickerTarget,
+                        toggleScrollWheel: toggleScrollWheel
+                    )
+                    .onAppear {
+                        if state.target == 0 {
+                            state.target = 100
                         }
-                        .listRowSeparator(.hidden, edges: .top)
                     }
                 }
             }
@@ -509,11 +460,6 @@ struct AddOverrideForm: View {
         return !toggle
     }
 
-    private func totalDurationInMinutes() -> Int {
-        let durationTotal = (durationHours * 60) + durationMinutes
-        return max(0, durationTotal)
-    }
-
     private func isOverrideInvalid() -> (Bool, String?) {
         let noDurationSpecified = !state.indefinite && state.overrideDuration == 0
         let targetZeroWithOverride = state.shouldOverrideTarget && state.target == 0

+ 57 - 66
FreeAPS/Sources/Modules/OverrideConfig/View/AddTempTargetForm.swift

@@ -68,7 +68,6 @@ struct AddTempTargetForm: View {
                 saveButton
             }
             .listSectionSpacing(10)
-            .listRowSpacing(10)
             .padding(.top, 30)
             .ignoresSafeArea(edges: .top)
             .scrollContentBackground(.hidden).background(color)
@@ -179,89 +178,81 @@ struct AddTempTargetForm: View {
             Section {
                 let settingsProvider = PickerSettingsProvider.shared
                 let glucoseSetting = PickerSetting(value: 0, step: targetStep, min: 80, max: 270, type: .glucose)
-                HStack {
-                    TargetPicker(
-                        label: "Target Glucose",
-                        selection: Binding(
-                            get: { state.tempTargetTarget },
-                            set: { state.tempTargetTarget = $0 }
-                        ),
-                        options: settingsProvider.generatePickerValues(
-                            from: glucoseSetting,
-                            units: state.units,
-                            roundMinToStep: true
-                        ),
+                TargetPicker(
+                    label: "Target Glucose",
+                    selection: Binding(
+                        get: { state.tempTargetTarget },
+                        set: { state.tempTargetTarget = $0 }
+                    ),
+                    options: settingsProvider.generatePickerValues(
+                        from: glucoseSetting,
                         units: state.units,
-                        hasChanges: $hasChanges,
-                        targetStep: $targetStep,
-                        displayPickerTarget: $displayPickerTarget,
-                        toggleScrollWheel: toggleScrollWheel
-                    )
-                }
+                        roundMinToStep: true
+                    ),
+                    units: state.units,
+                    hasChanges: $hasChanges,
+                    targetStep: $targetStep,
+                    displayPickerTarget: $displayPickerTarget,
+                    toggleScrollWheel: toggleScrollWheel
+                )
                 .onChange(of: state.tempTargetTarget) {
-                    state.percentage = Double(state.computeAdjustedPercentage() * 100)
+                    state.percentage = state.computeAdjustedPercentage()
                 }
             }
             .listRowBackground(Color.chart)
 
             if state.tempTargetTarget != state.normalTarget {
                 let computedHalfBasalTarget = Decimal(state.computeHalfBasalTarget())
-                let sensHint = state.tempTargetTarget > state.normalTarget ?
-                    "Reducing all delivered insulin to \(formattedPercentage(state.percentage))%." :
-                    "Increasing all delivered insulin by \(formattedPercentage(state.percentage - 100))%."
+
                 if state.isAdjustSensEnabled() {
                     Section(
-                        header: Text(sensHint)
-                            .textCase(.none)
-                            .foregroundStyle(colorScheme == .dark ? Color.orange : Color.accentColor),
+                        footer: percentageDescription(state.percentage),
                         content: {
-                            VStack {
-                                Picker("Sensitivity Adjustment", selection: $tempTargetSensitivityAdjustmentType) {
-                                    ForEach(TempTargetSensitivityAdjustmentType.allCases, id: \.self) { option in
-                                        Text(option.rawValue).tag(option)
-                                    }
-                                    .pickerStyle(MenuPickerStyle())
-                                    .onChange(of: tempTargetSensitivityAdjustmentType) { newValue in
-                                        if newValue == .standard {
-                                            state.halfBasalTarget = state.settingHalfBasalTarget
-                                            state.percentage = Double(state.computeAdjustedPercentage() * 100)
-                                        }
-                                    }
+                            Picker("Sensitivity Adjustment", selection: $tempTargetSensitivityAdjustmentType) {
+                                ForEach(TempTargetSensitivityAdjustmentType.allCases, id: \.self) { option in
+                                    Text(option.rawValue).tag(option)
                                 }
-
-                                if tempTargetSensitivityAdjustmentType == .slider {
-                                    Text("\(formattedPercentage(state.percentage)) % Insulin")
-                                        .foregroundColor(isUsingSlider ? .orange : Color.tabBar)
-                                        .font(.title3)
-                                        .fontWeight(.bold)
-                                    Slider(
-                                        value: $state.percentage,
-                                        in: state.computeSliderLow() ... state.computeSliderHigh(),
-                                        step: 5
-                                    ) {} minimumValueLabel: {
-                                        Text("\(state.computeSliderLow(), specifier: "%.0f")%")
-                                    } maximumValueLabel: {
-                                        Text("\(state.computeSliderHigh(), specifier: "%.0f")%")
-                                    } onEditingChanged: { editing in
-                                        isUsingSlider = editing
-                                        state.halfBasalTarget = Decimal(state.computeHalfBasalTarget())
+                                .pickerStyle(MenuPickerStyle())
+                                .onChange(of: tempTargetSensitivityAdjustmentType) { _, newValue in
+                                    if newValue == .standard {
+                                        state.halfBasalTarget = state.settingHalfBasalTarget
+                                        state.percentage = state.computeAdjustedPercentage()
                                     }
+                                }
+                            }
 
-                                    Divider()
-
-                                    HStack {
-                                        Text(
-                                            "Half Basal Exercise Target:"
-                                        )
-                                        Spacer()
-                                        Text(formattedGlucose(glucose: computedHalfBasalTarget))
-                                    }.foregroundStyle(.primary)
+                            Text("\(formattedPercentage(state.percentage)) % Insulin")
+                                .foregroundColor(isUsingSlider ? .orange : Color.tabBar)
+                                .font(.title3)
+                                .fontWeight(.bold)
+                                .frame(maxWidth: .infinity, alignment: .center)
+
+                            if tempTargetSensitivityAdjustmentType == .slider {
+                                Slider(
+                                    value: $state.percentage,
+                                    in: state.computeSliderLow() ... state.computeSliderHigh(),
+                                    step: 5
+                                ) {} minimumValueLabel: {
+                                    Text("\(state.computeSliderLow(), specifier: "%.0f")%")
+                                } maximumValueLabel: {
+                                    Text("\(state.computeSliderHigh(), specifier: "%.0f")%")
+                                } onEditingChanged: { editing in
+                                    isUsingSlider = editing
+                                    state.halfBasalTarget = Decimal(state.computeHalfBasalTarget())
                                 }
-                            }.padding(.vertical, 10)
+                                .listRowSeparator(.hidden, edges: .top)
+
+                                HStack {
+                                    Text(
+                                        "Half Basal Exercise Target:"
+                                    )
+                                    Spacer()
+                                    Text(formattedGlucose(glucose: computedHalfBasalTarget))
+                                }.foregroundStyle(.primary)
+                            }
                         }
                     )
                     .listRowBackground(Color.chart)
-                    .padding(.top, -10)
                 }
             }
         }

+ 1 - 76
FreeAPS/Sources/Modules/OverrideConfig/View/EditOverrideForm.swift

@@ -80,13 +80,6 @@ struct EditOverrideForm: View {
             )
     }
 
-    private var formatter: NumberFormatter {
-        let formatter = NumberFormatter()
-        formatter.numberStyle = .decimal
-        formatter.maximumFractionDigits = 0
-        return formatter
-    }
-
     private var percentageSelection: Binding<Double> {
         Binding<Double>(
             get: {
@@ -131,7 +124,6 @@ struct EditOverrideForm: View {
                     )
                 }
             }
-            .onAppear { targetStep = state.units == .mgdL ? 5 : 9 }
             .onDisappear {
                 if !hasChanges {
                     // Reset UI changes
@@ -328,6 +320,7 @@ struct EditOverrideForm: View {
                 if target_override {
                     let settingsProvider = PickerSettingsProvider.shared
                     let glucoseSetting = PickerSetting(value: 0, step: targetStep, min: 72, max: 270, type: .glucose)
+
                     TargetPicker(
                         label: "Target Glucose",
                         selection: Binding(
@@ -654,71 +647,3 @@ struct EditOverrideForm: View {
         return !toggle
     }
 }
-
-struct TargetPicker: View {
-    let label: String
-    @Binding var selection: Decimal
-    let options: [Decimal]
-    let units: GlucoseUnits
-    @Binding var hasChanges: Bool
-    @Binding var targetStep: Decimal
-    @Binding var displayPickerTarget: Bool
-    var toggleScrollWheel: (_ picker: Bool) -> Bool
-
-    var body: some View {
-        VStack {
-            HStack {
-                Text(label)
-                Spacer()
-                Text(
-                    (units == .mgdL ? selection.description : selection.formattedAsMmolL) + " " + units.rawValue
-                )
-                .foregroundColor(!displayPickerTarget ? .primary : .accentColor)
-            }
-            .onTapGesture {
-                displayPickerTarget = toggleScrollWheel(displayPickerTarget)
-            }
-            if displayPickerTarget {
-                HStack {
-                    // Radio buttons and text on the left side
-                    VStack(alignment: .leading) {
-                        // Radio buttons for step iteration
-                        let stepChoices: [Decimal] = units == .mgdL ? [1, 5] : [1, 9]
-                        ForEach(stepChoices, id: \.self) { step in
-                            let label = (units == .mgdL ? step.description : step.formattedAsMmolL) + " " +
-                                units.rawValue
-                            RadioButton(
-                                isSelected: targetStep == step,
-                                label: label
-                            ) {
-                                targetStep = step
-                                selection = OverrideConfig.StateModel.roundTargetToStep(selection, step)
-                            }
-                            .padding(.top, 10)
-                        }
-                    }
-                    .frame(maxWidth: .infinity)
-
-                    Spacer()
-
-                    // Picker on the right side
-                    Picker(selection: Binding(
-                        get: { OverrideConfig.StateModel.roundTargetToStep(selection, targetStep) },
-                        set: {
-                            selection = $0
-                            hasChanges = true
-                        }
-                    ), label: Text("")) {
-                        ForEach(options, id: \.self) { option in
-                            Text((units == .mgdL ? option.description : option.formattedAsMmolL) + " " + units.rawValue)
-                                .tag(option)
-                        }
-                    }
-                    .pickerStyle(WheelPickerStyle())
-                    .frame(maxWidth: .infinity)
-                }
-                .listRowSeparator(.hidden, edges: .top)
-            }
-        }
-    }
-}

+ 117 - 131
FreeAPS/Sources/Modules/OverrideConfig/View/EditTempTargetForm.swift

@@ -17,7 +17,7 @@ struct EditTempTargetForm: View {
     @State private var duration: Decimal
     @State private var date: Date
     @State private var halfBasalTarget: Decimal
-    @State private var percentage: Decimal
+    @State private var percentage: Double
 
     @State private var hasChanges = false
     @State private var showAlert = false
@@ -39,9 +39,9 @@ struct EditTempTargetForm: View {
         if let hbt = tempTargetToEdit.halfBasalTarget?.decimalValue {
             let H = hbt
             let T = tempTargetToEdit.target?.decimalValue ?? 100
-            let calcPercentage = Double(state.computeAdjustedPercentage(usingHBT: H, usingTarget: T) * 100)
-            _percentage = State(initialValue: Decimal(calcPercentage))
-        } else { _percentage = State(initialValue: Decimal(100)) }
+            let calcPercentage = state.computeAdjustedPercentage(usingHBT: H, usingTarget: T)
+            _percentage = State(initialValue: calcPercentage)
+        } else { _percentage = State(initialValue: 100) }
     }
 
     var color: LinearGradient {
@@ -67,7 +67,6 @@ struct EditTempTargetForm: View {
                 saveButton
             }
             .listSectionSpacing(10)
-            .listRowSpacing(10)
             .padding(.top, 30)
             .ignoresSafeArea(edges: .top)
             .scrollContentBackground(.hidden).background(color)
@@ -169,30 +168,28 @@ struct EditTempTargetForm: View {
             }.listRowBackground(Color.chart)
 
             Section {
-                HStack {
-                    // Picker on the right side
-                    let settingsProvider = PickerSettingsProvider.shared
-                    let glucoseSetting = PickerSetting(value: 0, step: targetStep, min: 80, max: 270, type: .glucose)
-                    TargetPicker(
-                        label: "Target Glucose",
-                        selection: Binding(
-                            get: { target },
-                            set: { target = $0 }
-                        ),
-                        options: settingsProvider.generatePickerValues(
-                            from: glucoseSetting,
-                            units: state.units,
-                            roundMinToStep: true
-                        ),
+                // Picker on the right side
+                let settingsProvider = PickerSettingsProvider.shared
+                let glucoseSetting = PickerSetting(value: 0, step: targetStep, min: 80, max: 270, type: .glucose)
+                TargetPicker(
+                    label: "Target Glucose",
+                    selection: Binding(
+                        get: { target },
+                        set: { target = $0 }
+                    ),
+                    options: settingsProvider.generatePickerValues(
+                        from: glucoseSetting,
                         units: state.units,
-                        hasChanges: $hasChanges,
-                        targetStep: $targetStep,
-                        displayPickerTarget: $displayPickerTarget,
-                        toggleScrollWheel: toggleScrollWheel
-                    )
-                }
+                        roundMinToStep: true
+                    ),
+                    units: state.units,
+                    hasChanges: $hasChanges,
+                    targetStep: $targetStep,
+                    displayPickerTarget: $displayPickerTarget,
+                    toggleScrollWheel: toggleScrollWheel
+                )
                 .onChange(of: target) {
-                    percentage = state.computeAdjustedPercentage(usingHBT: halfBasalTarget, usingTarget: target) * 100
+                    percentage = state.computeAdjustedPercentage(usingHBT: halfBasalTarget, usingTarget: target)
                 }
             }
             .listRowBackground(Color.chart)
@@ -200,81 +197,74 @@ struct EditTempTargetForm: View {
             if target != state.normalTarget {
                 let computedHalfBasalTarget = Decimal(
                     state
-                        .computeHalfBasalTarget(usingTarget: target, usingPercentage: Double(percentage))
+                        .computeHalfBasalTarget(usingTarget: target, usingPercentage: percentage)
                 )
-                let sensHint = target > state.normalTarget ?
-                    "Reducing all delivered insulin to \(formattedPercentage(Double(percentage)))%." :
-                    "Increasing all delivered insulin by \(formattedPercentage(Double(percentage) - 100))%."
 
                 if state.isAdjustSensEnabled(usingTarget: target) {
                     Section(
-                        header: Text(sensHint)
-                            .textCase(.none)
-                            .foregroundStyle(colorScheme == .dark ? Color.orange : Color.accentColor),
+                        footer: percentageDescription(percentage),
                         content: {
-                            VStack {
-                                Picker("Sensitivity Adjustment", selection: $tempTargetSensitivityAdjustmentType) {
-                                    ForEach(TempTargetSensitivityAdjustmentType.allCases, id: \.self) { option in
-                                        Text(option.rawValue).tag(option)
-                                    }
-                                    .pickerStyle(MenuPickerStyle())
-                                    .onChange(of: tempTargetSensitivityAdjustmentType) { newValue in
-                                        if newValue == .standard {
-                                            halfBasalTarget = state.settingHalfBasalTarget
-                                            percentage = (
-                                                state
-                                                    .computeAdjustedPercentage(usingHBT: halfBasalTarget, usingTarget: target) *
-                                                    100
-                                            )
-                                        }
-                                    }
+                            Picker("Sensitivity Adjustment", selection: $tempTargetSensitivityAdjustmentType) {
+                                ForEach(TempTargetSensitivityAdjustmentType.allCases, id: \.self) { option in
+                                    Text(option.rawValue).tag(option)
                                 }
-
-                                if tempTargetSensitivityAdjustmentType == .slider {
-                                    Text("\(formattedPercentage(Double(percentage))) % Insulin")
-                                        .foregroundColor(isUsingSlider ? .orange : Color.tabBar)
-                                        .font(.title3)
-                                        .fontWeight(.bold)
-                                    Slider(
-                                        value: Binding(
-                                            get: {
-                                                Double(truncating: percentage as NSNumber)
-                                            },
-                                            set: { newValue in
-                                                percentage = Decimal(newValue)
-                                                hasChanges = true
-                                                halfBasalTarget = Decimal(state.computeHalfBasalTarget(
-                                                    usingTarget: target,
-                                                    usingPercentage: Double(percentage)
-                                                ))
-                                            }
-                                        ),
-                                        in: Double(state.computeSliderLow(usingTarget: target)) ...
-                                            Double(state.computeSliderHigh(usingTarget: target)),
-                                        step: 5
-                                    ) {}
-                                    minimumValueLabel: {
-                                        Text("\(state.computeSliderLow(usingTarget: target), specifier: "%.0f")%")
-                                    }
-                                    maximumValueLabel: {
-                                        Text("\(state.computeSliderHigh(usingTarget: target), specifier: "%.0f")%")
+                                .pickerStyle(MenuPickerStyle())
+                                .onChange(of: tempTargetSensitivityAdjustmentType) { _, newValue in
+                                    if newValue == .standard {
+                                        halfBasalTarget = state.settingHalfBasalTarget
+                                        hasChanges = true
+                                        percentage = state.computeAdjustedPercentage(
+                                            usingHBT: halfBasalTarget,
+                                            usingTarget: target
+                                        )
                                     }
+                                }
+                            }
 
-                                    Divider()
-
-                                    HStack {
-                                        Text(
-                                            "Half Basal Exercise Target:"
-                                        )
-                                        Spacer()
-                                        Text(formattedGlucose(glucose: computedHalfBasalTarget))
-                                    }.foregroundStyle(.primary)
+                            Text("\(formattedPercentage(percentage)) % Insulin")
+                                .foregroundColor(isUsingSlider ? .orange : Color.tabBar)
+                                .font(.title3)
+                                .fontWeight(.bold)
+                                .frame(maxWidth: .infinity, alignment: .center)
+
+                            if tempTargetSensitivityAdjustmentType == .slider {
+                                Slider(
+                                    value: Binding(
+                                        get: {
+                                            Double(truncating: percentage as NSNumber)
+                                        },
+                                        set: { newValue in
+                                            percentage = newValue
+                                            hasChanges = true
+                                            halfBasalTarget = Decimal(state.computeHalfBasalTarget(
+                                                usingTarget: target,
+                                                usingPercentage: percentage
+                                            ))
+                                        }
+                                    ),
+                                    in: state.computeSliderLow(usingTarget: target) ... state
+                                        .computeSliderHigh(usingTarget: target) - 1,
+                                    step: 5
+                                ) {}
+                                minimumValueLabel: {
+                                    Text("\(state.computeSliderLow(usingTarget: target), specifier: "%.0f")%")
+                                }
+                                maximumValueLabel: {
+                                    Text("\(state.computeSliderHigh(usingTarget: target), specifier: "%.0f")%")
                                 }
-                            }.padding(.vertical, 10)
+                                .listRowSeparator(.hidden, edges: .top)
+
+                                HStack {
+                                    Text(
+                                        "Half Basal Exercise Target:"
+                                    )
+                                    Spacer()
+                                    Text(formattedGlucose(glucose: computedHalfBasalTarget))
+                                }.foregroundStyle(.primary)
+                            }
                         }
                     )
                     .listRowBackground(Color.chart)
-                    .padding(.top, -10)
                 }
             }
         }
@@ -284,52 +274,48 @@ struct EditTempTargetForm: View {
         HStack {
             Spacer()
             Button(action: {
-                if !state.isInputInvalid(target: target) {
-                    saveChanges()
-
-                    do {
-                        guard let moc = tempTarget.managedObjectContext else { return }
-                        guard moc.hasChanges else { return }
-                        try moc.save()
-
-                        if let currentActiveTempTarget = state.currentActiveTempTarget {
-                            Task {
-                                // TODO: - Creating a Run entry is probably needed for Overrides as well and the reason for "jumping" Overrides?
-                                // Disable previous active Temp Targets
-                                await state.disableAllActiveOverrides(
-                                    except: currentActiveTempTarget.objectID,
-                                    createOverrideRunEntry: false
+                saveChanges()
+                do {
+                    guard let moc = tempTarget.managedObjectContext else { return }
+                    guard moc.hasChanges else { return }
+                    try moc.save()
+
+                    if let currentActiveTempTarget = state.currentActiveTempTarget {
+                        Task {
+                            // TODO: - Creating a Run entry is probably needed for Overrides as well and the reason for "jumping" Overrides?
+                            // Disable previous active Temp Targets
+                            await state.disableAllActiveOverrides(
+                                except: currentActiveTempTarget.objectID,
+                                createOverrideRunEntry: false
+                            )
+
+                            // If the temp target which currently gets edited is enabled, then store it to the Temp Target JSON so that oref uses it
+                            if isEnabled {
+                                let tempTarget = TempTarget(
+                                    name: name,
+                                    createdAt: Date(),
+                                    targetTop: target,
+                                    targetBottom: target,
+                                    duration: duration,
+                                    enteredBy: TempTarget.manual,
+                                    reason: TempTarget.custom,
+                                    isPreset: isPreset ? true : false,
+                                    enabled: isEnabled ? true : false,
+                                    halfBasalTarget: halfBasalTarget
                                 )
 
-                                // If the temp target which currently gets edited is enabled, then store it to the Temp Target JSON so that oref uses it
-                                if isEnabled {
-                                    let tempTarget = TempTarget(
-                                        name: name,
-                                        createdAt: Date(),
-                                        targetTop: target,
-                                        targetBottom: target,
-                                        duration: duration,
-                                        enteredBy: TempTarget.manual,
-                                        reason: TempTarget.custom,
-                                        isPreset: isPreset ? true : false,
-                                        enabled: isEnabled ? true : false,
-                                        halfBasalTarget: halfBasalTarget
-                                    )
-
-                                    // Store to TempTargetStorage so that oref uses the edited Temp target
-                                    state.saveTempTargetToStorage(tempTargets: [tempTarget])
-                                }
-
-                                // Update view
-                                state.updateLatestTempTargetConfiguration()
+                                // Store to TempTargetStorage so that oref uses the edited Temp target
+                                state.saveTempTargetToStorage(tempTargets: [tempTarget])
                             }
-                        }
 
-                        hasChanges = false
-                        presentationMode.wrappedValue.dismiss()
-                    } catch {
-                        debugPrint("Failed to Edit Temp Target")
+                            // Update view
+                            state.updateLatestTempTargetConfiguration()
+                        }
                     }
+                    hasChanges = false
+                    presentationMode.wrappedValue.dismiss()
+                } catch {
+                    debugPrint("Failed to Edit Temp Target")
                 }
             }, label: {
                 Text("Save")

+ 49 - 32
FreeAPS/Sources/Modules/OverrideConfig/View/OverrideRootView.swift

@@ -19,7 +19,7 @@ extension OverrideConfig {
         @State private var selectedTempTarget: TempTargetStored?
 
         // temp targets
-        @State private var isConfirmDeleteShown = false
+        @State private var isConfirmDeletePresented = false
         @State private var isPromptPresented = false
         @State private var isRemoveAlertPresented = false
         @State private var removeAlert: Alert?
@@ -249,7 +249,7 @@ extension OverrideConfig {
                         .swipeActions(edge: .trailing, allowsFullSwipe: true) {
                             Button(role: .none) {
                                 selectedOverride = preset
-                                isConfirmDeleteShown = true
+                                isConfirmDeletePresented = true
                             } label: {
                                 Label("Delete", systemImage: "trash")
                                     .tint(.red)
@@ -267,7 +267,7 @@ extension OverrideConfig {
                 .onMove(perform: state.reorderOverride)
                 .confirmationDialog(
                     "Delete the Override Preset \"\(selectedOverride?.name ?? "")\"?",
-                    isPresented: $isConfirmDeleteShown,
+                    isPresented: $isConfirmDeletePresented,
                     titleVisibility: .visible
                 ) {
                     if let itemToDelete = selectedOverride {
@@ -322,7 +322,7 @@ extension OverrideConfig {
                             Button(role: .none) {
                                 Task {
                                     selectedTempTarget = preset
-                                    isConfirmDeleteShown = true
+                                    isConfirmDeletePresented = true
                                 }
                             } label: {
                                 Label("Delete", systemImage: "trash")
@@ -341,7 +341,7 @@ extension OverrideConfig {
                 .onMove(perform: state.reorderTempTargets)
                 .confirmationDialog(
                     "Delete the Temp Target Preset \"\(selectedTempTarget?.name ?? "")\"?",
-                    isPresented: $isConfirmDeleteShown,
+                    isPresented: $isConfirmDeletePresented,
                     titleVisibility: .visible
                 ) {
                     if let itemToDelete = selectedTempTarget {
@@ -489,8 +489,7 @@ extension OverrideConfig {
                     .RawValue ?? Double(state.settingHalfBasalTarget)
             )
             let percentage = Int(
-                state
-                    .computeAdjustedPercentage(usingHBT: presetHalfBasalTarget, usingTarget: presetTarget) * 100
+                state.computeAdjustedPercentage(usingHBT: presetHalfBasalTarget, usingTarget: presetTarget)
             )
 
             return ZStack(alignment: .trailing, content: {
@@ -554,30 +553,46 @@ extension OverrideConfig {
         }
 
         @ViewBuilder private func overridesView(for preset: OverrideStored) -> some View {
-            let target = (state.units == .mgdL ? preset.target : preset.target?.decimalValue.asMmolL as NSDecimalNumber?) ?? 0
-            let duration = (preset.duration ?? 0) as Decimal
+            let isSelected = preset.id == selectedPresetID
             let name = preset.name ?? ""
+            let indefinite = preset.indefinite
+            let duration = preset.duration?.decimalValue ?? Decimal(0)
             let percentage = preset.percentage
-            let perpetual = preset.indefinite
-            let durationString = perpetual ? "" : "\(formatHrMin(Int(duration)))"
-            let scheduledSMBstring = preset.smbIsScheduledOff && preset.start != preset.end
-                ? " \(formatTimeRange(start: preset.start?.stringValue, end: preset.end?.stringValue))"
-                : ""
-            let smbString = (preset.smbIsOff || preset.smbIsScheduledOff) ? "SMBs Off\(scheduledSMBstring)" : ""
-            let targetString = target != 0 ? "\(target.description) \(state.units.rawValue)" : ""
-            let maxMinutesSMB = (preset.smbMinutes as Decimal?) != nil ? (preset.smbMinutes ?? 0) as Decimal : 0
-            let maxMinutesUAM = (preset.uamMinutes as Decimal?) != nil ? (preset.uamMinutes ?? 0) as Decimal : 0
-            let maxSmbMinsString = (
-                maxMinutesSMB != 0 && preset.advancedSettings && !preset.smbIsOff && maxMinutesSMB != state
-                    .defaultSmbMinutes
-            ) ?
-                "\(maxMinutesSMB.formatted()) min SMB" : ""
-            let maxUamMinsString = (
-                maxMinutesUAM != 0 && preset.advancedSettings && !preset.smbIsOff && maxMinutesUAM != state
-                    .defaultUamMinutes
-            ) ?
-                "\(maxMinutesUAM.formatted()) min UAM" : ""
-            let isfAndCRstring: String = {
+            let smbMinutes = preset.smbMinutes?.decimalValue ?? Decimal(0)
+            let uamMinutes = preset.uamMinutes?.decimalValue ?? Decimal(0)
+
+            let target: String = {
+                guard let targetValue = preset.target, targetValue != 0 else { return "" }
+                return state.units == .mgdL ? targetValue.description : targetValue.decimalValue.formattedAsMmolL
+            }()
+
+            let targetString = target.isEmpty ? "" : "\(target) \(state.units.rawValue)"
+
+            let durationString = indefinite ? "" : "\(formatHrMin(Int(duration)))"
+
+            let scheduledSMBString: String = {
+                guard preset.smbIsScheduledOff, preset.start != preset.end else { return "" }
+                return " \(formatTimeRange(start: preset.start?.stringValue, end: preset.end?.stringValue))"
+            }()
+
+            let smbString: String = {
+                guard preset.smbIsOff || preset.smbIsScheduledOff else { return "" }
+                return "SMBs Off\(scheduledSMBString)"
+            }()
+
+            let maxSmbMinsString: String = {
+                guard smbMinutes != 0, preset.advancedSettings, !preset.smbIsOff,
+                      smbMinutes != state.defaultSmbMinutes else { return "" }
+                return "\(smbMinutes.formatted()) min SMB"
+            }()
+
+            let maxUamMinsString: String = {
+                guard uamMinutes != 0, preset.advancedSettings, !preset.smbIsOff,
+                      uamMinutes != state.defaultUamMinutes else { return "" }
+                return "\(uamMinutes.formatted()) min UAM"
+            }()
+
+            let isfAndCrString: String = {
                 switch (preset.isfAndCr, preset.isf, preset.cr) {
                 case (_, true, true),
                      (true, _, _):
@@ -590,16 +605,18 @@ extension OverrideConfig {
                     return ""
                 }
             }()
-            let isSelected = preset.id == selectedPresetID
 
+            let percentageString = percentage != 100 ? "\(Int(percentage))%\(isfAndCrString)" : ""
+
+            // Combine all labels into a single array, filtering out empty strings
             let labels: [String] = [
                 durationString,
-                percentage != 100 ? "\(Int(percentage))%\(isfAndCRstring)" : "",
+                percentageString,
                 targetString,
                 smbString,
                 maxSmbMinsString,
                 maxUamMinsString
-            ].filter { !$0.isEmpty } // filter out empty labels
+            ].filter { !$0.isEmpty }
 
             if !name.isEmpty {
                 ZStack(alignment: .trailing) {

+ 1 - 1
Model/TrioCoreDataPersistentContainer.xcdatamodeld/TrioCoreDataPersistentContainer.xcdatamodel/contents

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="23231" systemVersion="24A335" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithSwiftData="YES" userDefinedModelVersionIdentifier="">
+<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="23231" systemVersion="24A348" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithSwiftData="YES" userDefinedModelVersionIdentifier="">
     <entity name="BolusStored" representedClassName="BolusStored" syncable="YES">
         <attribute name="amount" optional="YES" attributeType="Decimal" defaultValueString="0"/>
         <attribute name="isExternal" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>