Procházet zdrojové kódy

Refactor HbA1c and TIR chart interface setting from toggle to picker

Deniz Cengiz před 1 rokem
rodič
revize
7e07afb8fa

+ 8 - 0
FreeAPS.xcodeproj/project.pbxproj

@@ -409,6 +409,7 @@
 		D6DEC113821A7F1056C4AA1E /* NightscoutConfigDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F2A13DF0EDEEEDC4106AA2A /* NightscoutConfigDataFlow.swift */; };
 		D76333C9256787610B3B4875 /* AutotuneConfigStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D295A3F870E826BE371C0BB5 /* AutotuneConfigStateModel.swift */; };
 		DBA5254DBB2586C98F61220C /* ISFEditorProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F9F137F126D9F8DEB799F26 /* ISFEditorProvider.swift */; };
+		DD07CA142CE80B73002D45A9 /* TimeInRangeChartStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD07CA132CE80B73002D45A9 /* TimeInRangeChartStyle.swift */; };
 		DD09D47B2C5986D1003FEA5D /* CalendarEventSettingsDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD09D47A2C5986D1003FEA5D /* CalendarEventSettingsDataFlow.swift */; };
 		DD09D47D2C5986DA003FEA5D /* CalendarEventSettingsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD09D47C2C5986DA003FEA5D /* CalendarEventSettingsProvider.swift */; };
 		DD09D47F2C5986E5003FEA5D /* CalendarEventSettingsStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD09D47E2C5986E5003FEA5D /* CalendarEventSettingsStateModel.swift */; };
@@ -473,6 +474,7 @@
 		DDD1631A2C4C695E00CD525A /* EditOverrideForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD163192C4C695E00CD525A /* EditOverrideForm.swift */; };
 		DDD1631C2C4C697400CD525A /* AddOverrideForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD1631B2C4C697400CD525A /* AddOverrideForm.swift */; };
 		DDD1631F2C4C6F6900CD525A /* TrioCoreDataPersistentContainer.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = DDD1631D2C4C6F6900CD525A /* TrioCoreDataPersistentContainer.xcdatamodeld */; };
+		DDD6D4D32CDE90720029439A /* HbA1cDisplayUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD6D4D22CDE90720029439A /* HbA1cDisplayUnit.swift */; };
 		DDE179522C910127003CDDB7 /* MealPresetStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE179322C910127003CDDB7 /* MealPresetStored+CoreDataClass.swift */; };
 		DDE179532C910127003CDDB7 /* MealPresetStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE179332C910127003CDDB7 /* MealPresetStored+CoreDataProperties.swift */; };
 		DDE179542C910127003CDDB7 /* LoopStatRecord+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE179342C910127003CDDB7 /* LoopStatRecord+CoreDataClass.swift */; };
@@ -1089,6 +1091,7 @@
 		D0BDC6993C1087310EDFC428 /* CarbRatioEditorRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CarbRatioEditorRootView.swift; sourceTree = "<group>"; };
 		D295A3F870E826BE371C0BB5 /* AutotuneConfigStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AutotuneConfigStateModel.swift; sourceTree = "<group>"; };
 		DC2C6489D29ECCCAD78E0721 /* GlucoseNotificationSettingsStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = GlucoseNotificationSettingsStateModel.swift; sourceTree = "<group>"; };
+		DD07CA132CE80B73002D45A9 /* TimeInRangeChartStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeInRangeChartStyle.swift; sourceTree = "<group>"; };
 		DD09D47A2C5986D1003FEA5D /* CalendarEventSettingsDataFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarEventSettingsDataFlow.swift; sourceTree = "<group>"; };
 		DD09D47C2C5986DA003FEA5D /* CalendarEventSettingsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarEventSettingsProvider.swift; sourceTree = "<group>"; };
 		DD09D47E2C5986E5003FEA5D /* CalendarEventSettingsStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarEventSettingsStateModel.swift; sourceTree = "<group>"; };
@@ -1153,6 +1156,7 @@
 		DDD163192C4C695E00CD525A /* EditOverrideForm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditOverrideForm.swift; sourceTree = "<group>"; };
 		DDD1631B2C4C697400CD525A /* AddOverrideForm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddOverrideForm.swift; sourceTree = "<group>"; };
 		DDD1631E2C4C6F6900CD525A /* TrioCoreDataPersistentContainer.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = TrioCoreDataPersistentContainer.xcdatamodel; sourceTree = "<group>"; };
+		DDD6D4D22CDE90720029439A /* HbA1cDisplayUnit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HbA1cDisplayUnit.swift; sourceTree = "<group>"; };
 		DDE179322C910127003CDDB7 /* MealPresetStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MealPresetStored+CoreDataClass.swift"; sourceTree = "<group>"; };
 		DDE179332C910127003CDDB7 /* MealPresetStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MealPresetStored+CoreDataProperties.swift"; sourceTree = "<group>"; };
 		DDE179342C910127003CDDB7 /* LoopStatRecord+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LoopStatRecord+CoreDataClass.swift"; sourceTree = "<group>"; };
@@ -1952,6 +1956,7 @@
 		388E5A5925B6F0250019842D /* Models */ = {
 			isa = PBXGroup;
 			children = (
+				DD07CA132CE80B73002D45A9 /* TimeInRangeChartStyle.swift */,
 				DD940BA92CA7585D000830A5 /* GlucoseColorScheme.swift */,
 				DD6D67E32C9C253500660C9B /* ColorSchemeOption.swift */,
 				385CEAC025F2EA52002D6D5B /* Announcement.swift */,
@@ -1998,6 +2003,7 @@
 				DD6B7CB32C7B71F700B75029 /* ForecastDisplayType.swift */,
 				DD6B7CB52C7B748B00B75029 /* TotalInsulinDisplayType.swift */,
 				DD9ECB692CA99F6C00AA7C45 /* PushMessage.swift */,
+				DDD6D4D22CDE90720029439A /* HbA1cDisplayUnit.swift */,
 			);
 			path = Models;
 			sourceTree = "<group>";
@@ -3289,6 +3295,7 @@
 				382C134B25F14E3700715CE1 /* BGTargets.swift in Sources */,
 				38AEE75725F0F18E0013F05B /* CarbsStorage.swift in Sources */,
 				38B4F3CA25E502E200E76A18 /* SwiftNotificationCenter.swift in Sources */,
+				DD07CA142CE80B73002D45A9 /* TimeInRangeChartStyle.swift in Sources */,
 				38AEE75225F022080013F05B /* SettingsManager.swift in Sources */,
 				3894873A2614928B004DF424 /* DispatchTimer.swift in Sources */,
 				3895E4C625B9E00D00214B37 /* Preferences.swift in Sources */,
@@ -3386,6 +3393,7 @@
 				DD1745482C55C61D00211FAC /* AutosensSettingsStateModel.swift in Sources */,
 				DD1745462C55C61500211FAC /* AutosensSettingsProvider.swift in Sources */,
 				3811DEAF25C9D88300A708ED /* KeyValueStorage.swift in Sources */,
+				DDD6D4D32CDE90720029439A /* HbA1cDisplayUnit.swift in Sources */,
 				38FE826D25CC8461001FF17A /* NightscoutAPI.swift in Sources */,
 				388358C825EEF6D200E024B2 /* BasalProfileEntry.swift in Sources */,
 				3811DE0B25C9D32F00A708ED /* BaseView.swift in Sources */,

+ 2 - 2
FreeAPS/Resources/json/defaults/freeaps/freeaps_settings.json

@@ -33,14 +33,14 @@
   "useAppleHealth" : false,
   "smoothGlucose" : false,
   "displayOnWatch" : "BGTarget",
-  "overrideHbA1cUnit" : false,
+  "hbA1cDisplayUnit" : "percent",
   "high" : 180,
   "low" : 70,
   "hours" : 6,
   "glucoseColorScheme" : "staticColor",
   "xGridLines" : true,
   "yGridLines" : true,
-  "oneDimensionalGraph" : false,
+  "timeInRangeChartStyle" : "vertical",
   "rulerMarks" : true,
   "forecastDisplayType": "cone",
   "maxCarbs": 250,

+ 16 - 17
FreeAPS/Sources/APS/APSManager.swift

@@ -1043,10 +1043,8 @@ final class BaseAPSManager: APSManager, Injectable {
                 scheduled_basal: 0,
                 total_average: 0
             )
-
-            let gs = await glucoseStats
-            let overrideHbA1cUnit = gs.overrideHbA1cUnit
-            let hbA1cUnit = !overrideHbA1cUnit ? (units == .mmolL ? "mmol/mol" : "%") : (units == .mmolL ? "%" : "mmol/mol")
+            let processedGlucoseStats = await glucoseStats
+            let hbA1cDisplayUnit = processedGlucoseStats.hbA1cDisplayUnit
 
             let dailystat = await Statistics(
                 created_at: Date(),
@@ -1064,14 +1062,15 @@ final class BaseAPSManager: APSManager, Injectable {
                 insulinType: insulin_type.rawValue,
                 peakActivityTime: iPa,
                 Carbs_24h: await carbTotal,
-                GlucoseStorage_Days: Decimal(roundDouble(gs.numberofDays, 1)),
+                GlucoseStorage_Days: Decimal(roundDouble(processedGlucoseStats.numberofDays, 1)),
                 Statistics: Stats(
-                    Distribution: gs.TimeInRange,
-                    Glucose: gs.avg,
-                    HbA1c: gs.hbs, Units: Units(Glucose: units.rawValue, HbA1c: hbA1cUnit),
+                    Distribution: processedGlucoseStats.TimeInRange,
+                    Glucose: processedGlucoseStats.avg,
+                    HbA1c: processedGlucoseStats.hbs,
+                    Units: Units(Glucose: units.rawValue, HbA1c: hbA1cDisplayUnit.rawValue),
                     LoopCycles: loopStats,
                     Insulin: insulin,
-                    Variance: gs.variance
+                    Variance: processedGlucoseStats.variance
                 )
             )
             storage.save(dailystat, as: file)
@@ -1234,7 +1233,7 @@ final class BaseAPSManager: APSManager, Injectable {
                 cv: Double,
                 readings: Double
             ),
-            overrideHbA1cUnit: Bool,
+            hbA1cDisplayUnit: HbA1cDisplayUnit,
             numberofDays: Double,
             TimeInRange: TIRs,
             avg: Averages,
@@ -1270,7 +1269,7 @@ final class BaseAPSManager: APSManager, Injectable {
                 cv: Double,
                 readings: Double
             ),
-            overrideHbA1cUnit: Bool,
+            hbA1cDisplayUnit: HbA1cDisplayUnit,
             numberofDays: Double,
             TimeInRange: TIRs,
             avg: Averages,
@@ -1301,18 +1300,18 @@ final class BaseAPSManager: APSManager, Injectable {
                 total: self.roundDecimal(Decimal(totalDaysGlucose.median), 1)
             )
 
-            let overrideHbA1cUnit = self.settingsManager.settings.overrideHbA1cUnit
+            let hbA1cDisplayUnit = self.settingsManager.settings.hbA1cDisplayUnit
 
             let hbs = Durations(
-                day: ((units == .mmolL && !overrideHbA1cUnit) || (units == .mgdL && overrideHbA1cUnit)) ?
+                day: ((units == .mmolL && hbA1cDisplayUnit == .mmolMol) || (units == .mgdL && hbA1cDisplayUnit == .percent)) ?
                     self.roundDecimal(Decimal(oneDayGlucose.ifcc), 1) : self.roundDecimal(Decimal(oneDayGlucose.ngsp), 1),
-                week: ((units == .mmolL && !overrideHbA1cUnit) || (units == .mgdL && overrideHbA1cUnit)) ?
+                week: ((units == .mmolL && hbA1cDisplayUnit == .mmolMol) || (units == .mgdL && hbA1cDisplayUnit == .percent)) ?
                     self.roundDecimal(Decimal(sevenDaysGlucose.ifcc), 1) : self
                     .roundDecimal(Decimal(sevenDaysGlucose.ngsp), 1),
-                month: ((units == .mmolL && !overrideHbA1cUnit) || (units == .mgdL && overrideHbA1cUnit)) ?
+                month: ((units == .mmolL && hbA1cDisplayUnit == .mmolMol) || (units == .mgdL && hbA1cDisplayUnit == .percent)) ?
                     self.roundDecimal(Decimal(thirtyDaysGlucose.ifcc), 1) : self
                     .roundDecimal(Decimal(thirtyDaysGlucose.ngsp), 1),
-                total: ((units == .mmolL && !overrideHbA1cUnit) || (units == .mgdL && overrideHbA1cUnit)) ?
+                total: ((units == .mmolL && hbA1cDisplayUnit == .mmolMol) || (units == .mgdL && hbA1cDisplayUnit == .percent)) ?
                     self.roundDecimal(Decimal(totalDaysGlucose.ifcc), 1) : self.roundDecimal(Decimal(totalDaysGlucose.ngsp), 1)
             )
 
@@ -1386,7 +1385,7 @@ final class BaseAPSManager: APSManager, Injectable {
             )
             let variance = Variance(SD: standardDeviations, CV: cvs)
 
-            result = (oneDayGlucose, overrideHbA1cUnit, numberOfDays, TimeInRange, avg, hbs, variance)
+            result = (oneDayGlucose, hbA1cDisplayUnit, numberOfDays, TimeInRange, avg, hbs, variance)
         }
 
         return result!

+ 6 - 6
FreeAPS/Sources/Models/FreeAPSSettings.swift

@@ -49,14 +49,14 @@ struct FreeAPSSettings: JSON, Equatable {
     var useAppleHealth: Bool = false
     var smoothGlucose: Bool = false
     var displayOnWatch: AwConfig = .BGTarget
-    var overrideHbA1cUnit: Bool = false
+    var hbA1cDisplayUnit: HbA1cDisplayUnit = .percent
     var high: Decimal = 180
     var low: Decimal = 70
     var hours: Int = 6
     var glucoseColorScheme: GlucoseColorScheme = .staticColor
     var xGridLines: Bool = true
     var yGridLines: Bool = true
-    var oneDimensionalGraph: Bool = false
+    var timeInRangeChartStyle: TimeInRangeChartStyle = .vertical
     var rulerMarks: Bool = true
     var forecastDisplayType: ForecastDisplayType = .cone
     var maxCarbs: Decimal = 250
@@ -263,8 +263,8 @@ extension FreeAPSSettings: Decodable {
             settings.yGridLines = yGridLines
         }
 
-        if let oneDimensionalGraph = try? container.decode(Bool.self, forKey: .oneDimensionalGraph) {
-            settings.oneDimensionalGraph = oneDimensionalGraph
+        if let timeInRangeChartStyle = try? container.decode(TimeInRangeChartStyle.self, forKey: .timeInRangeChartStyle) {
+            settings.timeInRangeChartStyle = timeInRangeChartStyle
         }
 
         if let rulerMarks = try? container.decode(Bool.self, forKey: .rulerMarks) {
@@ -275,8 +275,8 @@ extension FreeAPSSettings: Decodable {
             settings.forecastDisplayType = forecastDisplayType
         }
 
-        if let overrideHbA1cUnit = try? container.decode(Bool.self, forKey: .overrideHbA1cUnit) {
-            settings.overrideHbA1cUnit = overrideHbA1cUnit
+        if let hbA1cDisplayUnit = try? container.decode(HbA1cDisplayUnit.self, forKey: .hbA1cDisplayUnit) {
+            settings.hbA1cDisplayUnit = hbA1cDisplayUnit
         }
 
         if let maxCarbs = try? container.decode(Decimal.self, forKey: .maxCarbs) {

+ 16 - 0
FreeAPS/Sources/Models/HbA1cDisplayUnit.swift

@@ -0,0 +1,16 @@
+import Foundation
+
+enum HbA1cDisplayUnit: String, JSON, CaseIterable, Identifiable, Codable, Hashable {
+    var id: String { rawValue }
+    case percent
+    case mmolMol
+
+    var displayName: String {
+        switch self {
+        case .percent:
+            return NSLocalizedString("Percent", comment: "")
+        case .mmolMol:
+            return NSLocalizedString("mmol/mol", comment: "")
+        }
+    }
+}

+ 16 - 0
FreeAPS/Sources/Models/TimeInRangeChartStyle.swift

@@ -0,0 +1,16 @@
+import Foundation
+
+enum TimeInRangeChartStyle: String, JSON, CaseIterable, Identifiable, Codable, Hashable {
+    var id: String { rawValue }
+    case vertical
+    case horizontal
+
+    var displayName: String {
+        switch self {
+        case .vertical:
+            return NSLocalizedString("Vertical", comment: "")
+        case .horizontal:
+            return NSLocalizedString("Horizontal", comment: "")
+        }
+    }
+}

+ 2 - 2
FreeAPS/Sources/Models/TotalInsulinDisplayType.swift

@@ -14,9 +14,9 @@ enum TotalInsulinDisplayType: String, JSON, CaseIterable, Identifiable, Codable,
     var displayName: String {
         switch self {
         case .totalDailyDose:
-            return NSLocalizedString("Total Daily Dose", comment: "")
+            return NSLocalizedString("TDD", comment: "")
         case .totalInsulinInScope:
-            return NSLocalizedString("Total Insulin in Scope", comment: "")
+            return NSLocalizedString("TINS", comment: "")
         }
     }
 }

+ 3 - 3
FreeAPS/Sources/Modules/Home/HomeStateModel.swift

@@ -52,7 +52,7 @@ extension Home {
         var highGlucose: Decimal = 180
         var currentGlucoseTarget: Decimal = 100
         var glucoseColorScheme: GlucoseColorScheme = .staticColor
-        var overrideUnit: Bool = false
+        var hbA1cDisplayUnit: HbA1cDisplayUnit = .percent
         var displayXgridLines: Bool = false
         var displayYgridLines: Bool = false
         var thresholdLines: Bool = false
@@ -333,7 +333,7 @@ extension Home {
             maxValue = settingsManager.preferences.autosensMax
             lowGlucose = settingsManager.settings.low
             highGlucose = settingsManager.settings.high
-            overrideUnit = settingsManager.settings.overrideHbA1cUnit
+            hbA1cDisplayUnit = settingsManager.settings.hbA1cDisplayUnit
             displayXgridLines = settingsManager.settings.xGridLines
             displayYgridLines = settingsManager.settings.yGridLines
             thresholdLines = settingsManager.settings.rulerMarks
@@ -589,7 +589,7 @@ extension Home.StateModel:
         Task {
             await getCurrentGlucoseTarget()
         }
-        overrideUnit = settingsManager.settings.overrideHbA1cUnit
+        hbA1cDisplayUnit = settingsManager.settings.hbA1cDisplayUnit
         glucoseColorScheme = settingsManager.settings.glucoseColorScheme
         displayXgridLines = settingsManager.settings.xGridLines
         displayYgridLines = settingsManager.settings.yGridLines

+ 4 - 4
FreeAPS/Sources/Modules/Stat/StatStateModel.swift

@@ -9,8 +9,8 @@ extension Stat {
         @ObservationIgnored @Injected() var settings: SettingsManager!
         var highLimit: Decimal = 10 / 0.0555
         var lowLimit: Decimal = 4 / 0.0555
-        var overrideUnit: Bool = false
-        var layingChart: Bool = false
+        var hbA1cDisplayUnit: HbA1cDisplayUnit = .percent
+        var timeInRangeChartStyle: TimeInRangeChartStyle = .vertical
         var units: GlucoseUnits = .mgdL
         var glucoseFromPersistence: [GlucoseStored] = []
 
@@ -34,8 +34,8 @@ extension Stat {
             highLimit = settingsManager.settings.high
             lowLimit = settingsManager.settings.low
             units = settingsManager.settings.units
-            overrideUnit = settingsManager.settings.overrideHbA1cUnit
-            layingChart = settingsManager.settings.oneDimensionalGraph
+            hbA1cDisplayUnit = settingsManager.settings.hbA1cDisplayUnit
+            timeInRangeChartStyle = settingsManager.settings.timeInRangeChartStyle
         }
 
         func setupGlucoseArray(for duration: Duration) {

+ 7 - 7
FreeAPS/Sources/Modules/Stat/View/ChartsView.swift

@@ -7,8 +7,8 @@ struct ChartsView: View {
     let highLimit: Decimal
     let lowLimit: Decimal
     let units: GlucoseUnits
-    let overrideUnit: Bool
-    let standing: Bool
+    let hbA1cDisplayUnit: HbA1cDisplayUnit
+    let timeInRangeChartStyle: TimeInRangeChartStyle
 
     let glucose: [GlucoseStored]
 
@@ -21,7 +21,7 @@ struct ChartsView: View {
     var body: some View {
         glucoseChart
         Rectangle().fill(.cyan.opacity(0.2)).frame(maxHeight: 3)
-        if standing {
+        if timeInRangeChartStyle == .horizontal {
             VStack {
                 tirChart
                 Rectangle().fill(.cyan.opacity(0.2)).frame(maxHeight: 3)
@@ -39,15 +39,15 @@ struct ChartsView: View {
         highLimit: Decimal,
         lowLimit: Decimal,
         units: GlucoseUnits,
-        overrideUnit: Bool,
-        standing: Bool,
+        hbA1cDisplayUnit: HbA1cDisplayUnit,
+        timeInRangeChartStyle: TimeInRangeChartStyle,
         glucose: [GlucoseStored]
     ) {
         self.highLimit = highLimit
         self.lowLimit = lowLimit
         self.units = units
-        self.overrideUnit = overrideUnit
-        self.standing = standing
+        self.hbA1cDisplayUnit = hbA1cDisplayUnit
+        self.timeInRangeChartStyle = timeInRangeChartStyle
         self.glucose = glucose
     }
 

+ 15 - 15
FreeAPS/Sources/Modules/Stat/View/StatRootView.swift

@@ -45,7 +45,7 @@ extension Stat {
                         highLimit: state.highLimit,
                         lowLimit: state.lowLimit,
                         units: state.units,
-                        overrideUnit: state.overrideUnit
+                        hbA1cDisplayUnit: state.hbA1cDisplayUnit
                     )
                 case .Day:
                     StatsView(
@@ -53,7 +53,7 @@ extension Stat {
                         highLimit: state.highLimit,
                         lowLimit: state.lowLimit,
                         units: state.units,
-                        overrideUnit: state.overrideUnit
+                        hbA1cDisplayUnit: state.hbA1cDisplayUnit
                     )
                 case .Week:
                     StatsView(
@@ -61,7 +61,7 @@ extension Stat {
                         highLimit: state.highLimit,
                         lowLimit: state.lowLimit,
                         units: state.units,
-                        overrideUnit: state.overrideUnit
+                        hbA1cDisplayUnit: state.hbA1cDisplayUnit
                     )
                 case .Month:
                     StatsView(
@@ -69,7 +69,7 @@ extension Stat {
                         highLimit: state.highLimit,
                         lowLimit: state.lowLimit,
                         units: state.units,
-                        overrideUnit: state.overrideUnit
+                        hbA1cDisplayUnit: state.hbA1cDisplayUnit
                     )
                 case .Total:
                     StatsView(
@@ -77,7 +77,7 @@ extension Stat {
                         highLimit: state.highLimit,
                         lowLimit: state.lowLimit,
                         units: state.units,
-                        overrideUnit: state.overrideUnit
+                        hbA1cDisplayUnit: state.hbA1cDisplayUnit
                     )
                 }
             }
@@ -90,8 +90,8 @@ extension Stat {
                     highLimit: state.highLimit,
                     lowLimit: state.lowLimit,
                     units: state.units,
-                    overrideUnit: state.overrideUnit,
-                    standing: state.layingChart,
+                    hbA1cDisplayUnit: state.hbA1cDisplayUnit,
+                    timeInRangeChartStyle: state.timeInRangeChartStyle,
                     glucose: state.glucoseFromPersistence
                 )
             case .Day:
@@ -99,8 +99,8 @@ extension Stat {
                     highLimit: state.highLimit,
                     lowLimit: state.lowLimit,
                     units: state.units,
-                    overrideUnit: state.overrideUnit,
-                    standing: state.layingChart,
+                    hbA1cDisplayUnit: state.hbA1cDisplayUnit,
+                    timeInRangeChartStyle: state.timeInRangeChartStyle,
                     glucose: state.glucoseFromPersistence
                 )
             case .Week:
@@ -108,8 +108,8 @@ extension Stat {
                     highLimit: state.highLimit,
                     lowLimit: state.lowLimit,
                     units: state.units,
-                    overrideUnit: state.overrideUnit,
-                    standing: state.layingChart,
+                    hbA1cDisplayUnit: state.hbA1cDisplayUnit,
+                    timeInRangeChartStyle: state.timeInRangeChartStyle,
                     glucose: state.glucoseFromPersistence
                 )
             case .Month:
@@ -117,8 +117,8 @@ extension Stat {
                     highLimit: state.highLimit,
                     lowLimit: state.lowLimit,
                     units: state.units,
-                    overrideUnit: state.overrideUnit,
-                    standing: state.layingChart,
+                    hbA1cDisplayUnit: state.hbA1cDisplayUnit,
+                    timeInRangeChartStyle: state.timeInRangeChartStyle,
                     glucose: state.glucoseFromPersistence
                 )
             case .Total:
@@ -126,8 +126,8 @@ extension Stat {
                     highLimit: state.highLimit,
                     lowLimit: state.lowLimit,
                     units: state.units,
-                    overrideUnit: state.overrideUnit,
-                    standing: state.layingChart,
+                    hbA1cDisplayUnit: state.hbA1cDisplayUnit,
+                    timeInRangeChartStyle: state.timeInRangeChartStyle,
                     glucose: state.glucoseFromPersistence
                 )
             }

+ 13 - 5
FreeAPS/Sources/Modules/Stat/View/StatsView.swift

@@ -11,7 +11,7 @@ struct StatsView: View {
     var highLimit: Decimal
     var lowLimit: Decimal
     var units: GlucoseUnits
-    var overrideUnit: Bool
+    var hbA1cDisplayUnit: HbA1cDisplayUnit
 
     private let conversionFactor = 0.0555
 
@@ -30,7 +30,7 @@ struct StatsView: View {
         highLimit: Decimal,
         lowLimit: Decimal,
         units: GlucoseUnits,
-        overrideUnit: Bool
+        hbA1cDisplayUnit: HbA1cDisplayUnit
     ) {
         _fetchRequest = FetchRequest<LoopStatRecord>(
             sortDescriptors: [NSSortDescriptor(key: "start", ascending: false)],
@@ -45,7 +45,7 @@ struct StatsView: View {
         self.highLimit = highLimit
         self.lowLimit = lowLimit
         self.units = units
-        self.overrideUnit = overrideUnit
+        self.hbA1cDisplayUnit = hbA1cDisplayUnit
     }
 
     var loops: some View {
@@ -127,8 +127,16 @@ struct StatsView: View {
 
     var hba1c: some View {
         HStack(spacing: 50) {
-            let useUnit: GlucoseUnits = (units == .mmolL && overrideUnit) ? .mgdL :
-                (units == .mgdL && overrideUnit || units == .mmolL) ? .mmolL : .mgdL
+            let useUnit: GlucoseUnits = {
+                if units == .mmolL && hbA1cDisplayUnit == .mmolMol {
+                    return .mgdL
+                } else if (units == .mgdL && hbA1cDisplayUnit == .mmolMol) || units == .mmolL {
+                    return .mmolL
+                } else {
+                    return .mgdL
+                }
+            }()
+
             let hba1cs = glucoseStats()
             // First date
             let previous = glucose.last?.date ?? Date()

+ 6 - 4
FreeAPS/Sources/Modules/UserInterfaceSettings/UserInterfaceSettingsStateModel.swift

@@ -2,18 +2,18 @@ import SwiftUI
 
 extension UserInterfaceSettings {
     final class StateModel: BaseStateModel<Provider> {
-        @Published var overrideHbA1cUnit = false
         @Published var low: Decimal = 70
         @Published var high: Decimal = 180
         @Published var xGridLines = false
         @Published var yGridLines: Bool = false
-        @Published var oneDimensionalGraph = false
         @Published var rulerMarks: Bool = true
         @Published var forecastDisplayType: ForecastDisplayType = .cone
         @Published var totalInsulinDisplayType: TotalInsulinDisplayType = .totalDailyDose
         @Published var showCarbsRequiredBadge: Bool = true
         @Published var carbsRequiredThreshold: Decimal = 0
         @Published var glucoseColorScheme: GlucoseColorScheme = .staticColor
+        @Published var hbA1cDisplayUnit: HbA1cDisplayUnit = .percent
+        @Published var timeInRangeChartStyle: TimeInRangeChartStyle = .vertical
 
         var units: GlucoseUnits = .mgdL
 
@@ -21,11 +21,9 @@ extension UserInterfaceSettings {
             let units = settingsManager.settings.units
             self.units = units
 
-            subscribeSetting(\.overrideHbA1cUnit, on: $overrideHbA1cUnit) { overrideHbA1cUnit = $0 }
             subscribeSetting(\.xGridLines, on: $xGridLines) { xGridLines = $0 }
             subscribeSetting(\.yGridLines, on: $yGridLines) { yGridLines = $0 }
             subscribeSetting(\.rulerMarks, on: $rulerMarks) { rulerMarks = $0 }
-            subscribeSetting(\.oneDimensionalGraph, on: $oneDimensionalGraph) { oneDimensionalGraph = $0 }
 
             subscribeSetting(\.forecastDisplayType, on: $forecastDisplayType) { forecastDisplayType = $0 }
 
@@ -43,6 +41,10 @@ extension UserInterfaceSettings {
             ) { carbsRequiredThreshold = $0 }
 
             subscribeSetting(\.glucoseColorScheme, on: $glucoseColorScheme) { glucoseColorScheme = $0 }
+
+            subscribeSetting(\.hbA1cDisplayUnit, on: $hbA1cDisplayUnit) { hbA1cDisplayUnit = $0 }
+
+            subscribeSetting(\.timeInRangeChartStyle, on: $timeInRangeChartStyle) { timeInRangeChartStyle = $0 }
         }
     }
 }

+ 82 - 43
FreeAPS/Sources/Modules/UserInterfaceSettings/View/UserInterfaceSettingsRootView.swift

@@ -372,10 +372,10 @@ extension UserInterfaceSettings {
                 }.listRowBackground(Color.chart)
 
                 Section {
-                    VStack {
+                    VStack(alignment: .leading) {
                         Picker(
                             selection: $state.totalInsulinDisplayType,
-                            label: Text("Total Insulin Display Type")
+                            label: Text("Total Insulin Display Type").multilineTextAlignment(.leading)
                         ) {
                             ForEach(TotalInsulinDisplayType.allCases) { selection in
                                 Text(selection.displayName).tag(selection)
@@ -419,49 +419,88 @@ extension UserInterfaceSettings {
                     }.padding(.bottom)
                 }.listRowBackground(Color.chart)
 
-                // TODO: this needs to be a picker: mmol/L or %
-                SettingInputSection(
-                    decimalValue: $decimalPlaceholder,
-                    booleanValue: $state.overrideHbA1cUnit,
-                    shouldDisplayHint: $shouldDisplayHint,
-                    selectedVerboseHint: Binding(
-                        get: { selectedVerboseHint },
-                        set: {
-                            selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = "Override HbA1c Unit"
-                        }
-                    ),
-                    units: state.units,
-                    type: .boolean,
-                    label: "Override HbA1c Unit",
-                    miniHint: "Display HbA1c in mmol/mol or %.",
-                    verboseHint: Text(
-                        "Choose which format you'd prefer the HbA1c value in the statistics view as a percentage (Example: 6.5%) or mmol/mol (Example: 48 mmol/mol)"
-                    ),
-                    headerText: "Trio Statistics"
-                )
+                Section(
+                    header: Text("Trio Statistics"),
+                    content: {
+                        VStack {
+                            Picker(
+                                selection: $state.hbA1cDisplayUnit,
+                                label: Text("HbA1c Display Unit")
+                            ) {
+                                ForEach(HbA1cDisplayUnit.allCases) { selection in
+                                    Text(selection.displayName).tag(selection)
+                                }
+                            }.padding(.top)
 
-                // TODO: this needs to be a picker: choose bar chart or progress bar
-                SettingInputSection(
-                    decimalValue: $decimalPlaceholder,
-                    booleanValue: $state.oneDimensionalGraph,
-                    shouldDisplayHint: $shouldDisplayHint,
-                    selectedVerboseHint: Binding(
-                        get: { selectedVerboseHint },
-                        set: {
-                            selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = "Standing / Laying TIR Chart"
-                        }
-                    ),
-                    units: state.units,
-                    type: .boolean,
-                    label: "Standing / Laying TIR Chart",
-                    miniHint: "Select a vertical chart or horizontal chart to display your Time in Range Statistics.",
-                    verboseHint: VStack {
-                        Text("Select a vertical / standing chart by turning this feature OFF.")
-                        Text("Select a horizontal / laying chart by turning this feature ON.")
+                            HStack(alignment: .center) {
+                                Text(
+                                    "Choose to display HbA1c in % or mmol/mol."
+                                )
+                                .font(.footnote)
+                                .foregroundColor(.secondary)
+                                .lineLimit(nil)
+                                Spacer()
+                                Button(
+                                    action: {
+                                        hintLabel = "HbA1c Display Unit"
+                                        selectedVerboseHint =
+                                            AnyView(
+                                                Text(
+                                                    "Choose which format you'd prefer the HbA1c value in the statistics view as a percentage (Example: 6.5%) or mmol/mol (Example: 48 mmol/mol)."
+                                                )
+                                            )
+                                        shouldDisplayHint.toggle()
+                                    },
+                                    label: {
+                                        HStack {
+                                            Image(systemName: "questionmark.circle")
+                                        }
+                                    }
+                                ).buttonStyle(BorderlessButtonStyle())
+                            }.padding(.top)
+                        }.padding(.bottom)
                     }
-                )
+                ).listRowBackground(Color.chart)
+
+                Section {
+                    VStack(alignment: .leading) {
+                        Picker(
+                            selection: $state.timeInRangeChartStyle,
+                            label: Text("Time in Range Chart Style").multilineTextAlignment(.leading)
+                        ) {
+                            ForEach(TimeInRangeChartStyle.allCases) { selection in
+                                Text(selection.displayName).tag(selection)
+                            }
+                        }.padding(.top)
+
+                        HStack(alignment: .center) {
+                            Text(
+                                "Choose to display the Time in Range chart as a vertical bar chart or horizontal line chart."
+                            )
+                            .font(.footnote)
+                            .foregroundColor(.secondary)
+                            .lineLimit(nil)
+                            Spacer()
+                            Button(
+                                action: {
+                                    hintLabel = "Time in Range Chart Style"
+                                    selectedVerboseHint =
+                                        AnyView(
+                                            Text(
+                                                "Choose which style for the time in range chart you'd prefer: a standing, i.e., vertical, bar chart or a laying, i.e., horizontal, line chart."
+                                            )
+                                        )
+                                    shouldDisplayHint.toggle()
+                                },
+                                label: {
+                                    HStack {
+                                        Image(systemName: "questionmark.circle")
+                                    }
+                                }
+                            ).buttonStyle(BorderlessButtonStyle())
+                        }.padding(.top)
+                    }.padding(.bottom)
+                }.listRowBackground(Color.chart)
 
                 SettingInputSection(
                     decimalValue: $state.carbsRequiredThreshold,