Forráskód Böngészése

Refactor legend sheet; add missing chart shapes and elements

Deniz Cengiz 1 éve
szülő
commit
6b1d6c36bc

+ 5 - 2
FreeAPS.xcodeproj/project.pbxproj

@@ -486,6 +486,7 @@
 		DD9ECB712CA9A0BA00AA7C45 /* RemoteControlConfigProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD9ECB6E2CA9A0BA00AA7C45 /* RemoteControlConfigProvider.swift */; };
 		DD9ECB722CA9A0BA00AA7C45 /* RemoteControlConfigDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD9ECB6F2CA9A0BA00AA7C45 /* RemoteControlConfigDataFlow.swift */; };
 		DD9ECB742CA9A0C300AA7C45 /* RemoteControlConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD9ECB732CA9A0C300AA7C45 /* RemoteControlConfig.swift */; };
+		DDA6E2502D22187500C2988C /* ChartLegendView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDA6E24F2D22187500C2988C /* ChartLegendView.swift */; };
 		DDB37CC52D05048F00D99BF4 /* ContactImageStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB37CC42D05048F00D99BF4 /* ContactImageStorage.swift */; };
 		DDB37CC72D05127500D99BF4 /* FontExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDB37CC62D05127500D99BF4 /* FontExtensions.swift */; };
 		DDCEBF5B2CC1B76400DF4C36 /* LiveActivity+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCEBF5A2CC1B76400DF4C36 /* LiveActivity+Helper.swift */; };
@@ -1062,9 +1063,8 @@
 		BDF530D72B40F8AC002CAF43 /* LockScreenView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockScreenView.swift; sourceTree = "<group>"; };
 		BDFD16592AE40438007F0DDA /* TreatmentsRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TreatmentsRootView.swift; sourceTree = "<group>"; };
 		BF8BCB0C37DEB5EC377B9612 /* BasalProfileEditorRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = BasalProfileEditorRootView.swift; sourceTree = "<group>"; };
-		C19984D62EFC0035A9E9644D /* BolusProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = BolusProvider.swift; sourceTree = "<group>"; };
-		C2A0A42E2CE0312C003B98E8 /* ConstantValues.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstantValues.swift; sourceTree = "<group>"; };
 		C19984D62EFC0035A9E9644D /* TreatmentsProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = TreatmentsProvider.swift; sourceTree = "<group>"; };
+		C2A0A42E2CE0312C003B98E8 /* ConstantValues.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstantValues.swift; sourceTree = "<group>"; };
 		C377490C77661D75E8C50649 /* ManualTempBasalRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ManualTempBasalRootView.swift; sourceTree = "<group>"; };
 		C8D1A7CA8C10C4403D4BBFA7 /* TreatmentsDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = TreatmentsDataFlow.swift; sourceTree = "<group>"; };
 		CC6C406D2ACDD69E009B8058 /* RawFetchedProfile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RawFetchedProfile.swift; sourceTree = "<group>"; };
@@ -1192,6 +1192,7 @@
 		DD9ECB6E2CA9A0BA00AA7C45 /* RemoteControlConfigProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteControlConfigProvider.swift; sourceTree = "<group>"; };
 		DD9ECB6F2CA9A0BA00AA7C45 /* RemoteControlConfigDataFlow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteControlConfigDataFlow.swift; sourceTree = "<group>"; };
 		DD9ECB732CA9A0C300AA7C45 /* RemoteControlConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteControlConfig.swift; sourceTree = "<group>"; };
+		DDA6E24F2D22187500C2988C /* ChartLegendView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChartLegendView.swift; sourceTree = "<group>"; };
 		DDB37CC22D05044D00D99BF4 /* ContactTrickEntryStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ContactTrickEntryStored+CoreDataClass.swift"; sourceTree = "<group>"; };
 		DDB37CC32D05044D00D99BF4 /* ContactTrickEntryStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ContactTrickEntryStored+CoreDataProperties.swift"; sourceTree = "<group>"; };
 		DDB37CC42D05048F00D99BF4 /* ContactImageStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactImageStorage.swift; sourceTree = "<group>"; };
@@ -1888,6 +1889,7 @@
 		3833B51E260264AC003021B3 /* Chart */ = {
 			isa = PBXGroup;
 			children = (
+				DDA6E24F2D22187500C2988C /* ChartLegendView.swift */,
 				BD3CC0712B0B89D50013189E /* MainChartView.swift */,
 				BDDAF9F12D0055CC00B34E7A /* ChartElements */,
 			);
@@ -3552,6 +3554,7 @@
 				DDD1631F2C4C6F6900CD525A /* TrioCoreDataPersistentContainer.xcdatamodeld in Sources */,
 				DD1745482C55C61D00211FAC /* AutosensSettingsStateModel.swift in Sources */,
 				DD1745462C55C61500211FAC /* AutosensSettingsProvider.swift in Sources */,
+				DDA6E2502D22187500C2988C /* ChartLegendView.swift in Sources */,
 				3811DEAF25C9D88300A708ED /* KeyValueStorage.swift in Sources */,
 				DDD6D4D32CDE90720029439A /* HbA1cDisplayUnit.swift in Sources */,
 				38FE826D25CC8461001FF17A /* NightscoutAPI.swift in Sources */,

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

@@ -68,7 +68,6 @@ extension Home {
         var totalBolus: Decimal = 0
         var isStatusPopupPresented: Bool = false
         var isLegendPresented: Bool = false
-        var legendSheetDetent = PresentationDetent.large
         var totalInsulinDisplayType: TotalInsulinDisplayType = .totalDailyDose
         var roundedTotalBolus: String = ""
         var selectedTab: Int = 0

+ 187 - 0
FreeAPS/Sources/Modules/Home/View/Chart/ChartLegendView.swift

@@ -0,0 +1,187 @@
+import SwiftUI
+
+struct ChartLegendView: View {
+    var state: Home.StateModel
+
+    @State var legendSheetDetent = PresentationDetent.large
+
+    var body: some View {
+        NavigationStack {
+            VStack(alignment: .leading) {
+                List {
+                    Text(
+                        "The main chart in Trio is made up of various elements and shapes. Find their meanings below."
+                    )
+                    .font(.subheadline)
+                    .foregroundColor(.secondary)
+                    .padding(.bottom)
+                    .listRowBackground(Color.clear)
+
+                    Text("Forecast").bold().listRowBackground(Color.clear)
+
+                    VStack(alignment: .leading) {
+                        Text(
+                            "The oref algorithm determines insulin dosing based on a number of scenarios that it estimates with different types of forecasts."
+                        )
+                        .font(.subheadline)
+                        .foregroundColor(.secondary)
+
+                        if state.forecastDisplayType == .lines {
+                            legendLinesView
+                        } else {
+                            legendConeOfUncertaintyView
+                        }
+                    }
+
+                    Text("Other Elements & Shapes").bold().listRowBackground(Color.clear).padding(.top)
+
+                    DefinitionRow(
+                        term: "CGM Glucose Value",
+                        definition: Text(
+                            "Displays real-time glucose readings from a Continuous Glucose Monitor (CGM). Depending on your user interface settings, this may be displayed in a static (red, green, orange) or dynamic coloring scheme (full color spectrum)."
+                        ),
+                        color: Color.green,
+                        iconString: !state.settingsManager.settings.smoothGlucose ? "circle.fill" : "record.circle.fill"
+                    )
+
+                    DefinitionRow(
+                        term: "Manual Glucose Measurement",
+                        definition: Text("Manually entered blood glucose, such as a fingerstick test."),
+                        color: Color.red,
+                        iconString: "drop.fill"
+                    )
+
+                    DefinitionRow(
+                        term: "Bolus",
+                        definition: Text(
+                            "Shows an insulin dose, which can be a small automated dose (super-micro-bolus), a manually entered dose, or one given externally (e.g., a pen shot)."
+                        ),
+                        color: Color.insulin,
+                        iconString: "arrowtriangle.down.fill"
+                    )
+
+                    DefinitionRow(
+                        term: "Carb Entry",
+                        definition: Text("Tracks the carbohydrates you eat, entered to guide insulin dosing."),
+                        color: Color.orange,
+                        iconString: "arrowtriangle.down.fill",
+                        shouldRotateIcon: true
+                    )
+
+                    DefinitionRow(
+                        term: "Fat-Protein Carb Equivalent",
+                        definition: Text(
+                            "Represents carb equivalent for fat and protein, calculated using the Warsaw Method"
+                        ),
+                        color: Color.brown,
+                        iconString: "circle.fill"
+                    )
+
+                    DefinitionRow(
+                        term: "Override",
+                        definition: Text(
+                            "Indicates when an override is or was active, temporarily changing therapy settings (e.g., basal rate, insulin sensitivity, carb ratio, target glucose, or whether Trio can dose SMBs)."
+                        ),
+                        color: Color.purple,
+                        iconString: "button.horizontal.fill"
+                    )
+
+                    DefinitionRow(
+                        term: "Temporary Target",
+                        definition: Text(
+                            "Marks when a short-term temporary glucose target is or was active, (potentially) altering when or how much insulin is delivered."
+                        ),
+                        color: Color.green.opacity(0.4),
+                        iconString: "button.horizontal.fill"
+                    )
+
+                    DefinitionRow(
+                        term: "Past Insulin-on-Board (IOB)",
+                        definition: Text(
+                            "Shows the IOB value calculated by the algorithm at a specific time in the past. These values are snapshots and won’t change if insulin is added or removed after the fact."
+                        ),
+                        color: Color.darkerBlue.opacity(0.8),
+                        iconString: "line.diagonal"
+                    )
+
+                    DefinitionRow(
+                        term: "Past Carbs-on-Board (COB)",
+                        definition: Text(
+                            "Shows the COB value calculated by the algorithm at a specific time in the past. These values are snapshots and won’t change if carbs are added or removed after the fact."
+                        ),
+                        color: Color.orange.opacity(0.8),
+                        iconString: "line.diagonal"
+                    )
+                }
+                .navigationBarTitle("Legend", displayMode: .inline)
+                .padding(.trailing, 10)
+                .padding(.bottom, 15)
+
+                Button {
+                    state.isLegendPresented.toggle()
+                } label: {
+                    Text("Got it!")
+                        .frame(maxWidth: .infinity, alignment: .center)
+                }
+                .buttonStyle(.bordered)
+                .padding(.top)
+            }
+            .padding([.horizontal, .bottom])
+            .ignoresSafeArea(edges: .top)
+            .presentationDetents(
+                [.fraction(0.9), .large],
+                selection: $legendSheetDetent
+            )
+        }
+    }
+
+    var legendLinesView: some View {
+        Group {
+            DefinitionRow(
+                term: "IOB (Insulin on Board)",
+                definition: Text(
+                    "Forecasts future glucose readings based on the amount of insulin still active in the body."
+                ),
+                color: .insulin
+            )
+            DefinitionRow(
+                term: "ZT (Zero-Temp)",
+                definition: Text(
+                    "Forecasts the worst-case future glucose reading scenario if no carbs are absorbed and insulin delivery is stopped until glucose starts rising."
+                ),
+                color: .zt
+            )
+            DefinitionRow(
+                term: "COB (Carbs on Board)",
+                definition: Text(
+                    "Forecasts future glucose reading changes by considering the amount of carbohydrates still being absorbed in the body."
+                ),
+                color: .loopYellow
+            )
+            DefinitionRow(
+                term: "UAM (Unannounced Meal)",
+                definition: Text(
+                    "Forecasts future glucose levels and insulin dosing needs for unexpected meals or other causes of glucose reading increases without prior notice."
+                ),
+                color: .uam
+            )
+        }
+    }
+
+    var legendConeOfUncertaintyView: some View {
+        Group {
+            DefinitionRow(
+                term: "Cone of Uncertainty",
+                definition: VStack(alignment: .leading, spacing: 10) {
+                    Text(
+                        "For simplicity reasons, oref's various forecast curves are displayed as a \"Cone of Uncertainty\" that depicts a possible, forecasted range of future glucose fluctuation based on the current data and the algothim's result."
+                    )
+                    Text(
+                        "Note: To modify the forecast display type, go to Trio Settings > Features > User Interface > Forecast Display Type."
+                    )
+                },
+                color: Color.blue.opacity(0.5)
+            )
+        }
+    }
+}

+ 2 - 88
FreeAPS/Sources/Modules/Home/View/HomeRootView.swift

@@ -898,96 +898,10 @@ extension Home {
                 }
             }
             .sheet(isPresented: $state.isLegendPresented) {
-                legendSheetView()
+                ChartLegendView(state: state)
             }
         }
 
-        @ViewBuilder func legendSheetView() -> some View {
-            NavigationStack {
-                VStack(alignment: .leading, spacing: 16) {
-                    Text(
-                        "The oref algorithm determines insulin dosing based on a number of scenarios that it estimates with different types of forecasts."
-                    )
-                    .font(.subheadline)
-                    .foregroundColor(.secondary)
-
-                    if state.forecastDisplayType == .lines {
-                        legendLinesView()
-                    } else {
-                        legendConeOfUncertaintyView()
-                    }
-
-                    Button {
-                        state.isLegendPresented.toggle()
-                    } label: {
-                        Text("Got it!")
-                            .frame(maxWidth: .infinity, alignment: .center)
-                    }
-                    .buttonStyle(.bordered)
-                    .padding(.top)
-                }
-                .padding()
-                .presentationDetents(
-                    [.fraction(0.9), .large],
-                    selection: $state.legendSheetDetent
-                )
-            }
-        }
-
-        @ViewBuilder func legendLinesView() -> some View {
-            List {
-                DefinitionRow(
-                    term: "IOB (Insulin on Board)",
-                    definition: Text(
-                        "Forecasts future glucose readings based on the amount of insulin still active in the body."
-                    ),
-                    color: .insulin
-                )
-                DefinitionRow(
-                    term: "ZT (Zero-Temp)",
-                    definition: Text(
-                        "Forecasts the worst-case future glucose reading scenario if no carbs are absorbed and insulin delivery is stopped until glucose starts rising."
-                    ),
-                    color: .zt
-                )
-                DefinitionRow(
-                    term: "COB (Carbs on Board)",
-                    definition: Text(
-                        "Forecasts future glucose reading changes by considering the amount of carbohydrates still being absorbed in the body."
-                    ),
-                    color: .loopYellow
-                )
-                DefinitionRow(
-                    term: "UAM (Unannounced Meal)",
-                    definition: Text(
-                        "Forecasts future glucose levels and insulin dosing needs for unexpected meals or other causes of glucose reading increases without prior notice."
-                    ),
-                    color: .uam
-                )
-            }
-            .padding(.trailing, 10)
-            .navigationBarTitle("Legend", displayMode: .inline)
-        }
-
-        @ViewBuilder func legendConeOfUncertaintyView() -> some View {
-            List {
-                DefinitionRow(
-                    term: "Cone of Uncertainty",
-                    definition: VStack {
-                        Text(
-                            "For simplicity reasons, oref's various forecast curves are displayed as a \"Cone of Uncertainty\" that depicts a possible, forecasted range of future glucose fluctuation based on the current data and the algothim's result."
-                        )
-                        Text(
-                            "Note: To modify the forecast display type, go to Trio Settings > Features > User Interface > Forecast Display Type."
-                        )
-                    },
-                    color: Color.blue.opacity(0.5)
-                )
-            }
-            .padding(.trailing, 10)
-            .navigationBarTitle("Legend", displayMode: .inline)
-        }
-
         @ViewBuilder func tabBar() -> some View {
             ZStack(alignment: .bottom) {
                 TabView(selection: $selectedTab) {
@@ -1058,7 +972,7 @@ extension Home {
             }
         }
 
-        //TODO: Consolidate all mmol parsing methods (in TagCloudView, NightscoutManager and HomeRootView) to one central func
+        // TODO: Consolidate all mmol parsing methods (in TagCloudView, NightscoutManager and HomeRootView) to one central func
         private func parseReasonConclusion(_ reasonConclusion: String, isMmolL _: Bool) -> String {
             let patterns = [
                 "minGuardBG\\s*-?\\d+\\.?\\d*<-?\\d+\\.?\\d*",