Deniz Cengiz 1 год назад
Родитель
Сommit
420b0c7966

+ 4 - 0
Trio.xcodeproj/project.pbxproj

@@ -579,6 +579,7 @@
 		DDE1796F2C910127003CDDB7 /* OrefDetermination+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE1794F2C910127003CDDB7 /* OrefDetermination+CoreDataProperties.swift */; };
 		DDE179702C910127003CDDB7 /* OverrideStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE179502C910127003CDDB7 /* OverrideStored+CoreDataClass.swift */; };
 		DDE179712C910127003CDDB7 /* OverrideStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDE179512C910127003CDDB7 /* OverrideStored+CoreDataProperties.swift */; };
+		DDEBB05C2D89E9050032305D /* TimeInRangeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDEBB05B2D89E9050032305D /* TimeInRangeType.swift */; };
 		DDF847DD2C5C28720049BB3B /* LiveActivitySettingsDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF847DC2C5C28720049BB3B /* LiveActivitySettingsDataFlow.swift */; };
 		DDF847DF2C5C28780049BB3B /* LiveActivitySettingsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF847DE2C5C28780049BB3B /* LiveActivitySettingsProvider.swift */; };
 		DDF847E12C5C287F0049BB3B /* LiveActivitySettingsStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF847E02C5C287F0049BB3B /* LiveActivitySettingsStateModel.swift */; };
@@ -1307,6 +1308,7 @@
 		DDE1794F2C910127003CDDB7 /* OrefDetermination+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OrefDetermination+CoreDataProperties.swift"; sourceTree = "<group>"; };
 		DDE179502C910127003CDDB7 /* OverrideStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OverrideStored+CoreDataClass.swift"; sourceTree = "<group>"; };
 		DDE179512C910127003CDDB7 /* OverrideStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OverrideStored+CoreDataProperties.swift"; sourceTree = "<group>"; };
+		DDEBB05B2D89E9050032305D /* TimeInRangeType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeInRangeType.swift; sourceTree = "<group>"; };
 		DDF847DC2C5C28720049BB3B /* LiveActivitySettingsDataFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveActivitySettingsDataFlow.swift; sourceTree = "<group>"; };
 		DDF847DE2C5C28780049BB3B /* LiveActivitySettingsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveActivitySettingsProvider.swift; sourceTree = "<group>"; };
 		DDF847E02C5C287F0049BB3B /* LiveActivitySettingsStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveActivitySettingsStateModel.swift; sourceTree = "<group>"; };
@@ -2095,6 +2097,7 @@
 		388E5A5925B6F0250019842D /* Models */ = {
 			isa = PBXGroup;
 			children = (
+				DDEBB05B2D89E9050032305D /* TimeInRangeType.swift */,
 				3B2F77852D7E52ED005ED9FA /* TDD.swift */,
 				DD4FFF322D458EE600B6CFF9 /* GarminWatchState.swift */,
 				DD3078692D42F94000DE0490 /* GarminDevice.swift */,
@@ -4058,6 +4061,7 @@
 				CE7CA3542A064973004BE681 /* TempPresetsIntentRequest.swift in Sources */,
 				58A3D5442C96DE11003F90FC /* TempTargetStored+Helper.swift in Sources */,
 				DD6B7CB42C7B71F700B75029 /* ForecastDisplayType.swift in Sources */,
+				DDEBB05C2D89E9050032305D /* TimeInRangeType.swift in Sources */,
 				DD5DC9F32CF3D9DD00AB8703 /* AdjustmentsStateModel+TempTargets.swift in Sources */,
 				F5F7E6C1B7F098F59EB67EC5 /* TargetsEditorDataFlow.swift in Sources */,
 				DD17453A2C55BFA600211FAC /* AlgorithmAdvancedSettingsDataFlow.swift in Sources */,

+ 2 - 1
Trio/Resources/json/defaults/freeaps/freeaps_settings.json

@@ -47,5 +47,6 @@
   "lockScreenView": "simple",
   "useCalendar": false,
   "displayCalendarIOBandCOB": false,
-  "displayCalendarEmojis": false
+  "displayCalendarEmojis": false,
+  "timeInRangeType": "timeInTightRange"
 }

+ 21 - 0
Trio/Sources/Localizations/Main/Localizable.xcstrings

@@ -48574,6 +48574,9 @@
         }
       }
     },
+    "Choose type type of time in range to be used for Trio's statistics." : {
+
+    },
     "Choose whether or not to display one or both X- and Y-Axis grid lines." : {
       "localizations" : {
         "bg" : {
@@ -48980,6 +48983,9 @@
         }
       }
     },
+    "Choose which type of time in range type want Trio to adopt for all its statistical charts and displays:" : {
+
+    },
     "Clear" : {
       "comment" : "Button",
       "extractionState" : "manual",
@@ -176937,6 +176943,12 @@
         }
       }
     },
+    "Time in Normoglycemia (TING)" : {
+
+    },
+    "Time in Normoglycemia (TING):" : {
+
+    },
     "Time in Range Chart Style" : {
       "extractionState" : "stale",
       "localizations" : {
@@ -177038,6 +177050,15 @@
         }
       }
     },
+    "Time in Range Type" : {
+
+    },
+    "Time in Tight Range (TITR)" : {
+
+    },
+    "Time in Tight Range (TITR):" : {
+
+    },
     "Time interval between FPUs." : {
       "localizations" : {
         "bg" : {

+ 16 - 0
Trio/Sources/Models/TimeInRangeType.swift

@@ -0,0 +1,16 @@
+import Foundation
+
+enum TimeInRangeType: String, JSON, CaseIterable, Identifiable, Codable, Hashable {
+    var id: String { rawValue }
+    case timeInTightRange
+    case timeInNormoglycemia
+    var displayName: String {
+        switch self {
+        case .timeInTightRange:
+            return String(localized: "Time in Tight Range (TITR)", comment: "")
+
+        case .timeInNormoglycemia:
+            return String(localized: "Time in Normoglycemia (TING)", comment: "")
+        }
+    }
+}

+ 5 - 0
Trio/Sources/Models/TrioSettings.swift

@@ -69,6 +69,7 @@ struct TrioSettings: JSON, Equatable {
     var useLiveActivity: Bool = false
     var lockScreenView: LockScreenView = .simple
     var bolusShortcut: BolusShortcutLimit = .notAllowed
+    var timeInRangeType: TimeInRangeType = .timeInTightRange
 }
 
 extension TrioSettings: Decodable {
@@ -295,6 +296,10 @@ extension TrioSettings: Decodable {
             settings.bolusShortcut = bolusShortcut
         }
 
+        if let timeInRangeType = try? container.decode(TimeInRangeType.self, forKey: .timeInRangeType) {
+            settings.timeInRangeType = timeInRangeType
+        }
+
         self = settings
     }
 }

+ 3 - 0
Trio/Sources/Modules/UserInterfaceSettings/UserInterfaceSettingsStateModel.swift

@@ -12,6 +12,7 @@ extension UserInterfaceSettings {
         @Published var carbsRequiredThreshold: Decimal = 0
         @Published var glucoseColorScheme: GlucoseColorScheme = .staticColor
         @Published var eA1cDisplayUnit: EstimatedA1cDisplayUnit = .percent
+        @Published var timeInRangeType: TimeInRangeType = .timeInTightRange
 
         var units: GlucoseUnits = .mgdL
 
@@ -39,6 +40,8 @@ extension UserInterfaceSettings {
             subscribeSetting(\.glucoseColorScheme, on: $glucoseColorScheme) { glucoseColorScheme = $0 }
 
             subscribeSetting(\.eA1cDisplayUnit, on: $eA1cDisplayUnit) { eA1cDisplayUnit = $0 }
+
+            subscribeSetting(\.timeInRangeType, on: $timeInRangeType) { timeInRangeType = $0 }
         }
     }
 }

+ 69 - 0
Trio/Sources/Modules/UserInterfaceSettings/View/UserInterfaceSettingsRootView.swift

@@ -419,6 +419,75 @@ extension UserInterfaceSettings {
                     }
                 ).listRowBackground(Color.chart)
 
+                Section {
+                    VStack(alignment: .leading) {
+                        Picker(
+                            selection: $state.timeInRangeType,
+                            label: Text("Time in Range Type").multilineTextAlignment(.leading)
+                        ) {
+                            ForEach(TimeInRangeType.allCases) { selection in
+                                Text(selection.displayName).tag(selection)
+                            }
+                        }.padding(.top)
+
+                        HStack(alignment: .center) {
+                            Text(
+                                "Choose type type of time in range to be used for Trio's statistics."
+                            )
+                            .font(.footnote)
+                            .foregroundColor(.secondary)
+                            .lineLimit(nil)
+                            Spacer()
+                            Button(
+                                action: {
+                                    hintLabel = String(localized: "Time in Range Type")
+                                    selectedVerboseHint =
+                                        AnyView(
+                                            VStack(
+                                                alignment: .leading,
+                                                spacing: 10
+                                            ) {
+                                                Text(
+                                                    "Choose which type of time in range type want Trio to adopt for all its statistical charts and displays:"
+                                                )
+                                                VStack(
+                                                    alignment: .leading,
+                                                    spacing: 5
+                                                ) {
+                                                    Text(
+                                                        "Time in Tight Range (TITR):"
+                                                    )
+                                                    .bold()
+                                                    Text(
+                                                        "Uses the fairly established Time in Tight Range definition, which is defined as time between \(state.units == .mgdL ? Decimal(70) : 70.asMmolL) and \(state.units == .mgdL ? Decimal(140) : 140.asMmolL) \(state.units.rawValue)."
+                                                    )
+                                                }
+                                                VStack(
+                                                    alignment: .leading,
+                                                    spacing: 5
+                                                ) {
+                                                    Text(
+                                                        "Time in Normoglycemia (TING):"
+                                                    )
+                                                    .bold()
+                                                    Text(
+                                                        "Uses the very new – first discussed at ATTD 2025 in Amsterdam, NL – Time in Normoglycemia definition, which adopts the normoglycemic minimum threshold (\(state.units == .mgdL ? Decimal(63) : 53.asMmolL) \(state.units.rawValue)) and \(state.units == .mgdL ? Decimal(140) : 140.asMmolL) \(state.units.rawValue)."
+                                                    )
+                                                }
+                                            }
+                                        )
+                                    shouldDisplayHint.toggle()
+                                },
+                                label: {
+                                    HStack {
+                                        Image(systemName: "questionmark.circle")
+                                    }
+                                }
+                            ).buttonStyle(BorderlessButtonStyle())
+                        }.padding(.top)
+                    }.padding(.bottom)
+                }.listRowBackground(Color.chart)
+
                 SettingInputSection(
                     decimalValue: $state.carbsRequiredThreshold,
                     booleanValue: $state.showCarbsRequiredBadge,