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

Merge pull request #69 from mountrcg/tempTargets

Suggestion to different advanced TT workflow
Deniz Cengiz 1 год назад
Родитель
Сommit
83cfac63f0

+ 5 - 9
FreeAPS/Sources/Modules/Home/View/HomeRootView.swift

@@ -503,13 +503,9 @@ extension Home {
 
         @ViewBuilder func adjustmentsOverrideView(_ overrideString: String) -> some View {
             Group {
-                Image(systemName: "person.fill")
-                    .font(.system(size: 24))
-                    .foregroundColor(Color(
-                        red: 0.6235294118,
-                        green: 0.4235294118,
-                        blue: 0.9803921569
-                    ))
+                Image(systemName: "clock.arrow.2.circlepath")
+                    .font(.system(size: 20))
+                    .foregroundStyle(Color.primary, Color(red: 0.6235294118, green: 0.4235294118, blue: 0.9803921569))
                 VStack(alignment: .leading) {
                     Text(latestOverride.first?.name ?? "Custom Override")
                         .font(.subheadline)
@@ -524,8 +520,8 @@ extension Home {
         @ViewBuilder func adjustmentsTempTargetView(_ tempTargetString: String) -> some View {
             Group {
                 Image(systemName: "target")
-                    .font(.system(size: 24))
-                    .foregroundColor(.loopGreen)
+                    .font(.system(size: 20))
+                    .foregroundStyle(Color.loopGreen)
                 VStack(alignment: .leading) {
                     Text(latestTempTarget.first?.name ?? "Temp Target")
                         .font(.subheadline)

+ 50 - 16
FreeAPS/Sources/Modules/OverrideConfig/OverrideStateModel.swift

@@ -54,7 +54,13 @@ extension OverrideConfig {
         @Published var minValue: Decimal = 0.15
         @Published var viewPercantage = false
         @Published var halfBasalTarget: Decimal = 160
+        @Published var settingHalfBasalTarget: Decimal = 160
         @Published var didSaveSettings: Bool = false
+        @Published var didAdjustSens: Bool = false {
+            didSet {
+                handleAdjustSensToggle()
+            }
+        }
 
         let coredataContext = CoreDataStack.shared.newTaskContext()
         let viewContext = CoreDataStack.shared.persistentContainer.viewContext
@@ -76,6 +82,9 @@ extension OverrideConfig {
             updateLatestTempTargetConfiguration()
             maxValue = settingsManager.preferences.autosensMax
             minValue = settingsManager.preferences.autosensMin
+            settingHalfBasalTarget = settingsManager.preferences.halfBasalExerciseTarget
+            halfBasalTarget = settingsManager.preferences.halfBasalExerciseTarget
+            percentage = Double(computeAdjustedPercentage() * 100)
             broadcaster.register(SettingsObserver.self, observer: self)
         }
 
@@ -726,29 +735,37 @@ extension OverrideConfig.StateModel {
         tempTargetTarget = 0
         tempTargetDuration = 0
         percentage = 100
-        halfBasalTarget = 160
+        halfBasalTarget = settingsManager.preferences.halfBasalExerciseTarget
+    }
+
+    func handleAdjustSensToggle() {
+        if !didAdjustSens {
+            halfBasalTarget = settingHalfBasalTarget
+            percentage = Double(computeAdjustedPercentage(using: settingHalfBasalTarget) * 100)
+        }
     }
 
     func computeHalfBasalTarget() -> Double {
-        let ratio = Decimal(percentage / 100)
+        let adjustmentRatio = Decimal(percentage / 100)
         let normalTarget: Decimal = 100
-        var target: Decimal = tempTargetTarget
+        var tempTargetValue: Decimal = tempTargetTarget
         if units == .mmolL {
-            target = target.asMgdL }
-        var hbtcalc = halfBasalTarget
-        if ratio != 1 {
-            hbtcalc = ((2 * ratio * normalTarget) - normalTarget - (ratio * target)) / (ratio - 1)
+            tempTargetValue = tempTargetValue.asMgdL }
+        var halfBasalTargetValue = halfBasalTarget
+        if adjustmentRatio != 1 {
+            halfBasalTargetValue = ((2 * adjustmentRatio * normalTarget) - normalTarget - (adjustmentRatio * tempTargetValue)) /
+                (adjustmentRatio - 1)
         }
-        return round(Double(hbtcalc))
+        return round(Double(halfBasalTargetValue))
     }
 
     func computeSliderLow() -> Double {
         var minSens: Double = 15
-        var target = tempTargetTarget
+        var tempTargetValue = tempTargetTarget
         if units == .mmolL {
-            target = Decimal(round(Double(tempTargetTarget.asMgdL))) }
-        if target == 0 { return minSens }
-        if target < 100 ||
+            tempTargetValue = Decimal(round(Double(tempTargetTarget.asMgdL))) }
+        if tempTargetValue == 0 { return minSens }
+        if tempTargetValue < 100 ||
             (
                 !settingsManager.preferences.highTemptargetRaisesSensitivity && !settingsManager.preferences
                     .exerciseMode
@@ -759,13 +776,30 @@ extension OverrideConfig.StateModel {
 
     func computeSliderHigh() -> Double {
         var maxSens = Double(maxValue * 100)
-        var target = tempTargetTarget
-        if target == 0 { return maxSens }
+        var tempTargetValue = tempTargetTarget
+        if tempTargetValue == 0 { return maxSens }
         if units == .mmolL {
-            target = Decimal(round(Double(tempTargetTarget.asMgdL))) }
-        if target > 100 || !settingsManager.preferences.lowTemptargetLowersSensitivity { maxSens = 100 }
+            tempTargetValue = Decimal(round(Double(tempTargetTarget.asMgdL))) }
+        if tempTargetValue > 100 || !settingsManager.preferences.lowTemptargetLowersSensitivity { maxSens = 100 }
         return maxSens
     }
+
+    func computeAdjustedPercentage(using initialHalfBasalTarget: Decimal? = nil) -> Decimal {
+        let halfBasalTargetValue = initialHalfBasalTarget ?? halfBasalTarget
+        let normalTarget: Decimal = 100
+        let deviationFromNormal = (halfBasalTargetValue - normalTarget)
+        let tempTargetValue = tempTargetTarget
+        var adjustmentRatio: Decimal = 1
+
+        if deviationFromNormal * (deviationFromNormal + tempTargetValue - normalTarget) <= 0 {
+            adjustmentRatio = maxValue
+        } else {
+            adjustmentRatio = deviationFromNormal / (deviationFromNormal + tempTargetValue - normalTarget)
+        }
+
+        adjustmentRatio = min(adjustmentRatio, maxValue)
+        return adjustmentRatio
+    }
 }
 
 extension OverrideConfig.StateModel: SettingsObserver {

+ 129 - 84
FreeAPS/Sources/Modules/OverrideConfig/View/AddTempTargetForm.swift

@@ -10,11 +10,10 @@ struct AddTempTargetForm: View {
     @State private var showPresetAlert = false
     @State private var alertString = ""
     @State private var isUsingSlider = false
-    @State private var advancedConfiguration = false
+
     @State private var didPressSave =
         false // only used for fixing the Disclaimer showing up after pressing save (after the state was resetted), maybe refactor this...
-
-    @State private var shouldDisplayHint: Bool = false
+    @State private var shouldDisplayHint = false
     @State var hintDetent = PresentationDetent.large
     @State var selectedVerboseHint: String?
     @State var hintLabel: String?
@@ -54,6 +53,10 @@ struct AddTempTargetForm: View {
         return formatter
     }
 
+    var isSliderEnabled: Bool {
+        state.computeSliderHigh() > state.computeSliderLow()
+    }
+
     var body: some View {
         NavigationView {
             Form {
@@ -111,6 +114,9 @@ struct AddTempTargetForm: View {
                     Text("Target")
                     Spacer()
                     TextFieldWithToolBar(text: $state.tempTargetTarget, placeholder: "0", numberFormatter: glucoseFormatter)
+                        .onChange(of: state.tempTargetTarget) { _ in
+                            state.percentage = Double(state.computeAdjustedPercentage() * 100)
+                        }
                     Text(state.units.rawValue).foregroundColor(.secondary)
                 }
 
@@ -124,6 +130,123 @@ struct AddTempTargetForm: View {
             }
         ).listRowBackground(Color.chart)
 
+        if isSliderEnabled && state.tempTargetTarget != 0 {
+            if state.tempTargetTarget > 100 {
+                Section {
+                    VStack(alignment: .leading) {
+                        Text("Raised Sensitivity:")
+                            .font(.footnote)
+                            .fontWeight(.bold)
+                        Text("Insulin reduced to \(formattedPercentage(state.percentage))% of regular amount.")
+                            .font(.footnote)
+                            .lineLimit(1)
+                    }
+                }.listRowBackground(Color.tabBar)
+                Section {
+                    VStack {
+                        Toggle("Adjust Sensitivity", isOn: $state.didAdjustSens).padding(.top)
+                        HStack(alignment: .top) {
+                            Text(
+                                "Temp Target raises Sensitivity. Further adjust if desired!"
+                            )
+                            .font(.footnote)
+                            .foregroundColor(.secondary)
+                            .lineLimit(nil)
+                            Spacer()
+                            Button(
+                                action: {
+                                    hintLabel = "Adjust Sensitivity for high Temp Target "
+                                    selectedVerboseHint =
+                                        "You have enabled High TempTarget Raises Sensitivity in Target Behaviour settings. Therefore current high Temp Target of \(state.tempTargetTarget) would raise your sensitivity, hence reduce Insulin dosing to \(formattedPercentage(state.percentage)) % of regular amount. This can be adjusted to another desired Insulin percentage!"
+                                    shouldDisplayHint.toggle()
+                                },
+                                label: {
+                                    HStack {
+                                        Image(systemName: "questionmark.circle")
+                                    }
+                                }
+                            ).buttonStyle(BorderlessButtonStyle())
+                        }.padding(.top)
+                    }.padding(.bottom)
+                }.listRowBackground(Color.chart)
+            } else if state.tempTargetTarget < 100 {
+                Section {
+                    VStack(alignment: .leading) {
+                        Text("Lowered Sensitivity:")
+                            .font(.footnote)
+                            .fontWeight(.bold)
+                        Text("Insulin increased to \(formattedPercentage(state.percentage))% of regular amount.")
+                            .font(.footnote)
+                            .lineLimit(1)
+                    }
+                }.listRowBackground(Color.tabBar)
+                Section {
+                    VStack {
+                        Toggle("Adjust Insulin %", isOn: $state.didAdjustSens).padding(.top)
+                        HStack(alignment: .top) {
+                            Text(
+                                "Temp Target lowers Sensitivity. Further adjust if desired!"
+                            )
+                            .font(.footnote)
+                            .foregroundColor(.secondary)
+                            .lineLimit(nil)
+                            Spacer()
+                            Button(
+                                action: {
+                                    hintLabel = "Adjust Sensitivity for low Temp Target "
+                                    selectedVerboseHint =
+                                        "You have enabled Low TempTarget Lowers Sensitivity in Target Behaviour settings and set autosens Max > 1. Therefore current low Temp Target of \(state.tempTargetTarget) would lower your sensitivity, hence increase Insulin dosing to \(formattedPercentage(state.percentage)) % of regular amount. This can be adjusted to another desired Insulin percentage!"
+                                    shouldDisplayHint.toggle()
+                                },
+                                label: {
+                                    HStack {
+                                        Image(systemName: "questionmark.circle")
+                                    }
+                                }
+                            ).buttonStyle(BorderlessButtonStyle())
+                        }.padding(.top)
+                    }.padding(.bottom)
+                }.listRowBackground(Color.chart)
+            }
+
+            if state.didAdjustSens && state.tempTargetTarget != 100 {
+                Section {
+                    VStack {
+                        Text("\(Int(state.percentage)) % Insulin")
+                            .foregroundColor(isUsingSlider ? .orange : Color.tabBar)
+                            .font(.largeTitle)
+                        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())
+                        }
+                        .disabled(!isSliderEnabled)
+
+                        Divider()
+                        HStack {
+                            Text(
+                                state
+                                    .units == .mgdL ?
+                                    "Half Basal Exercise Target at: \(state.computeHalfBasalTarget().formatted(.number.precision(.fractionLength(0)))) mg/dl" :
+                                    "Half Basal Exercise Target at: \(state.computeHalfBasalTarget().asMmolL.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))) mmol/L"
+                            )
+                            .lineLimit(1)
+                            .minimumScaleFactor(0.5)
+                            .foregroundColor(.secondary)
+                            Spacer()
+                        }
+                    }
+                }.listRowBackground(Color.chart)
+            }
+        }
+
         // TODO: with iOS 17 we can change the body content wrapper from FORM to LIST and apply the .listSpacing modifier to make this all nice and small.
         Section {
             Button(action: {
@@ -152,89 +275,11 @@ struct AddTempTargetForm: View {
                 .frame(maxWidth: .infinity, alignment: .center)
                 .tint(.white)
         }.listRowBackground(state.tempTargetDuration == 0 ? Color(.systemGray4) : Color(.orange))
-
-        Section {
-            VStack {
-                Toggle("Enable Advanced Configuration", isOn: $advancedConfiguration).padding(.top)
-
-                HStack(alignment: .top) {
-                    Text(
-                        "Add an explanation of the advanced configuration options here."
-                    )
-                    .font(.footnote)
-                    .foregroundColor(.secondary)
-                    .lineLimit(nil)
-                    Spacer()
-                    Button(
-                        action: {
-                            hintLabel = "Advanced Temp Target Configuration"
-                            selectedVerboseHint = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr."
-                            shouldDisplayHint.toggle()
-                        },
-                        label: {
-                            HStack {
-                                Image(systemName: "questionmark.circle")
-                            }
-                        }
-                    ).buttonStyle(BorderlessButtonStyle())
-                }.padding(.top)
-            }.padding(.bottom)
-        }.listRowBackground(Color.chart)
-
-        if advancedConfiguration && state.tempTargetTarget != 0 {
-            if sliderEnabled {
-                Section {
-                    VStack {
-                        Text("\(state.percentage.formatted(.number)) % Insulin")
-                            .foregroundColor(isUsingSlider ? .orange : Color.tabBar)
-                            .font(.largeTitle)
-
-                        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())
-                        }
-                        .disabled(!sliderEnabled)
-
-                        Divider()
-                        Text(
-                            state
-                                .units == .mgdL ?
-                                "Half Basal Exercise Target at: \(state.computeHalfBasalTarget().formatted(.number.precision(.fractionLength(0)))) mg/dl" :
-                                "Half Basal Exercise Target at: \(state.computeHalfBasalTarget().asMmolL.formatted(.number.grouping(.never).rounded().precision(.fractionLength(1)))) mmol/L"
-                        )
-                        .foregroundColor(.secondary)
-                        .font(.caption).italic()
-                    }
-                }.listRowBackground(Color.chart)
-            } else {
-                Section {
-                    VStack(alignment: .leading) {
-                        Text(
-                            "You have not enabled the proper Preferences to change sensitivity with chosen TempTarget. Verify Autosens Max > 1 & lowTT lowers Sens is on for lowTT's. For high TTs check highTT raises Sens is on (or Exercise Mode)!"
-                        ).bold()
-                    }
-                }.listRowBackground(Color.tabBar)
-            }
-        } else if advancedConfiguration && state.tempTargetTarget == 0 && !didPressSave {
-            Section {
-                VStack(alignment: .leading) {
-                    Text(
-                        "You need to input a Target for your Temp Target at first to use the advanced configuration!"
-                    ).bold()
-                }
-            }.listRowBackground(Color.tabBar)
-        }
     }
 
-    var sliderEnabled: Bool {
-        state.computeSliderHigh() > state.computeSliderLow()
+    private func formattedPercentage(_ value: Double) -> String {
+        let percentageNumber = NSNumber(value: value)
+        return formatter.string(from: percentageNumber) ?? "\(value)"
     }
 
     private func setupAlertString() async {

+ 9 - 6
FreeAPS/Sources/Modules/OverrideConfig/View/OverrideRootView.swift

@@ -65,11 +65,13 @@ extension OverrideConfig {
                 HStack(spacing: 6) {
                     HStack {
                         Spacer()
-                        Image(systemName: "person.fill")
-                            .font(.system(size: 18))
-                            .foregroundColor(Color(red: 0.6235294118, green: 0.4235294118, blue: 0.9803921569))
+                        Image(systemName: "clock.arrow.2.circlepath")
+                            .font(.system(size: 20))
+                            .foregroundStyle(Color.primary, Color(red: 0.6235294118, green: 0.4235294118, blue: 0.9803921569))
                         Text(OverrideConfig.Tab.overrides.name)
                             .font(.subheadline)
+                            .lineLimit(1)
+                            .minimumScaleFactor(0.8)
                         Spacer()
                     }
                     .padding(.vertical, 6)
@@ -83,10 +85,12 @@ extension OverrideConfig {
                     HStack {
                         Spacer()
                         Image(systemName: "target")
-                            .font(.system(size: 18))
-                            .foregroundColor(.loopGreen)
+                            .font(.system(size: 20))
+                            .foregroundStyle(Color.loopGreen)
                         Text(OverrideConfig.Tab.tempTargets.name)
                             .font(.subheadline)
+                            .lineLimit(1)
+                            .minimumScaleFactor(0.8)
                         Spacer()
                     }
                     .padding(.vertical, 6)
@@ -98,7 +102,6 @@ extension OverrideConfig {
                         }
                     }
                 }
-                .padding(2)
                 .background(Color.gray.opacity(0.2))
                 .cornerRadius(8)
                 .padding(.horizontal)