Przeglądaj źródła

Merge branch 'dev_ns' into maxIOB-maxCOB

Mike Plante 1 rok temu
rodzic
commit
b6cb80b3a7
100 zmienionych plików z 181878 dodań i 51767 usunięć
  1. 1 1
      DanaKit
  2. 12 2
      Model/CoreDataStack.swift
  3. 14 0
      Model/Helper/AdjustmentStored+Helper.swift
  4. 9 6
      Model/Helper/CoreDataError.swift
  5. 0 10
      Model/Helper/OverrideStored+helper.swift
  6. 87 3
      Model/Helper/PumpEvent+helper.swift
  7. 1 1
      OmniBLE
  8. 1 1
      OmniKit
  9. 11 1
      Trio Watch App Extension/Helper/Helper+ButtonStyles.swift
  10. 20 0
      Trio Watch App Extension/Views/BolusProgressOverlay.swift
  11. 5 0
      Trio Watch App Extension/Views/GlucoseChartView.swift
  12. 13 4
      Trio Watch App Extension/Views/TrioMainWatchView.swift
  13. 14 0
      Trio Watch App Extension/WatchState.swift
  14. 39 130
      Trio.xcodeproj/project.pbxproj
  15. 0 23
      Trio/Resources/Base.lproj/InfoPlist.strings
  16. 1059 0
      Trio/Resources/InfoPlist.xcstrings
  17. 0 20
      Trio/Resources/ar.lproj/InfoPlist.strings
  18. 0 20
      Trio/Resources/ca.lproj/InfoPlist.strings
  19. 0 20
      Trio/Resources/da.lproj/InfoPlist.strings
  20. 0 20
      Trio/Resources/de.lproj/InfoPlist.strings
  21. 0 20
      Trio/Resources/es.lproj/InfoPlist.strings
  22. 0 20
      Trio/Resources/fi.lproj/InfoPlist.strings
  23. 0 20
      Trio/Resources/fr.lproj/InfoPlist.strings
  24. 0 20
      Trio/Resources/he.lproj/InfoPlist.strings
  25. 0 20
      Trio/Resources/hu.lproj/InfoPlist.strings
  26. 0 20
      Trio/Resources/it.lproj/InfoPlist.strings
  27. 0 20
      Trio/Resources/nb.lproj/InfoPlist.strings
  28. 0 20
      Trio/Resources/nl.lproj/InfoPlist.strings
  29. 0 20
      Trio/Resources/pl.lproj/InfoPlist.strings
  30. 0 20
      Trio/Resources/pt-BR.lproj/InfoPlist.strings
  31. 0 20
      Trio/Resources/pt-PT.lproj/InfoPlist.strings
  32. 0 20
      Trio/Resources/ru.lproj/InfoPlist.strings
  33. 0 20
      Trio/Resources/sk.lproj/InfoPlist.strings
  34. 0 20
      Trio/Resources/sv.lproj/InfoPlist.strings
  35. 0 20
      Trio/Resources/tr.lproj/InfoPlist.strings
  36. 0 20
      Trio/Resources/uk.lproj/InfoPlist.strings
  37. 0 20
      Trio/Resources/vi.lproj/InfoPlist.strings
  38. 0 20
      Trio/Resources/zh-Hans.lproj/InfoPlist.strings
  39. 9 3
      Trio/Sources/APS/APSManager.swift
  40. 1 1
      Trio/Sources/APS/CGM/AppGroupSource.swift
  41. 10 9
      Trio/Sources/APS/CGM/CGMType.swift
  42. 5 5
      Trio/Sources/APS/CGM/PluginSource.swift
  43. 0 8
      Trio/Sources/APS/DeviceDataManager.swift
  44. 27 4
      Trio/Sources/APS/FetchGlucoseManager.swift
  45. 25 10
      Trio/Sources/APS/OpenAPS/OpenAPS.swift
  46. 1 1
      Trio/Sources/APS/Storage/ContactImageStorage.swift
  47. 2 2
      Trio/Sources/APS/Storage/GlucoseStorage.swift
  48. 3 3
      Trio/Sources/APS/Storage/OverrideStorage.swift
  49. 4 2
      Trio/Sources/APS/Storage/PumpHistoryStorage.swift
  50. 3 3
      Trio/Sources/APS/Storage/TempTargetsStorage.swift
  51. 22 7
      Trio/Sources/Application/TrioApp.swift
  52. 4 4
      Trio/Sources/Helpers/BuildDetails.swift
  53. 15 0
      Trio/Sources/Helpers/CGMOptions.swift
  54. 4 4
      Trio/Sources/Helpers/HKUnit.swift
  55. 180308 0
      Trio/Sources/Localizations/Main/Localizable.xcstrings
  56. 0 2223
      Trio/Sources/Localizations/Main/ar.lproj/Localizable.strings
  57. 0 1917
      Trio/Sources/Localizations/Main/ca.lproj/Localizable.strings
  58. 0 2184
      Trio/Sources/Localizations/Main/da.lproj/Localizable.strings
  59. 0 2229
      Trio/Sources/Localizations/Main/de.lproj/Localizable.strings
  60. 0 2231
      Trio/Sources/Localizations/Main/en.lproj/Localizable.strings
  61. 0 2228
      Trio/Sources/Localizations/Main/es.lproj/Localizable.strings
  62. 0 2229
      Trio/Sources/Localizations/Main/fi.lproj/Localizable.strings
  63. 0 2220
      Trio/Sources/Localizations/Main/fr.lproj/Localizable.strings
  64. 0 2223
      Trio/Sources/Localizations/Main/he.lproj/Localizable.strings
  65. 0 2178
      Trio/Sources/Localizations/Main/hu.lproj/Localizable.strings
  66. 0 2225
      Trio/Sources/Localizations/Main/it.lproj/Localizable.strings
  67. 0 2223
      Trio/Sources/Localizations/Main/nb.lproj/Localizable.strings
  68. 0 2231
      Trio/Sources/Localizations/Main/nl.lproj/Localizable.strings
  69. 0 2225
      Trio/Sources/Localizations/Main/pl.lproj/Localizable.strings
  70. 0 2223
      Trio/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings
  71. 0 2223
      Trio/Sources/Localizations/Main/pt-PT.lproj/Localizable.strings
  72. 0 2226
      Trio/Sources/Localizations/Main/ru.lproj/Localizable.strings
  73. 0 2178
      Trio/Sources/Localizations/Main/sk.lproj/Localizable.strings
  74. 0 2223
      Trio/Sources/Localizations/Main/sv.lproj/Localizable.strings
  75. 0 2227
      Trio/Sources/Localizations/Main/tr.lproj/Localizable.strings
  76. 0 2223
      Trio/Sources/Localizations/Main/uk.lproj/Localizable.strings
  77. 0 2180
      Trio/Sources/Localizations/Main/vi.lproj/Localizable.strings
  78. 0 2225
      Trio/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings
  79. 4 0
      Trio/Sources/Logger/Logger.swift
  80. 16 16
      Trio/Sources/Models/ContactTrickEntry.swift
  81. 2 2
      Trio/Sources/Models/ForecastDisplayType.swift
  82. 2 2
      Trio/Sources/Models/HbA1cDisplayUnit.swift
  83. 2 2
      Trio/Sources/Models/LockScreenView.swift
  84. 2 2
      Trio/Sources/Models/TimeInRangeChartStyle.swift
  85. 2 2
      Trio/Sources/Models/TotalInsulinDisplayType.swift
  86. 2 0
      Trio/Sources/Models/WatchMessageKeys.swift
  87. 9 3
      Trio/Sources/Models/WatchState.swift
  88. 2 5
      Trio/Sources/Modules/Adjustments/AdjustmentsDataFlow.swift
  89. 2 2
      Trio/Sources/Modules/Adjustments/AdjustmentsStateModel+Extensions/AdjustmentsStateModel+Overrides.swift
  90. 5 5
      Trio/Sources/Modules/Adjustments/AdjustmentsStateModel+Extensions/AdjustmentsStateModel+TempTargets.swift
  91. 66 35
      Trio/Sources/Modules/AlgorithmAdvancedSettings/View/AlgorithmAdvancedSettingsRootView.swift
  92. 9 9
      Trio/Sources/Modules/AutosensSettings/View/AutosensSettingsRootView.swift
  93. 0 5
      Trio/Sources/Modules/BasalProfileEditor/View/BasalProfileEditorRootView.swift
  94. 4 0
      Trio/Sources/Modules/Base/BaseStateModel.swift
  95. 15 15
      Trio/Sources/Modules/BolusCalculatorConfig/View/BolusCalculatorConfigRootView.swift
  96. 0 9
      Trio/Sources/Modules/CGM/CGMDataFlow.swift
  97. 0 5
      Trio/Sources/Modules/CGM/CGMProvider.swift
  98. 0 266
      Trio/Sources/Modules/CGM/View/CGMRootView.swift
  99. 5 0
      Trio/Sources/Modules/CGMSettings/CGMSettingsDataFlow.swift
  100. 0 0
      Trio/Sources/Modules/CGMSettings/CGMSettingsProvider.swift

+ 1 - 1
DanaKit

@@ -1 +1 @@
-Subproject commit b07f236677b205d31d7ecf6144970348e8d5a3fe
+Subproject commit b44c5df260a8b38d6fd0b5851cc3aac5da5d9d57

+ 12 - 2
Model/CoreDataStack.swift

@@ -105,7 +105,7 @@ class CoreDataStack: ObservableObject {
     private func fetchPersistentHistoryTransactionsAndChanges() async throws {
         let taskContext = newTaskContext()
         taskContext.name = "persistentHistoryContext"
-//        debugPrint("Start fetching persistent history changes from the store ... \(DebuggingIdentifiers.inProgress)")
+        //        debugPrint("Start fetching persistent history changes from the store ... \(DebuggingIdentifiers.inProgress)")
 
         try await taskContext.perform {
             // Execute the persistent history change since the last transaction
@@ -120,7 +120,7 @@ class CoreDataStack: ObservableObject {
     }
 
     private func mergePersistentHistoryChanges(from history: [NSPersistentHistoryTransaction]) {
-//        debugPrint("Received \(history.count) persistent history transactions")
+        //        debugPrint("Received \(history.count) persistent history transactions")
         // Update view context with objectIDs from history change request
         /// - Tag: mergeChanges
         let viewContext = persistentContainer.viewContext
@@ -150,6 +150,16 @@ class CoreDataStack: ObservableObject {
             }
         }
     }
+
+    func initializeStack() throws {
+        // Force initialization of persistent container
+        let container = persistentContainer
+
+        // Verify the store is loaded and available
+        guard container.persistentStoreCoordinator.persistentStores.isNotEmpty else {
+            throw CoreDataError.storeNotInitializedError
+        }
+    }
 }
 
 // MARK: - Delete

+ 14 - 0
Model/Helper/AdjustmentStored+Helper.swift

@@ -0,0 +1,14 @@
+import CoreData
+import Foundation
+
+extension NSPredicate {
+    static var lastActiveAdjustmentNotYetUploadedToNightscout: NSPredicate {
+        let date = Date.oneDayAgo
+        return NSPredicate(
+            format: "date >= %@ AND enabled == %@ AND isUploadedToNS == %@",
+            date as NSDate,
+            true as NSNumber,
+            false as NSNumber
+        )
+    }
+}

+ 9 - 6
Model/Helper/CoreDataError.swift

@@ -7,23 +7,26 @@ enum CoreDataError: Error {
     case persistentHistoryChangeError
     case unexpectedError(error: Error)
     case fetchError
+    case storeNotInitializedError
 }
 
 extension CoreDataError: LocalizedError {
     var errorDescription: String? {
         switch self {
         case .creationError:
-            return NSLocalizedString("Failed to create a new object.", comment: "")
+            return String(localized: "Failed to create a new object.", comment: "")
         case .batchInsertError:
-            return NSLocalizedString("Failed to execute a batch insert request.", comment: "")
+            return String(localized: "Failed to execute a batch insert request.", comment: "")
         case .batchDeleteError:
-            return NSLocalizedString("Failed to execute a batch delete request.", comment: "")
+            return String(localized: "Failed to execute a batch delete request.", comment: "")
         case .persistentHistoryChangeError:
-            return NSLocalizedString("Failed to execute a persistent history change request.", comment: "")
+            return String(localized: "Failed to execute a persistent history change request.", comment: "")
         case let .unexpectedError(error):
-            return NSLocalizedString("Received unexpected error. \(error.localizedDescription)", comment: "")
+            return String(localized: "Received unexpected error. \(error.localizedDescription)", comment: "")
         case .fetchError:
-            return NSLocalizedString("Failed to fetch object \(DebuggingIdentifiers.failed).", comment: "")
+            return String(localized: "Failed to fetch object \(DebuggingIdentifiers.failed).", comment: "")
+        case .storeNotInitializedError:
+            return String(localized: "Failed to initialize Core Data's persistent store.", comment: "")
         }
     }
 }

+ 0 - 10
Model/Helper/OverrideStored+helper.swift

@@ -14,16 +14,6 @@ extension NSPredicate {
             true as NSNumber
         )
     }
-
-    static var lastActiveOverrideNotYetUploadedToNightscout: NSPredicate {
-        let date = Date.oneDayAgo
-        return NSPredicate(
-            format: "date >= %@ AND enabled == %@ AND isUploadedToNS == %@",
-            date as NSDate,
-            true as NSNumber,
-            false as NSNumber
-        )
-    }
 }
 
 extension OverrideStored {

+ 87 - 3
Model/Helper/PumpEvent+helper.swift

@@ -102,7 +102,7 @@ struct BolusDTO: Codable {
     var isExternal: Bool
     var isSMB: Bool
     var duration: Int
-    var _type: String = "Bolus"
+    var _type: String = EventType.bolus.rawValue
 }
 
 struct TempBasalDTO: Codable {
@@ -110,14 +110,14 @@ struct TempBasalDTO: Codable {
     var timestamp: String
     var temp: String
     var rate: Double
-    var _type: String = "TempBasal"
+    var _type: String = EventType.tempBasal.rawValue
 }
 
 struct TempBasalDurationDTO: Codable {
     var id: String
     var timestamp: String
     var duration: Int
-    var _type: String = "TempBasalDuration"
+    var _type: String = EventType.tempBasalDuration.rawValue
 
     private enum CodingKeys: String, CodingKey {
         case id
@@ -127,11 +127,39 @@ struct TempBasalDurationDTO: Codable {
     }
 }
 
+struct SuspendDTO: Codable {
+    var id: String
+    var timestamp: String
+    var _type: String = EventType.pumpSuspend.rawValue
+}
+
+struct ResumeDTO: Codable {
+    var id: String
+    var timestamp: String
+    var _type: String = EventType.pumpResume.rawValue
+}
+
+struct RewindDTO: Codable {
+    var id: String
+    var timestamp: String
+    var _type: String = EventType.rewind.rawValue
+}
+
+struct PrimeDTO: Codable {
+    var id: String
+    var timestamp: String
+    var _type: String = EventType.prime.rawValue
+}
+
 // Mask distinct DTO subtypes with a common enum that conforms to Encodable
 enum PumpEventDTO: Encodable {
     case bolus(BolusDTO)
     case tempBasal(TempBasalDTO)
     case tempBasalDuration(TempBasalDurationDTO)
+    case suspend(SuspendDTO)
+    case resume(ResumeDTO)
+    case rewind(RewindDTO)
+    case prime(PrimeDTO)
 
     func encode(to encoder: Encoder) throws {
         switch self {
@@ -141,6 +169,14 @@ enum PumpEventDTO: Encodable {
             try tempBasal.encode(to: encoder)
         case let .tempBasalDuration(tempBasalDuration):
             try tempBasalDuration.encode(to: encoder)
+        case let .suspend(suspend):
+            try suspend.encode(to: encoder)
+        case let .resume(resume):
+            try resume.encode(to: encoder)
+        case let .rewind(rewind):
+            try rewind.encode(to: encoder)
+        case let .prime(prime):
+            try prime.encode(to: encoder)
         }
     }
 }
@@ -195,4 +231,52 @@ extension PumpEventStored {
         )
         return .tempBasalDuration(tempBasalDurationDTO)
     }
+
+    func toPumpSuspendDTO() -> PumpEventDTO? {
+        guard let id = id, let timestamp = timestamp, let type = type, type == EventType.pumpSuspend.rawValue else {
+            return nil
+        }
+
+        let suspendDTO = SuspendDTO(
+            id: id,
+            timestamp: PumpEventStored.dateFormatter.string(from: timestamp)
+        )
+        return .suspend(suspendDTO)
+    }
+
+    func toPumpResumeDTO() -> PumpEventDTO? {
+        guard let id = id, let timestamp = timestamp, let type = type, type == EventType.pumpResume.rawValue else {
+            return nil
+        }
+
+        let resumeDTO = ResumeDTO(
+            id: id,
+            timestamp: PumpEventStored.dateFormatter.string(from: timestamp)
+        )
+        return .resume(resumeDTO)
+    }
+
+    func toRewindDTO() -> PumpEventDTO? {
+        guard let id = id, let timestamp = timestamp, let type = type, type == EventType.rewind.rawValue else {
+            return nil
+        }
+
+        let rewindDTO = RewindDTO(
+            id: id,
+            timestamp: PumpEventStored.dateFormatter.string(from: timestamp)
+        )
+        return .rewind(rewindDTO)
+    }
+
+    func toPrimeDTO() -> PumpEventDTO? {
+        guard let id = id, let timestamp = timestamp, let type = type, type == EventType.prime.rawValue else {
+            return nil
+        }
+
+        let primeDTO = PrimeDTO(
+            id: id,
+            timestamp: PumpEventStored.dateFormatter.string(from: timestamp)
+        )
+        return .prime(primeDTO)
+    }
 }

+ 1 - 1
OmniBLE

@@ -1 +1 @@
-Subproject commit 1fa2874419225c8c7af0d9afbd9faf823cda34e5
+Subproject commit 6f65cbae4c8089a892911e273204edfc4cc81e9d

+ 1 - 1
OmniKit

@@ -1 +1 @@
-Subproject commit 48a35efa52f42e0b72fe2e984f60d4482a11a75f
+Subproject commit 39915b05fe46b5d73eca52e156dd7efd11193ee8

+ 11 - 1
Trio Watch App Extension/Helper/Helper+ButtonStyles.swift

@@ -5,6 +5,8 @@ struct WatchOSButtonStyle: ButtonStyle {
     var foregroundColor: Color = .white
     var fontSize: Font = .title2
 
+    @Environment(\.isEnabled) private var isEnabled: Bool
+
     private var fontWeight: Font.Weight {
         switch deviceType {
         case .watch40mm:
@@ -44,11 +46,19 @@ struct WatchOSButtonStyle: ButtonStyle {
     }
 
     func makeBody(configuration: Configuration) -> some View {
+        var buttonBackground: Color {
+            if isEnabled {
+                return Color.tabBar.opacity(configuration.isPressed ? 0.8 : 1.0)
+            } else {
+                return Color.tabBar.opacity(0.4)
+            }
+        }
+
         configuration.label
             .font(fontSize)
             .fontWeight(fontWeight)
             .padding(buttonPadding)
-            .background(Color.tabBar.opacity(configuration.isPressed ? 0.8 : 1.0))
+            .background(buttonBackground)
             .clipShape(Circle())
             .animation(.easeInOut(duration: 0.1), value: configuration.isPressed)
     }

+ 20 - 0
Trio Watch App Extension/Views/BolusProgressOverlay.swift

@@ -16,6 +16,25 @@ struct BolusProgressOverlay: View {
         endPoint: .trailing
     )
 
+    private var isWatchStateDated: Bool {
+        // If `lastWatchStateUpdate` is nil, treat as "dated"
+        guard let lastUpdateTimestamp = state.lastWatchStateUpdate else {
+            return true
+        }
+        let now = Date().timeIntervalSince1970
+        let secondsSinceUpdate = now - lastUpdateTimestamp
+        // Return true if last update older than 5 min, so 1 loop cycle
+        return secondsSinceUpdate > 5 * 60
+    }
+
+    private var isSessionUnreachable: Bool {
+        guard let session = state.session else {
+            return true // No session at all => unreachable
+        }
+        // Return true if not .activated OR not reachable
+        return session.activationState != .activated
+    }
+
     var body: some View {
         VStack(spacing: 10) {
             VStack {
@@ -45,6 +64,7 @@ struct BolusProgressOverlay: View {
                 }
                 .buttonStyle(.bordered)
                 .padding()
+                .disabled(isWatchStateDated || isSessionUnreachable)
             }
             .padding()
             .background(Color.black.opacity(0.9))

+ 5 - 0
Trio Watch App Extension/Views/GlucoseChartView.swift

@@ -6,6 +6,8 @@ import SwiftUI
 
 struct GlucoseChartView: View {
     let glucoseValues: [(date: Date, glucose: Double, color: Color)]
+    let minYAxisValue: Decimal
+    let maxYAxisValue: Decimal
     @State private var timeWindow: TimeWindow = .threeHours
 
     enum TimeWindow: Int {
@@ -69,6 +71,9 @@ struct GlucoseChartView: View {
                         }
                     }
                 }
+                .chartYScale(
+                    domain: minYAxisValue ... maxYAxisValue
+                )
                 .chartPlotStyle { plotContent in
                     plotContent
                         .background(

+ 13 - 4
Trio Watch App Extension/Views/TrioMainWatchView.swift

@@ -88,8 +88,12 @@ struct TrioMainWatchView: View {
                 }.tag(0)
 
                 // Page 2: Glucose chart
-                GlucoseChartView(glucoseValues: state.glucoseValues)
-                    .tag(1)
+                GlucoseChartView(
+                    glucoseValues: state.glucoseValues,
+                    minYAxisValue: state.minYAxisValue,
+                    maxYAxisValue: state.maxYAxisValue
+                )
+                .tag(1)
             }
             .onAppear {
                 // Hard reset variables when main view appears
@@ -135,7 +139,9 @@ struct TrioMainWatchView: View {
                     } label: {
                         Image(systemName: "clock.arrow.2.circlepath")
                             .foregroundStyle(Color.primary, isOverrideActive ? Color.primary : Color.purple)
-                    }.tint(isOverrideActive ? Color.purple : nil)
+                    }
+                    .tint(isOverrideActive ? Color.purple : nil)
+                    .disabled(isWatchStateDated || isSessionUnreachable)
 
                     Button {
                         showingTreatmentMenuSheet = true
@@ -145,13 +151,16 @@ struct TrioMainWatchView: View {
                     }
                     .controlSize(.large)
                     .buttonStyle(WatchOSButtonStyle(deviceType: state.deviceType))
+                    .disabled(isWatchStateDated || isSessionUnreachable)
 
                     Button {
                         showingTempTargetSheet = true
                     } label: {
                         Image(systemName: "target")
                             .foregroundStyle(isTempTargetActive ? Color.primary : Color.loopGreen.opacity(0.75))
-                    }.tint(isTempTargetActive ? Color.loopGreen.opacity(0.75) : nil)
+                    }
+                    .tint(isTempTargetActive ? Color.loopGreen.opacity(0.75) : nil)
+                    .disabled(isWatchStateDated || isSessionUnreachable)
                 }
             }
             .fullScreenCover(isPresented: $showingTreatmentMenuSheet) {

+ 14 - 0
Trio Watch App Extension/WatchState.swift

@@ -20,6 +20,8 @@ import WatchConnectivity
     var trend: String? = ""
     var delta: String? = "--"
     var glucoseValues: [(date: Date, glucose: Double, color: Color)] = []
+    var minYAxisValue: Decimal = 39
+    var maxYAxisValue: Decimal = 200
     var cob: String? = "--"
     var iob: String? = "--"
     var lastLoopTime: String? = "--"
@@ -518,6 +520,18 @@ import WatchConnectivity
             .sorted { $0.date < $1.date }
         }
 
+        if let minYAxisValue = message[WatchMessageKeys.minYAxisValue] {
+            if let decimalValue = (minYAxisValue as? NSNumber)?.decimalValue {
+                self.minYAxisValue = decimalValue
+            }
+        }
+
+        if let maxYAxisValue = message[WatchMessageKeys.maxYAxisValue] {
+            if let decimalValue = (maxYAxisValue as? NSNumber)?.decimalValue {
+                self.maxYAxisValue = decimalValue
+            }
+        }
+
         if let overrideData = message[WatchMessageKeys.overridePresets] as? [[String: Any]] {
             overridePresets = overrideData.compactMap { data in
                 guard let name = data["name"] as? String,

+ 39 - 130
Trio.xcodeproj/project.pbxproj

@@ -28,7 +28,6 @@
 		190EBCC829FF13AA00BA767D /* UserInterfaceSettingsStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 190EBCC729FF13AA00BA767D /* UserInterfaceSettingsStateModel.swift */; };
 		190EBCCB29FF13CB00BA767D /* UserInterfaceSettingsRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 190EBCCA29FF13CB00BA767D /* UserInterfaceSettingsRootView.swift */; };
 		191F62682AD6B05A004D7911 /* NightscoutSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 191F62672AD6B05A004D7911 /* NightscoutSettings.swift */; };
-		1927C8E62744606D00347C69 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1927C8E82744606D00347C69 /* InfoPlist.strings */; };
 		1935364028496F7D001E0B16 /* Oref2_variables.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1935363F28496F7D001E0B16 /* Oref2_variables.swift */; };
 		193F6CDD2A512C8F001240FD /* Loops.swift in Sources */ = {isa = PBXBuildFile; fileRef = 193F6CDC2A512C8F001240FD /* Loops.swift */; };
 		195D80B42AF6973A00D25097 /* DynamicSettingsRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 195D80B32AF6973A00D25097 /* DynamicSettingsRootView.swift */; };
@@ -38,7 +37,6 @@
 		1967DFBE29D052C200759F30 /* Icons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1967DFBD29D052C200759F30 /* Icons.swift */; };
 		1967DFC029D053AC00759F30 /* IconSelection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1967DFBF29D053AC00759F30 /* IconSelection.swift */; };
 		1967DFC229D053D300759F30 /* IconImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1967DFC129D053D300759F30 /* IconImage.swift */; };
-		198377D2266BFFF6004DE65E /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 198377D4266BFFF6004DE65E /* Localizable.strings */; };
 		19A910302A24BF6300C8951B /* StatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A9102F2A24BF6300C8951B /* StatsView.swift */; };
 		19A910362A24D6D700C8951B /* DateFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A910352A24D6D700C8951B /* DateFilter.swift */; };
 		19A910382A24EF3200C8951B /* ChartsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A910372A24EF3200C8951B /* ChartsView.swift */; };
@@ -204,7 +202,7 @@
 		38FE826D25CC8461001FF17A /* NightscoutAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38FE826C25CC8461001FF17A /* NightscoutAPI.swift */; };
 		38FEF3FA2737E42000574A46 /* BaseStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38FEF3F92737E42000574A46 /* BaseStateModel.swift */; };
 		38FEF3FC2737E53800574A46 /* MainStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38FEF3FB2737E53800574A46 /* MainStateModel.swift */; };
-		38FEF3FE2738083E00574A46 /* CGMProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38FEF3FD2738083E00574A46 /* CGMProvider.swift */; };
+		38FEF3FE2738083E00574A46 /* CGMSettingsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38FEF3FD2738083E00574A46 /* CGMSettingsProvider.swift */; };
 		38FEF413273B317A00574A46 /* HKUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38FEF412273B317A00574A46 /* HKUnit.swift */; };
 		45252C95D220E796FDB3B022 /* ConfigEditorDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F8A87AA037BD079BA3528BA /* ConfigEditorDataFlow.swift */; };
 		45717281F743594AA9D87191 /* ConfigEditorRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 920DDB21E5D0EB813197500D /* ConfigEditorRootView.swift */; };
@@ -212,6 +210,7 @@
 		491D6FBE2D56741C00C49F67 /* TempTargetRunStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 491D6FB92D56741C00C49F67 /* TempTargetRunStored+CoreDataClass.swift */; };
 		491D6FBF2D56741C00C49F67 /* TempTargetRunStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 491D6FBA2D56741C00C49F67 /* TempTargetRunStored+CoreDataProperties.swift */; };
 		491D6FC02D56741C00C49F67 /* TempTargetStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 491D6FBB2D56741C00C49F67 /* TempTargetStored+CoreDataClass.swift */; };
+		49B9B57F2D5768D2009C6B59 /* AdjustmentStored+Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49B9B57E2D5768D2009C6B59 /* AdjustmentStored+Helper.swift */; };
 		5075C1608E6249A51495C422 /* TargetsEditorProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BDEA2DC60EDE0A3CA54DC73 /* TargetsEditorProvider.swift */; };
 		53F2382465BF74DB1A967C8B /* PumpConfigProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8630D58BDAD6D9C650B9B39 /* PumpConfigProvider.swift */; };
 		581516A42BCED84A00BF67D7 /* DebuggingIdentifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 581516A32BCED84A00BF67D7 /* DebuggingIdentifiers.swift */; };
@@ -277,6 +276,8 @@
 		7F7B756BE8543965D9FDF1A2 /* DataTableDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = A401509D21F7F35D4E109EDA /* DataTableDataFlow.swift */; };
 		8194B80890CDD6A3C13B0FEE /* SnoozeStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E26904AACA8D9C15D229D675 /* SnoozeStateModel.swift */; };
 		88AB39B23C9552BD6E0C9461 /* ISFEditorRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBB3BAE7494CB771ABAC7B8B /* ISFEditorRootView.swift */; };
+		8A91342A2D63D9A1007F8874 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 8A9134292D63D9A1007F8874 /* Localizable.xcstrings */; };
+		8A91342C2D63D9A2007F8874 /* InfoPlist.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 8A91342B2D63D9A2007F8874 /* InfoPlist.xcstrings */; };
 		8B759CFCF47B392BB365C251 /* BasalProfileEditorDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67F94DD2853CF42BA4E30616 /* BasalProfileEditorDataFlow.swift */; };
 		9702FF92A09C53942F20D7EA /* TargetsEditorRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DD795BA46B193644D48138C /* TargetsEditorRootView.swift */; };
 		9825E5E923F0B8FA80C8C7C7 /* NightscoutConfigStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0A48AE3AC813A49A517846A /* NightscoutConfigStateModel.swift */; };
@@ -286,7 +287,7 @@
 		B7C465E9472624D8A2BE2A6A /* (null) in Sources */ = {isa = PBXBuildFile; };
 		B958F1B72BA0711600484851 /* MKRingProgressView in Frameworks */ = {isa = PBXBuildFile; productRef = B958F1B62BA0711600484851 /* MKRingProgressView */; };
 		B9CAAEFC2AE70836000F68BC /* branch.txt in Resources */ = {isa = PBXBuildFile; fileRef = B9CAAEFB2AE70836000F68BC /* branch.txt */; };
-		BA00D96F7B2FF169A06FB530 /* CGMStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C018D1680307A31C9ED7120 /* CGMStateModel.swift */; };
+		BA00D96F7B2FF169A06FB530 /* CGMSettingsStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C018D1680307A31C9ED7120 /* CGMSettingsStateModel.swift */; };
 		BD04ECCE2D29952A008C5FEB /* BolusProgressOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD04ECCD2D299522008C5FEB /* BolusProgressOverlay.swift */; };
 		BD0B2EF32C5998E600B3298F /* MealPresetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD0B2EF22C5998E600B3298F /* MealPresetView.swift */; };
 		BD1661312B82ADAB00256551 /* CustomProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD1661302B82ADAB00256551 /* CustomProgressView.swift */; };
@@ -367,6 +368,7 @@
 		CE1F6DE72BAF1A180064EB8D /* BuildDetails.plist in Resources */ = {isa = PBXBuildFile; fileRef = CE1F6DE62BAF1A180064EB8D /* BuildDetails.plist */; };
 		CE1F6DE92BAF37C90064EB8D /* TidepoolConfigView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE1F6DE82BAF37C90064EB8D /* TidepoolConfigView.swift */; };
 		CE2FAD3A297D93F0001A872C /* BloodGlucoseExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE2FAD39297D93F0001A872C /* BloodGlucoseExtensions.swift */; };
+		CE3EEF9A2D463717001944DD /* CustomCGMOptionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE3EEF992D46370A001944DD /* CustomCGMOptionsView.swift */; };
 		CE48C86428CA69D5007C0598 /* OmniBLEPumpManagerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE48C86328CA69D5007C0598 /* OmniBLEPumpManagerExtensions.swift */; };
 		CE48C86628CA6B48007C0598 /* OmniPodManagerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE48C86528CA6B48007C0598 /* OmniPodManagerExtensions.swift */; };
 		CE51DD1C2A01970900F163F7 /* ConnectIQ 2.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE51DD1B2A01970800F163F7 /* ConnectIQ 2.xcframework */; };
@@ -414,6 +416,7 @@
 		CEE9A6592BBB418300EB5194 /* CalibrationsDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE9A6542BBB418300EB5194 /* CalibrationsDataFlow.swift */; };
 		CEE9A65C2BBB41C800EB5194 /* CalibrationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE9A65B2BBB41C800EB5194 /* CalibrationService.swift */; };
 		CEE9A65E2BBC9F6500EB5194 /* CalibrationsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE9A65D2BBC9F6500EB5194 /* CalibrationsTests.swift */; };
+		CEF1ED6B2D58FB5800FAF41E /* CGMOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEF1ED6A2D58FB4600FAF41E /* CGMOptions.swift */; };
 		D6D02515BBFBE64FEBE89856 /* DataTableRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 881E04BA5E0A003DE8E0A9C6 /* DataTableRootView.swift */; };
 		D6DEC113821A7F1056C4AA1E /* NightscoutConfigDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F2A13DF0EDEEEDC4106AA2A /* NightscoutConfigDataFlow.swift */; };
 		DBA5254DBB2586C98F61220C /* ISFEditorProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F9F137F126D9F8DEB799F26 /* ISFEditorProvider.swift */; };
@@ -565,7 +568,7 @@
 		E592A3792CEEC038009A472C /* ContactImageRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E592A3712CEEC038009A472C /* ContactImageRootView.swift */; };
 		E592A37A2CEEC038009A472C /* ContactImageProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E592A3742CEEC038009A472C /* ContactImageProvider.swift */; };
 		E974172296125A5AE99E634C /* PumpConfigRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD22C985B79A2F0D2EA3D9D /* PumpConfigRootView.swift */; };
-		F5CA3DB1F9DC8B05792BBFAA /* CGMDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9B5C0607505A38F256BF99A /* CGMDataFlow.swift */; };
+		F5CA3DB1F9DC8B05792BBFAA /* CGMSettingsDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9B5C0607505A38F256BF99A /* CGMSettingsDataFlow.swift */; };
 		F5F7E6C1B7F098F59EB67EC5 /* TargetsEditorDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA49538D56989D8DA6FCF538 /* TargetsEditorDataFlow.swift */; };
 		F816825E28DB441200054060 /* HeartBeatManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F816825D28DB441200054060 /* HeartBeatManager.swift */; };
 		F816826028DB441800054060 /* BluetoothTransmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F816825F28DB441800054060 /* BluetoothTransmitter.swift */; };
@@ -687,33 +690,8 @@
 		190EBCC529FF138000BA767D /* UserInterfaceSettingsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserInterfaceSettingsProvider.swift; sourceTree = "<group>"; };
 		190EBCC729FF13AA00BA767D /* UserInterfaceSettingsStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserInterfaceSettingsStateModel.swift; sourceTree = "<group>"; };
 		190EBCCA29FF13CB00BA767D /* UserInterfaceSettingsRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserInterfaceSettingsRootView.swift; sourceTree = "<group>"; };
-		1918333A26ADA46800F45722 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = "<group>"; };
 		191F62672AD6B05A004D7911 /* NightscoutSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutSettings.swift; sourceTree = "<group>"; };
-		1927C8E92744611700347C69 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8EA2744611800347C69 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8EB2744611900347C69 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
-		1927C8EC2744611A00347C69 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8ED2744611B00347C69 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8EE2744611C00347C69 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8EF2744611D00347C69 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8F02744611E00347C69 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8F12744611E00347C69 /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8F22744611F00347C69 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8F32744612000347C69 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8F42744612100347C69 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8F52744612100347C69 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
-		1927C8F62744612200347C69 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
-		1927C8F72744612300347C69 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8F82744612400347C69 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8F92744612400347C69 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8FA2744612500347C69 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8FB2744612600347C69 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		1927C8FE274489BA00347C69 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/InfoPlist.strings; sourceTree = "<group>"; };
 		1935363F28496F7D001E0B16 /* Oref2_variables.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Oref2_variables.swift; sourceTree = "<group>"; };
-		193F1E392B44C13B00525770 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		193F1E3A2B44C13B00525770 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Localizable.strings; sourceTree = "<group>"; };
-		193F1E3B2B44C14800525770 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		193F1E3C2B44C14800525770 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Localizable.strings; sourceTree = "<group>"; };
 		193F6CDC2A512C8F001240FD /* Loops.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Loops.swift; sourceTree = "<group>"; };
 		195D80B32AF6973A00D25097 /* DynamicSettingsRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicSettingsRootView.swift; sourceTree = "<group>"; };
 		195D80B62AF697B800D25097 /* DynamicSettingsDataFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicSettingsDataFlow.swift; sourceTree = "<group>"; };
@@ -722,32 +700,11 @@
 		1967DFBD29D052C200759F30 /* Icons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Icons.swift; sourceTree = "<group>"; };
 		1967DFBF29D053AC00759F30 /* IconSelection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconSelection.swift; sourceTree = "<group>"; };
 		1967DFC129D053D300759F30 /* IconImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconImage.swift; sourceTree = "<group>"; };
-		198377D3266BFFF6004DE65E /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377D5266C0A05004DE65E /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377D6266C0A0A004DE65E /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377D7266C0A15004DE65E /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; };
-		198377D8266C0A1C004DE65E /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377D9266C0A21004DE65E /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377DA266C0A2B004DE65E /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377DB266C0A32004DE65E /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377DC266C0A3C004DE65E /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377DD266C0A51004DE65E /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377DE266C0A69004DE65E /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377DF266C0A7F004DE65E /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377E0266C0AB5004DE65E /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377E1266C0ABF004DE65E /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377E2266C0AC8004DE65E /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377E3266C0ADC004DE65E /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = "<group>"; };
-		198377E4266C13D2004DE65E /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Localizable.strings; sourceTree = "<group>"; };
 		199561C0275E61A50077B976 /* HealthKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HealthKit.framework; path = Platforms/WatchOS.platform/Developer/SDKs/WatchOS8.0.sdk/System/Library/Frameworks/HealthKit.framework; sourceTree = DEVELOPER_DIR; };
-		199732B4271B72DD00129A3F /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Localizable.strings"; sourceTree = "<group>"; };
-		199732B5271B9EE900129A3F /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = "<group>"; };
 		19A9102F2A24BF6300C8951B /* StatsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsView.swift; sourceTree = "<group>"; };
 		19A910352A24D6D700C8951B /* DateFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateFilter.swift; sourceTree = "<group>"; };
 		19A910372A24EF3200C8951B /* ChartsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChartsView.swift; sourceTree = "<group>"; };
 		19B0EF2028F6D66200069496 /* Statistics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Statistics.swift; sourceTree = "<group>"; };
-		19C166682756EFBD00ED12E3 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/InfoPlist.strings; sourceTree = "<group>"; };
-		19C166692756EFBD00ED12E3 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/Localizable.strings; sourceTree = "<group>"; };
 		19D466A229AA2B80004D5F33 /* MealSettingsDataFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MealSettingsDataFlow.swift; sourceTree = "<group>"; };
 		19D466A429AA2BD4004D5F33 /* MealSettingsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MealSettingsProvider.swift; sourceTree = "<group>"; };
 		19D466A629AA2C22004D5F33 /* MealSettingsStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MealSettingsStateModel.swift; sourceTree = "<group>"; };
@@ -928,7 +885,7 @@
 		38FE826C25CC8461001FF17A /* NightscoutAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutAPI.swift; sourceTree = "<group>"; };
 		38FEF3F92737E42000574A46 /* BaseStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseStateModel.swift; sourceTree = "<group>"; };
 		38FEF3FB2737E53800574A46 /* MainStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainStateModel.swift; sourceTree = "<group>"; };
-		38FEF3FD2738083E00574A46 /* CGMProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CGMProvider.swift; sourceTree = "<group>"; };
+		38FEF3FD2738083E00574A46 /* CGMSettingsProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CGMSettingsProvider.swift; sourceTree = "<group>"; };
 		38FEF412273B317A00574A46 /* HKUnit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HKUnit.swift; sourceTree = "<group>"; };
 		3BDEA2DC60EDE0A3CA54DC73 /* TargetsEditorProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = TargetsEditorProvider.swift; sourceTree = "<group>"; };
 		3BF768BD6264FF7D71D66767 /* NightscoutConfigProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = NightscoutConfigProvider.swift; sourceTree = "<group>"; };
@@ -940,6 +897,7 @@
 		491D6FBA2D56741C00C49F67 /* TempTargetRunStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempTargetRunStored+CoreDataProperties.swift"; sourceTree = "<group>"; };
 		491D6FBB2D56741C00C49F67 /* TempTargetStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempTargetStored+CoreDataClass.swift"; sourceTree = "<group>"; };
 		491D6FBC2D56741C00C49F67 /* TempTargetStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TempTargetStored+CoreDataProperties.swift"; sourceTree = "<group>"; };
+		49B9B57E2D5768D2009C6B59 /* AdjustmentStored+Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AdjustmentStored+Helper.swift"; sourceTree = "<group>"; };
 		4DD795BA46B193644D48138C /* TargetsEditorRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = TargetsEditorRootView.swift; sourceTree = "<group>"; };
 		505E09DC17A0C3D0AF4B66FE /* ISFEditorStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ISFEditorStateModel.swift; sourceTree = "<group>"; };
 		581516A32BCED84A00BF67D7 /* DebuggingIdentifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebuggingIdentifiers.swift; sourceTree = "<group>"; };
@@ -978,7 +936,7 @@
 		5A2325512BFCBF55003518CA /* NightscoutUploadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutUploadView.swift; sourceTree = "<group>"; };
 		5A2325532BFCBF65003518CA /* NightscoutFetchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutFetchView.swift; sourceTree = "<group>"; };
 		5A2325572BFCC168003518CA /* NightscoutConnectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutConnectView.swift; sourceTree = "<group>"; };
-		5C018D1680307A31C9ED7120 /* CGMStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CGMStateModel.swift; sourceTree = "<group>"; };
+		5C018D1680307A31C9ED7120 /* CGMSettingsStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CGMSettingsStateModel.swift; sourceTree = "<group>"; };
 		5D5B4F8B4194BB7E260EF251 /* ConfigEditorStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ConfigEditorStateModel.swift; sourceTree = "<group>"; };
 		60744C3E9BB3652895C908CC /* DataTableProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DataTableProvider.swift; sourceTree = "<group>"; };
 		64AA5E04A2761F6EEA6568E1 /* CarbRatioEditorStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CarbRatioEditorStateModel.swift; sourceTree = "<group>"; };
@@ -1001,6 +959,8 @@
 		7E22146D3DF4853786C78132 /* CarbRatioEditorDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CarbRatioEditorDataFlow.swift; sourceTree = "<group>"; };
 		8782B44544F38F2B2D82C38E /* NightscoutConfigRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = NightscoutConfigRootView.swift; sourceTree = "<group>"; };
 		881E04BA5E0A003DE8E0A9C6 /* DataTableRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DataTableRootView.swift; sourceTree = "<group>"; };
+		8A9134292D63D9A1007F8874 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = "<group>"; };
+		8A91342B2D63D9A2007F8874 /* InfoPlist.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = InfoPlist.xcstrings; sourceTree = "<group>"; };
 		920DDB21E5D0EB813197500D /* ConfigEditorRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ConfigEditorRootView.swift; sourceTree = "<group>"; };
 		9455FA2D92E77A6C4AFED8A3 /* DataTableStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DataTableStateModel.swift; sourceTree = "<group>"; };
 		96653287EDB276A111288305 /* ManualTempBasalDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ManualTempBasalDataFlow.swift; sourceTree = "<group>"; };
@@ -1012,7 +972,7 @@
 		AAFF91130F2FCCC7EBBA11AD /* BasalProfileEditorStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = BasalProfileEditorStateModel.swift; sourceTree = "<group>"; };
 		AF65DA88F972B56090AD6AC3 /* PumpConfigDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PumpConfigDataFlow.swift; sourceTree = "<group>"; };
 		B5822B15939E719628E9FF7C /* SnoozeRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = SnoozeRootView.swift; sourceTree = "<group>"; };
-		B9B5C0607505A38F256BF99A /* CGMDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CGMDataFlow.swift; sourceTree = "<group>"; };
+		B9B5C0607505A38F256BF99A /* CGMSettingsDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CGMSettingsDataFlow.swift; sourceTree = "<group>"; };
 		B9CAAEFB2AE70836000F68BC /* branch.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = branch.txt; sourceTree = SOURCE_ROOT; };
 		BA49538D56989D8DA6FCF538 /* TargetsEditorDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = TargetsEditorDataFlow.swift; sourceTree = "<group>"; };
 		BD04ECCD2D299522008C5FEB /* BolusProgressOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BolusProgressOverlay.swift; sourceTree = "<group>"; };
@@ -1097,6 +1057,7 @@
 		CE398D012977349800DF218F /* CryptoKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CryptoKit.framework; path = System/Library/Frameworks/CryptoKit.framework; sourceTree = SDKROOT; };
 		CE398D17297C9EE800DF218F /* G7SensorKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = G7SensorKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		CE398D1A297D69A900DF218F /* ShareClient.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = ShareClient.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		CE3EEF992D46370A001944DD /* CustomCGMOptionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomCGMOptionsView.swift; sourceTree = "<group>"; };
 		CE48C86328CA69D5007C0598 /* OmniBLEPumpManagerExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OmniBLEPumpManagerExtensions.swift; sourceTree = "<group>"; };
 		CE48C86528CA6B48007C0598 /* OmniPodManagerExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OmniPodManagerExtensions.swift; sourceTree = "<group>"; };
 		CE51DD1B2A01970800F163F7 /* ConnectIQ 2.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = "ConnectIQ 2.xcframework"; path = "Dependencies/ConnectIQ 2.xcframework"; sourceTree = "<group>"; };
@@ -1144,6 +1105,7 @@
 		CEE9A6542BBB418300EB5194 /* CalibrationsDataFlow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CalibrationsDataFlow.swift; sourceTree = "<group>"; };
 		CEE9A65B2BBB41C800EB5194 /* CalibrationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalibrationService.swift; sourceTree = "<group>"; };
 		CEE9A65D2BBC9F6500EB5194 /* CalibrationsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalibrationsTests.swift; sourceTree = "<group>"; };
+		CEF1ED6A2D58FB4600FAF41E /* CGMOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGMOptions.swift; sourceTree = "<group>"; };
 		CFCFE0781F9074C2917890E8 /* ManualTempBasalStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ManualTempBasalStateModel.swift; sourceTree = "<group>"; };
 		D0BDC6993C1087310EDFC428 /* CarbRatioEditorRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CarbRatioEditorRootView.swift; sourceTree = "<group>"; };
 		DC2C6489D29ECCCAD78E0721 /* GlucoseNotificationSettingsStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = GlucoseNotificationSettingsStateModel.swift; sourceTree = "<group>"; };
@@ -1402,6 +1364,7 @@
 		0D76BBC81CEDC1A0050F45EF /* View */ = {
 			isa = PBXGroup;
 			children = (
+				CE3EEF992D46370A001944DD /* CustomCGMOptionsView.swift */,
 				38569352270B5E350002C50D /* CGMRootView.swift */,
 				CE7950232997D81700FA576E /* CGMSettingsView.swift */,
 				CE7950252998056D00FA576E /* CGMSetupView.swift */,
@@ -1522,7 +1485,7 @@
 		19D440A926B6FEBD008DA6C8 /* Main */ = {
 			isa = PBXGroup;
 			children = (
-				198377D4266BFFF6004DE65E /* Localizable.strings */,
+				8A9134292D63D9A1007F8874 /* Localizable.xcstrings */,
 			);
 			path = Main;
 			sourceTree = "<group>";
@@ -1619,7 +1582,7 @@
 				DD09D4792C5986BA003FEA5D /* CalendarEventSettings */,
 				CEE9A64D2BBB411C00EB5194 /* Calibrations */,
 				E42231DBF0DBE2B4B92D1B15 /* CarbRatioEditor */,
-				F75CB57ED6971B46F8756083 /* CGM */,
+				F75CB57ED6971B46F8756083 /* CGMSettings */,
 				0610F7D6D2EC00E3BA1569F0 /* ConfigEditor */,
 				E592A3762CEEC038009A472C /* ContactImage */,
 				9E56E3626FAD933385101B76 /* DataTable */,
@@ -1844,7 +1807,7 @@
 				388E596E25AD96040019842D /* javascript */,
 				3811DEC725C9DA7300A708ED /* Trio.entitlements */,
 				388E596425AD948E0019842D /* Info.plist */,
-				1927C8E82744606D00347C69 /* InfoPlist.strings */,
+				8A91342B2D63D9A2007F8874 /* InfoPlist.xcstrings */,
 				B9CAAEFB2AE70836000F68BC /* branch.txt */,
 				19DA487F29CD2B8400EEA1E7 /* Assets.xcassets */,
 			);
@@ -2119,6 +2082,7 @@
 		388E5A5A25B6F05F0019842D /* Helpers */ = {
 			isa = PBXGroup;
 			children = (
+				CEF1ED6A2D58FB4600FAF41E /* CGMOptions.swift */,
 				C2A0A42E2CE0312C003B98E8 /* ConstantValues.swift */,
 				DD940BAB2CA75889000830A5 /* DynamicGlucoseColor.swift */,
 				38F37827261260DC009DB701 /* Color+Extensions.swift */,
@@ -2348,6 +2312,7 @@
 		5825D1622BD405AE00F36E9B /* Helper */ = {
 			isa = PBXGroup;
 			children = (
+				49B9B57E2D5768D2009C6B59 /* AdjustmentStored+Helper.swift */,
 				581516A82BCEEDF800BF67D7 /* NSPredicates.swift */,
 				583684052BD178DB00070A60 /* GlucoseStored+helper.swift */,
 				58F107732BD1A4D000B1A680 /* Determination+helper.swift */,
@@ -3142,15 +3107,15 @@
 			path = GlucoseNotificationSettings;
 			sourceTree = "<group>";
 		};
-		F75CB57ED6971B46F8756083 /* CGM */ = {
+		F75CB57ED6971B46F8756083 /* CGMSettings */ = {
 			isa = PBXGroup;
 			children = (
-				B9B5C0607505A38F256BF99A /* CGMDataFlow.swift */,
-				38FEF3FD2738083E00574A46 /* CGMProvider.swift */,
-				5C018D1680307A31C9ED7120 /* CGMStateModel.swift */,
+				B9B5C0607505A38F256BF99A /* CGMSettingsDataFlow.swift */,
+				38FEF3FD2738083E00574A46 /* CGMSettingsProvider.swift */,
+				5C018D1680307A31C9ED7120 /* CGMSettingsStateModel.swift */,
 				0D76BBC81CEDC1A0050F45EF /* View */,
 			);
-			path = CGM;
+			path = CGMSettings;
 			sourceTree = "<group>";
 		};
 		F90692A8274B7A980037068D /* HealthKit */ = {
@@ -3404,15 +3369,15 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				198377D2266BFFF6004DE65E /* Localizable.strings in Resources */,
+				8A91342C2D63D9A2007F8874 /* InfoPlist.xcstrings in Resources */,
 				CE1F6DE72BAF1A180064EB8D /* BuildDetails.plist in Resources */,
 				38DF178D27733E6800B3528F /* snow.sks in Resources */,
 				388E597225AD9CF10019842D /* json in Resources */,
 				38DF178E27733E6800B3528F /* Assets.xcassets in Resources */,
 				19DA48E829CD339B00EEA1E7 /* Assets.xcassets in Resources */,
+				8A91342A2D63D9A1007F8874 /* Localizable.xcstrings in Resources */,
 				388E596F25AD96040019842D /* javascript in Resources */,
 				B9CAAEFC2AE70836000F68BC /* branch.txt in Resources */,
-				1927C8E62744606D00347C69 /* InfoPlist.strings in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -3780,6 +3745,7 @@
 				DD1745522C55CA5D00211FAC /* UnitsLimitsSettingsStateModel.swift in Sources */,
 				DD2CC85C2D25DA1000445446 /* GlucoseTargetsView.swift in Sources */,
 				190EBCC429FF136900BA767D /* UserInterfaceSettingsDataFlow.swift in Sources */,
+				CE3EEF9A2D463717001944DD /* CustomCGMOptionsView.swift in Sources */,
 				5A2325582BFCC168003518CA /* NightscoutConnectView.swift in Sources */,
 				3811DEB025C9D88300A708ED /* BaseKeychain.swift in Sources */,
 				110AEDE42C5193D200615CC9 /* BolusIntentRequest.swift in Sources */,
@@ -3825,6 +3791,7 @@
 				53F2382465BF74DB1A967C8B /* PumpConfigProvider.swift in Sources */,
 				5D16287A969E64D18CE40E44 /* PumpConfigStateModel.swift in Sources */,
 				19D466AA29AA3099004D5F33 /* MealSettingsRootView.swift in Sources */,
+				CEF1ED6B2D58FB5800FAF41E /* CGMOptions.swift in Sources */,
 				E974172296125A5AE99E634C /* PumpConfigRootView.swift in Sources */,
 				DD1745502C55CA5500211FAC /* UnitsLimitsSettingsProvider.swift in Sources */,
 				581AC4392BE22ED10038760C /* JSONConverter.swift in Sources */,
@@ -3836,7 +3803,7 @@
 				E06B911A275B5EEA003C04B6 /* Array+Extension.swift in Sources */,
 				38EA0600262091870064E39B /* BolusProgressViewStyle.swift in Sources */,
 				389ECDFE2601061500D86C4F /* View+Snapshot.swift in Sources */,
-				38FEF3FE2738083E00574A46 /* CGMProvider.swift in Sources */,
+				38FEF3FE2738083E00574A46 /* CGMSettingsProvider.swift in Sources */,
 				38E98A3725F5509500C0CED0 /* String+Extensions.swift in Sources */,
 				CC76E9512BD4812E008BEB61 /* Forecast+helper.swift in Sources */,
 				DD1745242C55526000211FAC /* SMBSettingsStateModel.swift in Sources */,
@@ -3954,6 +3921,7 @@
 				DD1745222C55524800211FAC /* SMBSettingsProvider.swift in Sources */,
 				BF1667ADE69E4B5B111CECAE /* ManualTempBasalProvider.swift in Sources */,
 				583684062BD178DB00070A60 /* GlucoseStored+helper.swift in Sources */,
+				49B9B57F2D5768D2009C6B59 /* AdjustmentStored+Helper.swift in Sources */,
 				F90692D6274B9A450037068D /* HealthKitStateModel.swift in Sources */,
 				BD1661312B82ADAB00256551 /* CustomProgressView.swift in Sources */,
 				C967DACD3B1E638F8B43BE06 /* ManualTempBasalStateModel.swift in Sources */,
@@ -3979,9 +3947,9 @@
 				D6D02515BBFBE64FEBE89856 /* DataTableRootView.swift in Sources */,
 				DD1745292C55642100211FAC /* SettingInputSection.swift in Sources */,
 				38569349270B5DFB0002C50D /* AppGroupSource.swift in Sources */,
-				F5CA3DB1F9DC8B05792BBFAA /* CGMDataFlow.swift in Sources */,
+				F5CA3DB1F9DC8B05792BBFAA /* CGMSettingsDataFlow.swift in Sources */,
 				BDF34F952C10D27300D51995 /* DeterminationData.swift in Sources */,
-				BA00D96F7B2FF169A06FB530 /* CGMStateModel.swift in Sources */,
+				BA00D96F7B2FF169A06FB530 /* CGMSettingsStateModel.swift in Sources */,
 				BD7DA9A52AE06DFC00601B20 /* BolusCalculatorConfigDataFlow.swift in Sources */,
 				6EADD581738D64431902AC0A /* (null) in Sources */,
 				CE94598729E9E4110047C9C6 /* WatchConfigRootView.swift in Sources */,
@@ -4138,69 +4106,6 @@
 		};
 /* End PBXTargetDependency section */
 
-/* Begin PBXVariantGroup section */
-		1927C8E82744606D00347C69 /* InfoPlist.strings */ = {
-			isa = PBXVariantGroup;
-			children = (
-				1927C8E92744611700347C69 /* ar */,
-				1927C8EA2744611800347C69 /* ca */,
-				1927C8EB2744611900347C69 /* zh-Hans */,
-				1927C8EC2744611A00347C69 /* da */,
-				1927C8ED2744611B00347C69 /* fi */,
-				1927C8EE2744611C00347C69 /* nl */,
-				1927C8EF2744611D00347C69 /* fr */,
-				1927C8F02744611E00347C69 /* de */,
-				1927C8F12744611E00347C69 /* he */,
-				1927C8F22744611F00347C69 /* it */,
-				1927C8F32744612000347C69 /* nb */,
-				1927C8F42744612100347C69 /* pl */,
-				1927C8F52744612100347C69 /* pt-BR */,
-				1927C8F62744612200347C69 /* pt-PT */,
-				1927C8F72744612300347C69 /* ru */,
-				1927C8F82744612400347C69 /* es */,
-				1927C8F92744612400347C69 /* sv */,
-				1927C8FA2744612500347C69 /* tr */,
-				1927C8FB2744612600347C69 /* uk */,
-				1927C8FE274489BA00347C69 /* Base */,
-				19C166682756EFBD00ED12E3 /* sk */,
-				193F1E392B44C13B00525770 /* hu */,
-				193F1E3B2B44C14800525770 /* vi */,
-			);
-			name = InfoPlist.strings;
-			sourceTree = "<group>";
-		};
-		198377D4266BFFF6004DE65E /* Localizable.strings */ = {
-			isa = PBXVariantGroup;
-			children = (
-				198377D3266BFFF6004DE65E /* en */,
-				198377D5266C0A05004DE65E /* ar */,
-				198377D6266C0A0A004DE65E /* ca */,
-				198377D7266C0A15004DE65E /* zh-Hans */,
-				198377D8266C0A1C004DE65E /* da */,
-				198377D9266C0A21004DE65E /* nl */,
-				198377DA266C0A2B004DE65E /* fr */,
-				198377DB266C0A32004DE65E /* de */,
-				198377DC266C0A3C004DE65E /* he */,
-				198377DD266C0A51004DE65E /* it */,
-				198377DE266C0A69004DE65E /* nb */,
-				198377DF266C0A7F004DE65E /* pl */,
-				198377E0266C0AB5004DE65E /* ru */,
-				198377E1266C0ABF004DE65E /* es */,
-				198377E2266C0AC8004DE65E /* sv */,
-				198377E3266C0ADC004DE65E /* tr */,
-				198377E4266C13D2004DE65E /* uk */,
-				1918333A26ADA46800F45722 /* fi */,
-				199732B4271B72DD00129A3F /* pt-PT */,
-				199732B5271B9EE900129A3F /* pt-BR */,
-				19C166692756EFBD00ED12E3 /* sk */,
-				193F1E3A2B44C13B00525770 /* hu */,
-				193F1E3C2B44C14800525770 /* vi */,
-			);
-			name = Localizable.strings;
-			sourceTree = "<group>";
-		};
-/* End PBXVariantGroup section */
-
 /* Begin XCBuildConfiguration section */
 		388E596525AD948E0019842D /* Debug */ = {
 			isa = XCBuildConfiguration;
@@ -4258,12 +4163,14 @@
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
 				IPHONEOS_DEPLOYMENT_TARGET = 17.0;
+				LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
 				MARKETING_VERSION = "$(APP_VERSION)";
 				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
 				MTL_FAST_MATH = YES;
 				ONLY_ACTIVE_ARCH = YES;
 				SDKROOT = iphoneos;
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+				SWIFT_EMIT_LOC_STRINGS = YES;
 				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 			};
 			name = Debug;
@@ -4318,11 +4225,13 @@
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
 				IPHONEOS_DEPLOYMENT_TARGET = 17.0;
+				LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
 				MARKETING_VERSION = "$(APP_VERSION)";
 				MTL_ENABLE_DEBUG_INFO = NO;
 				MTL_FAST_MATH = YES;
 				SDKROOT = iphoneos;
 				SWIFT_COMPILATION_MODE = wholemodule;
+				SWIFT_EMIT_LOC_STRINGS = YES;
 				SWIFT_OPTIMIZATION_LEVEL = "-O";
 				VALIDATE_PRODUCT = YES;
 			};

+ 0 - 23
Trio/Resources/Base.lproj/InfoPlist.strings

@@ -1,23 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Contacts Usage Description */
-"NSContactsUsageDescription" = "Allows Trio to access your contacts for live updates to your Apple Watch contact complication using the 'Contact Trick' feature.";

Plik diff jest za duży
+ 1059 - 0
Trio/Resources/InfoPlist.xcstrings


+ 0 - 20
Trio/Resources/ar.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, carbs and insulin";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, carbs and insulin";

+ 0 - 20
Trio/Resources/ca.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 20
Trio/Resources/da.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC bruges til at scanne Libre sensorer.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth bliver brugt til at kommunikere med din insulin pumpe og dine glukose monitor enheder";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth bliver brugt til at kommunikere med din insulin pumpe og dine glukose monitor enheder";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For autoriseret adgang til bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Kalender bruges til at oprette en ny glucose begivenheder.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App bruges til at opbevare blodglukose, insulin og kulhydrater";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App bruges til at opbevare blodglukose, insulin og kulhydrater";

+ 0 - 20
Trio/Resources/de.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC wird zum Scannen von Libre Sensoren benutzt.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth wird verwendet, um mit Insulinpumpen und CGMs zu kommunizieren.";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth wird verwendet, um mit Insulinpumpen und CGMs zu kommunizieren.";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "Autorisierung für Bolusabgabe";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "BZ-Werte werden im Kalender als temporärer Termin erstellt.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Die Apple Health App wird zum Speichern von Blutzuckerwerten, Insulin und Kohlenhydraten verwendet";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Die Apple Health App wird zum Speichern von Blutzuckerwerten, Insulin und Kohlenhydraten verwendet";

+ 0 - 20
Trio/Resources/es.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 20
Trio/Resources/fi.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 20
Trio/Resources/fr.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC est utilisé pour scanner les capteurs Libre.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth est utilisé pour communiquer avec la pompe à insuline et les dispositifs de surveillance continue du glucose";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth est utilisé pour communiquer avec la pompe à insuline et les dispositifs de surveillance continue du glucose";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "Pour les accès autorisés au bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Le calendrier est utilisé pour créer un nouvel événement de glycémie.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "L'application Santé est utilisée pour stocker la glycémie, l'insuline et les glucides";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "L'application Santé est utilisée pour stocker la glycémie, l'insuline et les glucides";

+ 0 - 20
Trio/Resources/he.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 20
Trio/Resources/hu.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 20
Trio/Resources/it.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC è usato per scansionare i sensori di Libre.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Il bluetooth viene utilizzato per comunicare con il microinfusore e i dispositivi CGM";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Il Bluetooth viene utilizzato per comunicare con il microinfusore e i dispositivi CGM";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "Per accesso autorizzato al bolo";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Il calendario è usato per creare nuovi eventi di glicemia.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "L'app Salute è usata per memorizzare glicemie, insulina e carboidrati";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "L'app Salute è usata per memorizzare glicemie, insulina e carboidrati";

+ 0 - 20
Trio/Resources/nb.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC brukes til å skanne Libre-sensorer.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth brukes til å kommunisere med insulinpumpe og blodsukkersensor";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth brukes til å kommunisere med insulinpumpe og blodsukkersensor-enheter";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For autorisert tilgang til bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Kalender brukes til å opprette nye blodsukker-oppføringer.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Helse-appen brukes til å lagre blodsukker, insulin og karbohydrater";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Helse-appen brukes til å lagre blodsukker, insulin og karbohydrater";

+ 0 - 20
Trio/Resources/nl.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC wordt gebruikt voor het scannen van Libre sensoren.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth wordt gebruikt om te communiceren met de insuline pomp en de continue glucose meter";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth wordt gebruikt om te communiceren met de insuline pomp en de continue glucose meter";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "Voor geautoriseerde toegang tot bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Agenda wordt gebruikt om nieuwe glucose gebeurtenissen aan te maken.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Apple Gezondheid wordt gebruikt om bloedglucose, insuline en koolhydraten op te slaan";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Apple Gezondheid wordt gebruikt om bloedglucose, insuline en koolhydraten op te slaan";

+ 0 - 20
Trio/Resources/pl.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 20
Trio/Resources/pt-BR.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 20
Trio/Resources/pt-PT.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC is used to scan Libre sensors.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth is used to communicate with insulin pump and continuous glucose monitor devices";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "For authorized acces to bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Calendar is used to create a new glucose events.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 20
Trio/Resources/ru.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC используется для сканирования сенсоров Libre.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth используется для связи с инсулиновой помпой и устройствами непрерывного мониторинга глюкозы";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth используется для связи с инсулиновой помпой и устройствами непрерывного мониторинга глюкозы";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "Для авторизованного болюса";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Календарь используется для создания новых событий о глюкозе.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Приложение здоровья используется для хранения глюкозы, инсулина и углеводов в крови";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Приложение здоровья используется для хранения глюкозы, инсулина и углеводов в крови";

+ 0 - 20
Trio/Resources/sk.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC sa používa na skenovanie snímačov Libre.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth sa používa pre komunikáciu s inzulínovou pumpou a zariadeniami na kontinuálne monitorovanie krvného cukru";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth sa používa pre komunikáciu s inzulínovou pumpou a zariadeniami na kontinuálne monitorovanie krvného cukru";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "Pre autorizovaný prístup k bolusu";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Kalendár slúži na vytvorenie novej udalosti s glukózou.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Aplikácia Zdravie sa používa na meranie glukózy v krvi, inzulínu a ukladanie sacharidov";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Aplikácia Zdravie sa používa na meranie glukózy v krvi, inzulínu a ukladanie sacharidov";

+ 0 - 20
Trio/Resources/sv.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC används för att skanna Libre-sensorn vid uppstart.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth används för att kommunicera med pump och kontinuerlig glukosmätare";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth används för att kommunicera med insulinpumpen och kontinuerliga glukosmätare";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "För auktoriserad åtkomst till bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Kalendern används för att skapa kalenderhändelser för glukosvärden.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Appen Hälsa används för att lagra blodsockervärden, insulin samt kolhydrater";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Appen Hälsa används för att lagra blodsockervärden, insulin och kolhydrater.";

+ 0 - 20
Trio/Resources/tr.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC Libre sensörlerini taramak için kullanılır.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth, insülin pompası ve sürekli glikoz izleme cihazları ile iletişim kurmak için kullanılır";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth, insülin pompası ve sürekli glikoz izleme cihazları ile iletişim kurmak için kullanılır";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "Bolus'a yetkili erişim için";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Takvim, yeni bir glikoz olayı oluşturmak için kullanılır.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 0 - 20
Trio/Resources/uk.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC використовується для сканування сенсорів Libre.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth використовується для обміну з інсуліновими помпами та безперервним моніторингом глюкози";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth використовується для обміну з інсуліновими помпами та безперервним моніторингом глюкози";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "Для авторизованого болюсу";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Для створення нових подій глюкози використовується календар.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Додаток Health використовується для зберігання глюкози в крові, інсуліну та вуглеводів";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Додаток Health використовується для зберігання глюкози в крові, інсуліну та вуглеводів";

+ 0 - 20
Trio/Resources/vi.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC được sử dụng để quét các cảm biến Libre.";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "Bluetooth được sử dụng để liên lạc với máy bơm insulin và các thiết bị theo dõi đường huyết liên tục/CGM.";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "Bluetooth được sử dụng để liên lạc với máy bơm insulin và các thiết bị theo dõi đường huyết liên tục/CGM.";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "Được cấp quyền truy cập vào bolus";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "Lịch \nCalendar được sử dụng để tạo ra một sự kiện glucose mới.";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Ứng dụng sức khỏe \nHealth được sử dụng để lưu trữ đường huyết, insulin và carbohydrate";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Ứng dụng sức khỏe \nHealth được sử dụng để lưu trữ đường huyết, insulin và carbohydrate";

+ 0 - 20
Trio/Resources/zh-Hans.lproj/InfoPlist.strings

@@ -1,20 +0,0 @@
-/* Privacy - NFC Scan Usage Description */
-"NFCReaderUsageDescription" = "NFC 用于扫描 Libre 传感器。";
-
-/* Privacy - Bluetooth Always Usage Description */
-"NSBluetoothAlwaysUsageDescription" = "蓝牙用于与胰岛素泵和连续血糖监测设备进行通信";
-
-/* Privacy - Bluetooth Peripheral Usage Description */
-"NSBluetoothPeripheralUsageDescription" = "蓝牙用于与胰岛素泵和连续血糖监测设备进行通信";
-
-/* Privacy - Face ID Usage Description */
-"NSFaceIDUsageDescription" = "用于输注胰岛素授权";
-
-/* Privacy - Calendars Usage Description */
-"NSCalendarsUsageDescription" = "日历用于创建一个新的葡萄糖事件。";
-
-/* Privacy - Health Update Usage Description */
-"NSHealthUpdateUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";
-
-/* Privacy - Health Share Usage Description */
-"NSHealthShareUsageDescription" = "Health App is used to store blood glucose, insulin and carbohydrates";

+ 9 - 3
Trio/Sources/APS/APSManager.swift

@@ -22,7 +22,7 @@ protocol APSManager {
     func enactTempBasal(rate: Double, duration: TimeInterval) async
     func determineBasal() async -> Bool
     func determineBasalSync() async
-    func simulateDetermineBasal(carbs: Decimal, iob: Decimal) async -> Determination?
+    func simulateDetermineBasal(simulatedCarbsAmount: Decimal, simulatedBolusAmount: Decimal) async -> Determination?
     func roundBolus(amount: Decimal) -> Decimal
     func roundBolusNoCap(amount: Decimal) -> Decimal
     var lastError: CurrentValueSubject<Error?, Never> { get }
@@ -414,10 +414,16 @@ final class BaseAPSManager: APSManager, Injectable {
         _ = await determineBasal()
     }
 
-    func simulateDetermineBasal(carbs: Decimal, iob: Decimal) async -> Determination? {
+    func simulateDetermineBasal(simulatedCarbsAmount: Decimal, simulatedBolusAmount: Decimal) async -> Determination? {
         do {
             let temp = await fetchCurrentTempBasal(date: Date.now)
-            return try await openAPS.determineBasal(currentTemp: temp, clock: Date(), carbs: carbs, iob: iob, simulation: true)
+            return try await openAPS.determineBasal(
+                currentTemp: temp,
+                clock: Date(),
+                simulatedCarbsAmount: simulatedCarbsAmount,
+                simulatedBolusAmount: simulatedBolusAmount,
+                simulation: true
+            )
         } catch {
             debugPrint(
                 "\(DebuggingIdentifiers.failed) \(#file) \(#function) Error occurred in invokeDummyDetermineBasalSync: \(error)"

+ 1 - 1
Trio/Sources/APS/CGM/AppGroupSource.swift

@@ -115,7 +115,7 @@ struct AppGroupSource: GlucoseSource {
     }
 
     func sourceInfo() -> [String: Any]? {
-        [GlucoseSourceKey.description.rawValue: "Group ID: \(Bundle.main.appGroupSuiteName ?? "Not set"))"]
+        [GlucoseSourceKey.description.rawValue: "Group ID: \(Bundle.main.appGroupSuiteName ?? String(localized: "Not set"))"]
     }
 }
 

+ 10 - 9
Trio/Sources/APS/CGM/CGMType.swift

@@ -14,15 +14,15 @@ enum CGMType: String, JSON, CaseIterable, Identifiable {
         case .none:
             return "None"
         case .nightscout:
-            return "Nightscout"
+            return "Nightscout as CGM"
         case .xdrip:
             return "xDrip4iOS"
         case .simulator:
-            return NSLocalizedString("Glucose Simulator", comment: "Glucose Simulator CGM type")
+            return String(localized: "Glucose Simulator", comment: "Glucose Simulator CGM type")
         case .enlite:
             return "Medtronic Enlite"
         case .plugin:
-            return "plugin CGM"
+            return "Plugin CGM"
         }
     }
 
@@ -52,20 +52,21 @@ enum CGMType: String, JSON, CaseIterable, Identifiable {
     var subtitle: String {
         switch self {
         case .none:
-            return NSLocalizedString("None", comment: "No CGM choiced")
+            return String(localized: "None", comment: "No CGM selected")
         case .nightscout:
-            return NSLocalizedString("Online or internal server", comment: "Online or internal server")
+            return String(localized: "Uses your Nightscout as CGM", comment: "Online or internal server")
         case .xdrip:
-            return NSLocalizedString(
+            return String(
+                localized:
                 "Using shared app group with external CGM app xDrip4iOS",
                 comment: "Shared app group xDrip4iOS"
             )
         case .simulator:
-            return NSLocalizedString("Simple simulator", comment: "Simple simulator")
+            return String(localized: "Glucose Simulator for Demo Only", comment: "Simple simulator")
         case .enlite:
-            return NSLocalizedString("Minilink transmitter", comment: "Minilink transmitter")
+            return String(localized: "Minilink transmitter", comment: "Minilink transmitter")
         case .plugin:
-            return NSLocalizedString("Plugin CGM", comment: "Plugin CGM")
+            return String(localized: "Plugin CGM", comment: "Plugin CGM")
         }
     }
 }

+ 5 - 5
Trio/Sources/APS/CGM/PluginSource.swift

@@ -7,7 +7,7 @@ import LoopKit
 import LoopKitUI
 
 final class PluginSource: GlucoseSource {
-    private let processQueue = DispatchQueue(label: "DexcomSource.processQueue")
+    private let processQueue = DispatchQueue(label: "CGMPluginSource.processQueue")
     private let glucoseStorage: GlucoseStorage!
     var glucoseManager: FetchGlucoseManager?
 
@@ -107,8 +107,7 @@ extension PluginSource: CGMManagerDelegate {
     func cgmManagerWantsDeletion(_ manager: CGMManager) {
         dispatchPrecondition(condition: .onQueue(processQueue))
         debug(.deviceManager, " CGM Manager with identifier \(manager.pluginIdentifier) wants deletion")
-        // TODO:
-        glucoseManager?.cgmGlucoseSourceType = .none
+        glucoseManager?.deleteGlucoseSource()
     }
 
     func cgmManager(_: CGMManager, hasNew readingResult: CGMReadingResult) {
@@ -160,8 +159,9 @@ extension PluginSource: CGMManagerDelegate {
         UUID().uuidString
     }
 
-    func cgmManager(_: CGMManager, didUpdate status: CGMManagerStatus) {
-        debug(.deviceManager, "DEBUG DID UPDATE STATE")
+    func cgmManager(_ cgmManager: CGMManager, didUpdate status: CGMManagerStatus) {
+        debug(.deviceManager, "CGM Manager did update state to \(status)")
+        
         processQueue.async {
             if self.cgmHasValidSensorSession != status.hasValidSensorSession {
                 self.cgmHasValidSensorSession = status.hasValidSensorSession

+ 0 - 8
Trio/Sources/APS/DeviceDataManager.swift

@@ -415,10 +415,6 @@ extension BaseDeviceDataManager: PumpManagerDelegate {
             settingsManager.updateInsulinCurve(status.insulinType)
         }
 
-        broadcaster.notify(PumpTimeZoneObserver.self, on: processQueue) {
-            $0.pumpTimeZoneDidChange(status.timeZone)
-        }
-
         if let omnipod = pumpManager as? OmnipodPumpManager {
             let reservoirVal = omnipod.state.podState?.lastInsulinMeasurements?.reservoirLevel ?? 0xDEAD_BEEF
             // TODO: find the value Pod.maximumReservoirReading
@@ -700,10 +696,6 @@ protocol PumpBatteryObserver {
     func pumpBatteryDidChange(_ battery: Battery)
 }
 
-protocol PumpTimeZoneObserver {
-    func pumpTimeZoneDidChange(_ timezone: TimeZone)
-}
-
 protocol PumpDeactivatedObserver {
     func pumpDeactivatedDidChange()
 }

+ 27 - 4
Trio/Sources/APS/FetchGlucoseManager.swift

@@ -126,10 +126,15 @@ final class BaseFetchGlucoseManager: FetchGlucoseManager, Injectable {
         // if plugin, if not the same pluginID, need to reset the cgmManager
         // if plugin and newManager provides, update cgmManager
         debug(.apsManager, "plugin : \(String(describing: cgmManager?.pluginIdentifier))")
-        if let manager = newManager
-        {
-            cgmManager = manager
-            removeCalibrations()
+
+        if let manager = newManager {
+            // If the pointer to manager is the *same* as our current `cgmManager`, skip re-init
+            if manager !== cgmManager {
+                // or do a more thorough check to see if it is the same class & state
+                removeCalibrations()
+                cgmManager = manager
+                glucoseSource = nil
+            }
         } else if self.cgmGlucoseSourceType == .plugin, cgmManager == nil, let rawCGMManager = rawCGMManager {
             cgmManager = cgmManagerFromRawValue(rawCGMManager)
             updateManagerUnits(cgmManager)
@@ -338,6 +343,24 @@ final class BaseFetchGlucoseManager: FetchGlucoseManager, Injectable {
     }
 }
 
+extension FetchGlucoseManager {
+    /// Dispatches given `functionToInvoke` to the CGM manager's queue (if any).
+    func performOnCGMManagerQueue(_ functionToInvoke: @escaping () -> Void) {
+        // If a CGM manager exists and it defines a delegate queue, use it
+        if let cgmManager = self.cgmManager,
+           let managerQueue = cgmManager.delegateQueue
+        {
+            managerQueue.async {
+                functionToInvoke()
+            }
+        } else {
+            // If there's no cgmManager or no queue, just run the block immediately
+            // This possibly executes `functionToInvoke` on main thread
+            functionToInvoke()
+        }
+    }
+}
+
 extension CGMManager {
     typealias RawValue = [String: Any]
 

+ 25 - 10
Trio/Sources/APS/OpenAPS/OpenAPS.swift

@@ -189,7 +189,10 @@ final class OpenAPS {
         }
     }
 
-    private func parsePumpHistory(_ pumpHistoryObjectIDs: [NSManagedObjectID], iob: Decimal? = nil) async -> String {
+    private func parsePumpHistory(
+        _ pumpHistoryObjectIDs: [NSManagedObjectID],
+        simulatedBolusAmount: Decimal? = nil
+    ) async -> String {
         // Return an empty JSON object if the list of object IDs is empty
         guard !pumpHistoryObjectIDs.isEmpty else { return "{}" }
 
@@ -199,9 +202,9 @@ final class OpenAPS {
             var dtos = self.loadAndMapPumpEvents(pumpHistoryObjectIDs)
 
             // Optionally add the IOB as a DTO
-            if let iob = iob {
-                let iobDTO = self.createIOBDTO(iob: iob)
-                dtos.insert(iobDTO, at: 0)
+            if let simulatedBolusAmount = simulatedBolusAmount {
+                let simulatedBolusDTO = self.createSimulatedBolusDTO(simulatedBolusAmount: simulatedBolusAmount)
+                dtos.insert(simulatedBolusDTO, at: 0)
             }
 
             // Convert the DTOs to JSON
@@ -226,12 +229,24 @@ final class OpenAPS {
             if let tempBasalDTO = event.toTempBasalDTOEnum() {
                 eventDTOs.append(tempBasalDTO)
             }
+            if let pumpSuspendDTO = event.toPumpSuspendDTO() {
+                eventDTOs.append(pumpSuspendDTO)
+            }
+            if let pumpResumeDTO = event.toPumpResumeDTO() {
+                eventDTOs.append(pumpResumeDTO)
+            }
+            if let rewindDTO = event.toRewindDTO() {
+                eventDTOs.append(rewindDTO)
+            }
+            if let primeDTO = event.toPrimeDTO() {
+                eventDTOs.append(primeDTO)
+            }
             return eventDTOs
         }
         return dtos
     }
 
-    private func createIOBDTO(iob: Decimal) -> PumpEventDTO {
+    private func createSimulatedBolusDTO(simulatedBolusAmount: Decimal) -> PumpEventDTO {
         let oneSecondAgo = Calendar.current
             .date(
                 byAdding: .second,
@@ -243,7 +258,7 @@ final class OpenAPS {
         let bolusDTO = BolusDTO(
             id: UUID().uuidString,
             timestamp: dateFormatted,
-            amount: Double(iob),
+            amount: Double(simulatedBolusAmount),
             isExternal: false,
             isSMB: true,
             duration: 0,
@@ -255,8 +270,8 @@ final class OpenAPS {
     func determineBasal(
         currentTemp: TempBasal,
         clock: Date = Date(),
-        carbs: Decimal? = nil,
-        iob: Decimal? = nil,
+        simulatedCarbsAmount: Decimal? = nil,
+        simulatedBolusAmount: Decimal? = nil,
         simulation: Bool = false
     ) async throws -> Determination? {
         debug(.openAPS, "Start determineBasal")
@@ -266,7 +281,7 @@ final class OpenAPS {
 
         // Perform asynchronous calls in parallel
         async let pumpHistoryObjectIDs = fetchPumpHistoryObjectIDs() ?? []
-        async let carbs = fetchAndProcessCarbs(additionalCarbs: carbs ?? 0)
+        async let carbs = fetchAndProcessCarbs(additionalCarbs: simulatedCarbsAmount ?? 0)
         async let glucose = fetchAndProcessGlucose()
         async let oref2 = oref2()
         async let profileAsync = loadFileFromStorageAsync(name: Settings.profile)
@@ -287,7 +302,7 @@ final class OpenAPS {
             reservoir,
             preferences
         ) = await (
-            parsePumpHistory(await pumpHistoryObjectIDs, iob: iob),
+            parsePumpHistory(await pumpHistoryObjectIDs, simulatedBolusAmount: simulatedBolusAmount),
             carbs,
             glucose,
             oref2,

+ 1 - 1
Trio/Sources/APS/Storage/ContactImageStorage.swift

@@ -39,7 +39,7 @@ final class BaseContactImageStorage: ContactImageStorage, Injectable {
 
             return fetchedContactImageEntries.compactMap { entry in
                 ContactImageEntry(
-                    name: entry.name ?? "No name provided",
+                    name: entry.name ?? String(localized: "No name provided"),
                     layout: ContactImageLayout(rawValue: entry.layout ?? "Default") ?? .default,
                     ring: ContactImageLargeRing(rawValue: entry.ring ?? "Hidden") ?? .none,
                     primary: ContactImageValue(rawValue: entry.primary ?? "Glucose Reading") ?? .glucose,

+ 2 - 2
Trio/Sources/APS/Storage/GlucoseStorage.swift

@@ -538,9 +538,9 @@ enum GlucoseAlarm {
     var displayName: String {
         switch self {
         case .high:
-            return NSLocalizedString("LOWALERT!", comment: "LOWALERT!")
+            return String(localized: "LOWALERT!", comment: "LOWALERT!")
         case .low:
-            return NSLocalizedString("HIGHALERT!", comment: "HIGHALERT!")
+            return String(localized: "HIGHALERT!", comment: "HIGHALERT!")
         }
     }
 }

+ 3 - 3
Trio/Sources/APS/Storage/OverrideStorage.swift

@@ -210,7 +210,7 @@ final class BaseOverrideStorage: @preconcurrency OverrideStorage, Injectable {
         let results = await CoreDataStack.shared.fetchEntitiesAsync(
             ofType: OverrideStored.self,
             onContext: backgroundContext,
-            predicate: NSPredicate.lastActiveOverrideNotYetUploadedToNightscout,
+            predicate: NSPredicate.lastActiveAdjustmentNotYetUploadedToNightscout,
             key: "date",
             ascending: false
         )
@@ -225,7 +225,7 @@ final class BaseOverrideStorage: @preconcurrency OverrideStorage, Injectable {
                     eventType: OverrideStored.EventType.nsExercise,
                     createdAt: override.date ?? Date(),
                     enteredBy: NightscoutExercise.local,
-                    notes: override.name ?? "Custom Override",
+                    notes: override.name ?? String(localized: "Custom Override"),
                     id: UUID(uuidString: override.id ?? UUID().uuidString)
                 )
             }
@@ -256,7 +256,7 @@ final class BaseOverrideStorage: @preconcurrency OverrideStorage, Injectable {
                     eventType: OverrideStored.EventType.nsExercise,
                     createdAt: (overrideRun.startDate ?? overrideRun.override?.date) ?? Date(),
                     enteredBy: NightscoutExercise.local,
-                    notes: overrideRun.name ?? "Custom Override",
+                    notes: overrideRun.name ?? String(localized: "Custom Override"),
                     id: overrideRun.id
                 )
             }

+ 4 - 2
Trio/Sources/APS/Storage/PumpHistoryStorage.swift

@@ -94,7 +94,8 @@ final class BasePumpHistoryStorage: PumpHistoryStorage, Injectable {
 
                         let newPumpEvent = PumpEventStored(context: self.context)
                         newPumpEvent.id = UUID().uuidString
-                        newPumpEvent.timestamp = event.date
+                        // restrict entry to now or past
+                        newPumpEvent.timestamp = event.date > Date() ? Date() : event.date
                         newPumpEvent.type = PumpEvent.bolus.rawValue
                         newPumpEvent.isUploadedToNS = false
                         newPumpEvent.isUploadedToHealth = false
@@ -232,7 +233,8 @@ final class BasePumpHistoryStorage: PumpHistoryStorage, Injectable {
             // create pump event
             let newPumpEvent = PumpEventStored(context: self.context)
             newPumpEvent.id = UUID().uuidString
-            newPumpEvent.timestamp = timestamp
+            // restrict entry to now or past
+            newPumpEvent.timestamp = timestamp > Date() ? Date() : timestamp
             newPumpEvent.type = PumpEvent.bolus.rawValue
             newPumpEvent.isUploadedToNS = false
             newPumpEvent.isUploadedToHealth = false

+ 3 - 3
Trio/Sources/APS/Storage/TempTargetsStorage.swift

@@ -14,7 +14,7 @@ protocol TempTargetsStorage {
     func fetchScheduledTempTargets() async -> [NSManagedObjectID]
     func fetchScheduledTempTarget(for targetDate: Date) async -> [NSManagedObjectID]
     func copyRunningTempTarget(_ tempTarget: TempTargetStored) async -> NSManagedObjectID
-    func deleteOverridePreset(_ objectID: NSManagedObjectID) async
+    func deleteTempTargetPreset(_ objectID: NSManagedObjectID) async
     func loadLatestTempTargetConfigurations(fetchLimit: Int) async -> [NSManagedObjectID]
     func syncDate() -> Date
     func recent() -> [TempTarget]
@@ -216,7 +216,7 @@ final class BaseTempTargetsStorage: TempTargetsStorage, Injectable {
         return newTempTarget.objectID
     }
 
-    @MainActor func deleteOverridePreset(_ objectID: NSManagedObjectID) async {
+    @MainActor func deleteTempTargetPreset(_ objectID: NSManagedObjectID) async {
         await CoreDataStack.shared.deleteObject(identifiedBy: objectID)
     }
 
@@ -246,7 +246,7 @@ final class BaseTempTargetsStorage: TempTargetsStorage, Injectable {
         let results = await CoreDataStack.shared.fetchEntitiesAsync(
             ofType: TempTargetStored.self,
             onContext: backgroundContext,
-            predicate: NSPredicate.lastActiveOverrideNotYetUploadedToNightscout, // TODO: create adjustment predicate (OR+TT)
+            predicate: NSPredicate.lastActiveAdjustmentNotYetUploadedToNightscout,
             key: "date",
             ascending: false
         )

+ 22 - 7
Trio/Sources/Application/TrioApp.swift

@@ -13,7 +13,7 @@ import Swinject
     // Read the color scheme preference from UserDefaults; defaults to system default setting
     @AppStorage("colorSchemePreference") private var colorSchemePreference: ColorSchemeOption = .systemDefault
 
-    let coreDataStack = CoreDataStack.shared
+    let coreDataStack: CoreDataStack
 
     @State private var appState = AppState()
 
@@ -68,14 +68,29 @@ import Swinject
             "Trio Started: v\(Bundle.main.releaseVersionNumber ?? "")(\(Bundle.main.buildVersionNumber ?? "")) [buildDate: \(String(describing: BuildDetails.default.buildDate()))] [buildExpires: \(String(describing: BuildDetails.default.calculateExpirationDate()))]"
         )
 
-        // Load services
-        loadServices()
+        // Setup up the Core Data Stack
+        coreDataStack = CoreDataStack.shared
 
-        // Fix bug in iOS 18 related to the translucent tab bar
-        configureTabBarAppearance()
+        do {
+            // Explicitly initialize Core Data Stacak
+            try coreDataStack.initializeStack()
+
+            // Load services
+            loadServices()
+
+            // Fix bug in iOS 18 related to the translucent tab bar
+            configureTabBarAppearance()
 
-        // Clear the persistentHistory and the NSManagedObjects that are older than 90 days every time the app starts
-        cleanupOldData()
+            // Clear the persistentHistory and the NSManagedObjects that are older than 90 days every time the app starts
+            cleanupOldData()
+        } catch {
+            debug(
+                .coreData,
+                "Failed to initialize Core Data Stack: \(error.localizedDescription)"
+            )
+            // Handle initialization failure
+            fatalError("Core Data Stack initialization failed: \(error.localizedDescription)")
+        }
     }
 
     var body: some Scene {

+ 4 - 4
Trio/Sources/Helpers/BuildDetails.swift

@@ -27,8 +27,8 @@ class BuildDetails {
     }
 
     var branchAndSha: String {
-        let branch = dict["com-trio-branch"] as? String ?? "Unknown"
-        let sha = dict["com-trio-commit-sha"] as? String ?? "Unknown"
+        let branch = dict["com-trio-branch"] as? String ?? String(localized: "Unknown")
+        let sha = dict["com-trio-commit-sha"] as? String ?? String(localized: "Unknown")
         return "\(branch) \(sha)"
     }
 
@@ -75,9 +75,9 @@ class BuildDetails {
     // Expiration header based on build type
     var expirationHeaderString: String {
         if isTestFlightBuild() {
-            return "Beta (TestFlight) Expires"
+            return String(localized: "Beta (TestFlight) Expires")
         } else {
-            return "App Expires"
+            return String(localized: "App Expires")
         }
     }
 }

+ 15 - 0
Trio/Sources/Helpers/CGMOptions.swift

@@ -0,0 +1,15 @@
+let cgmOptions: [CGMOption] = [
+    CGMOption(name: "Dexcom G5", predicate: { $0.type == .plugin && $0.displayName.contains("G5") }),
+    CGMOption(name: "Dexcom G6 / ONE", predicate: { $0.type == .plugin && $0.displayName.contains("G6") }),
+    CGMOption(name: "Dexcom G7 / ONE+", predicate: { $0.type == .plugin && $0.displayName.contains("G7") }),
+    CGMOption(name: "Dexcom Share", predicate: { $0.type == .plugin && $0.displayName.contains("Dexcom Share") }),
+    CGMOption(name: "FreeStyle Libre", predicate: { $0.type == .plugin && $0.displayName == "FreeStyle Libre" }),
+    CGMOption(
+        name: "FreeStyle Libre Demo",
+        predicate: { $0.type == .plugin && $0.displayName == "FreeStyle Libre Demo" }
+    ),
+    CGMOption(name: "Glucose Simulator", predicate: { $0.type == .simulator }),
+    CGMOption(name: "Medtronic Enlite", predicate: { $0.type == .enlite }),
+    CGMOption(name: "Nightscout as CGM", predicate: { $0.type == .nightscout }),
+    CGMOption(name: "xDrip4iOS", predicate: { $0.type == .xdrip })
+]

+ 4 - 4
Trio/Sources/Helpers/HKUnit.swift

@@ -44,13 +44,13 @@ extension HKUnit {
 
     var localizedShortUnitString: String {
         if self == HKUnit.millimolesPerLiter {
-            return NSLocalizedString("mmol/L", comment: "The short unit display string for millimoles of glucose per liter")
+            return String(localized: "mmol/L", comment: "The short unit display string for millimoles of glucose per liter")
         } else if self == .milligramsPerDeciliter {
-            return NSLocalizedString("mg/dL", comment: "The short unit display string for milligrams of glucose per decilter")
+            return String(localized: "mg/dL", comment: "The short unit display string for milligrams of glucose per decilter")
         } else if self == .internationalUnit() {
-            return NSLocalizedString("U", comment: "The short unit display string for international units of insulin")
+            return String(localized: "U", comment: "The short unit display string for international units of insulin")
         } else if self == .gram() {
-            return NSLocalizedString("g", comment: "The short unit display string for grams")
+            return String(localized: "g", comment: "The short unit display string for grams")
         } else {
             return String(describing: self)
         }

Plik diff jest za duży
+ 180308 - 0
Trio/Sources/Localizations/Main/Localizable.xcstrings


Plik diff jest za duży
+ 0 - 2223
Trio/Sources/Localizations/Main/ar.lproj/Localizable.strings


Plik diff jest za duży
+ 0 - 1917
Trio/Sources/Localizations/Main/ca.lproj/Localizable.strings


Plik diff jest za duży
+ 0 - 2184
Trio/Sources/Localizations/Main/da.lproj/Localizable.strings


Plik diff jest za duży
+ 0 - 2229
Trio/Sources/Localizations/Main/de.lproj/Localizable.strings


Plik diff jest za duży
+ 0 - 2231
Trio/Sources/Localizations/Main/en.lproj/Localizable.strings


Plik diff jest za duży
+ 0 - 2228
Trio/Sources/Localizations/Main/es.lproj/Localizable.strings


Plik diff jest za duży
+ 0 - 2229
Trio/Sources/Localizations/Main/fi.lproj/Localizable.strings


Plik diff jest za duży
+ 0 - 2220
Trio/Sources/Localizations/Main/fr.lproj/Localizable.strings


Plik diff jest za duży
+ 0 - 2223
Trio/Sources/Localizations/Main/he.lproj/Localizable.strings


Plik diff jest za duży
+ 0 - 2178
Trio/Sources/Localizations/Main/hu.lproj/Localizable.strings


Plik diff jest za duży
+ 0 - 2225
Trio/Sources/Localizations/Main/it.lproj/Localizable.strings


Plik diff jest za duży
+ 0 - 2223
Trio/Sources/Localizations/Main/nb.lproj/Localizable.strings


Plik diff jest za duży
+ 0 - 2231
Trio/Sources/Localizations/Main/nl.lproj/Localizable.strings


Plik diff jest za duży
+ 0 - 2225
Trio/Sources/Localizations/Main/pl.lproj/Localizable.strings


Plik diff jest za duży
+ 0 - 2223
Trio/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings


Plik diff jest za duży
+ 0 - 2223
Trio/Sources/Localizations/Main/pt-PT.lproj/Localizable.strings


Plik diff jest za duży
+ 0 - 2226
Trio/Sources/Localizations/Main/ru.lproj/Localizable.strings


Plik diff jest za duży
+ 0 - 2178
Trio/Sources/Localizations/Main/sk.lproj/Localizable.strings


Plik diff jest za duży
+ 0 - 2223
Trio/Sources/Localizations/Main/sv.lproj/Localizable.strings


Plik diff jest za duży
+ 0 - 2227
Trio/Sources/Localizations/Main/tr.lproj/Localizable.strings


Plik diff jest za duży
+ 0 - 2223
Trio/Sources/Localizations/Main/uk.lproj/Localizable.strings


Plik diff jest za duży
+ 0 - 2180
Trio/Sources/Localizations/Main/vi.lproj/Localizable.strings


Plik diff jest za duży
+ 0 - 2225
Trio/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings


+ 4 - 0
Trio/Sources/Logger/Logger.swift

@@ -115,6 +115,7 @@ final class Logger {
     static let remoteControl = Logger(category: .remoteControl, reporter: baseReporter)
     static let bolusState = Logger(category: .bolusState, reporter: baseReporter)
     static let watchManager = Logger(category: .watchManager, reporter: baseReporter)
+    static let coreData = Logger(category: .coreData, reporter: baseReporter)
 
     enum Category: String {
         case `default`
@@ -127,6 +128,7 @@ final class Logger {
         case remoteControl
         case bolusState
         case watchManager
+        case coreData
 
         var name: String {
             rawValue.capitalizingFirstLetter()
@@ -144,6 +146,7 @@ final class Logger {
             case .remoteControl: return .remoteControl
             case .bolusState: return .bolusState
             case .watchManager: return .watchManager
+            case .coreData: return .coreData
             }
         }
 
@@ -154,6 +157,7 @@ final class Logger {
             case .apsManager,
                  .bolusState,
                  .businessLogic,
+                 .coreData,
                  .deviceManager,
                  .nightscout,
                  .openAPS,

+ 16 - 16
Trio/Sources/Models/ContactTrickEntry.swift

@@ -130,23 +130,23 @@ enum ContactImageValue: String, JSON, CaseIterable, Identifiable, Codable {
     var displayName: String {
         switch self {
         case .none:
-            return NSLocalizedString("None", comment: "")
+            return String(localized: "None", comment: "")
         case .glucose:
-            return NSLocalizedString("Glucose Reading", comment: "")
+            return String(localized: "Glucose Reading", comment: "")
         case .eventualBG:
-            return NSLocalizedString("Eventual Glucose", comment: "")
+            return String(localized: "Eventual Glucose", comment: "")
         case .delta:
-            return NSLocalizedString("Glucose Delta", comment: "")
+            return String(localized: "Glucose Delta", comment: "")
         case .trend:
-            return NSLocalizedString("Glucose Trend", comment: "")
+            return String(localized: "Glucose Trend", comment: "")
         case .lastLoopDate:
-            return NSLocalizedString("Last Loop Time", comment: "")
+            return String(localized: "Last Loop Time", comment: "")
         case .cob:
-            return NSLocalizedString("COB", comment: "")
+            return String(localized: "COB", comment: "")
         case .iob:
-            return NSLocalizedString("IOB", comment: "")
+            return String(localized: "IOB", comment: "")
         case .ring:
-            return NSLocalizedString("Loop Status", comment: "")
+            return String(localized: "Loop Status", comment: "")
         }
     }
 }
@@ -159,9 +159,9 @@ enum ContactImageLayout: String, JSON, CaseIterable, Identifiable, Codable {
     var displayName: String {
         switch self {
         case .default:
-            return NSLocalizedString("Default", comment: "")
+            return String(localized: "Default", comment: "")
         case .split:
-            return NSLocalizedString("Split", comment: "")
+            return String(localized: "Split", comment: "")
         }
     }
 }
@@ -178,15 +178,15 @@ enum ContactImageLargeRing: String, JSON, CaseIterable, Identifiable, Codable {
     var displayName: String {
         switch self {
         case .none:
-            return NSLocalizedString("Hidden", comment: "")
+            return String(localized: "Hidden", comment: "")
         case .loop:
-            return NSLocalizedString("Loop Status", comment: "")
+            return String(localized: "Loop Status", comment: "")
 //        case .iob:
-//            return NSLocalizedString("Insulin on Board (IOB)", comment: "")
+//            return String(localized: "Insulin on Board (IOB)", comment: "")
 //        case .cob:
-//            return NSLocalizedString("Carbs on Board (COB)", comment: "")
+//            return String(localized: "Carbs on Board (COB)", comment: "")
 //        case .iobcob:
-//            return NSLocalizedString("IOB + COB", comment: "")
+//            return String(localized: "IOB + COB", comment: "")
         }
     }
 }

+ 2 - 2
Trio/Sources/Models/ForecastDisplayType.swift

@@ -7,10 +7,10 @@ enum ForecastDisplayType: String, JSON, CaseIterable, Identifiable, Codable, Has
     var displayName: String {
         switch self {
         case .cone:
-            return NSLocalizedString("Cone", comment: "")
+            return String(localized: "Cone", comment: "")
 
         case .lines:
-            return NSLocalizedString("Lines", comment: "")
+            return String(localized: "Lines", comment: "")
         }
     }
 }

+ 2 - 2
Trio/Sources/Models/HbA1cDisplayUnit.swift

@@ -8,9 +8,9 @@ enum HbA1cDisplayUnit: String, JSON, CaseIterable, Identifiable, Codable, Hashab
     var displayName: String {
         switch self {
         case .percent:
-            return NSLocalizedString("Percent", comment: "")
+            return String(localized: "Percent", comment: "")
         case .mmolMol:
-            return NSLocalizedString("mmol/mol", comment: "")
+            return String(localized: "mmol/mol", comment: "")
         }
     }
 }

+ 2 - 2
Trio/Sources/Models/LockScreenView.swift

@@ -7,9 +7,9 @@ enum LockScreenView: String, JSON, CaseIterable, Identifiable, Codable, Hashable
     var displayName: String {
         switch self {
         case .simple:
-            return NSLocalizedString("Simple", comment: "")
+            return String(localized: "Simple", comment: "")
         case .detailed:
-            return NSLocalizedString("Detailed", comment: "")
+            return String(localized: "Detailed", comment: "")
         }
     }
 }

+ 2 - 2
Trio/Sources/Models/TimeInRangeChartStyle.swift

@@ -8,9 +8,9 @@ enum TimeInRangeChartStyle: String, JSON, CaseIterable, Identifiable, Codable, H
     var displayName: String {
         switch self {
         case .vertical:
-            return NSLocalizedString("Vertical", comment: "")
+            return String(localized: "Vertical", comment: "")
         case .horizontal:
-            return NSLocalizedString("Horizontal", comment: "")
+            return String(localized: "Horizontal", comment: "")
         }
     }
 }

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

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

+ 2 - 0
Trio/Sources/Models/WatchMessageKeys.swift

@@ -37,6 +37,8 @@ enum WatchMessageKeys {
     static let cob = "cob"
     static let lastLoopTime = "lastLoopTime"
     static let glucoseValues = "glucoseValues"
+    static let minYAxisValue = "minYAxisValue"
+    static let maxYAxisValue = "maxYAxisValue"
     static let overridePresets = "overridePresets"
     static let tempTargetPresets = "tempTargetPresets"
 

+ 9 - 3
Trio/Sources/Models/WatchState.swift

@@ -8,6 +8,8 @@ struct WatchState: Hashable, Equatable, Sendable, Encodable {
     var trend: String?
     var delta: String?
     var glucoseValues: [WatchGlucoseObject] = []
+    var minYAxisValue: Decimal = 39.0
+    var maxYAxisValue: Decimal = 200.0
     var units: GlucoseUnits = .mgdL
     var iob: String?
     var cob: String?
@@ -17,9 +19,9 @@ struct WatchState: Hashable, Equatable, Sendable, Encodable {
 
     // Safety limits
     var maxBolus: Decimal = 10.0
-    var maxCarbs: Decimal = 250
-    var maxFat: Decimal = 250
-    var maxProtein: Decimal = 250
+    var maxCarbs: Decimal = 250.0
+    var maxFat: Decimal = 250.0
+    var maxProtein: Decimal = 250.0
 
     // Pump specific dosing increment
     var bolusIncrement: Decimal = 0.05
@@ -34,6 +36,8 @@ struct WatchState: Hashable, Equatable, Sendable, Encodable {
             zip(lhs.glucoseValues, rhs.glucoseValues).allSatisfy {
                 $0.0.date == $0.1.date && $0.0.glucose == $0.1.glucose && $0.0.color == $0.1.color
             } &&
+            lhs.minYAxisValue == rhs.minYAxisValue &&
+            lhs.maxYAxisValue == rhs.maxYAxisValue &&
             lhs.units == rhs.units &&
             lhs.iob == rhs.iob &&
             lhs.cob == rhs.cob &&
@@ -58,6 +62,8 @@ struct WatchState: Hashable, Equatable, Sendable, Encodable {
             hasher.combine(value.glucose)
             hasher.combine(value.color)
         }
+        hasher.combine(minYAxisValue)
+        hasher.combine(maxYAxisValue)
         hasher.combine(units)
         hasher.combine(iob)
         hasher.combine(cob)

+ 2 - 5
Trio/Sources/Modules/Adjustments/AdjustmentsDataFlow.swift

@@ -11,15 +11,12 @@ enum Adjustments {
         var id: String { rawValue }
 
         var name: String {
-            var name: String = ""
             switch self {
             case .overrides:
-                name = "Overrides"
+                return String(localized: "Overrides", comment: "Selected Tab")
             case .tempTargets:
-                name = "Temp Targets"
+                return String(localized: "Temp Targets", comment: "Selected Tab")
             }
-
-            return NSLocalizedString(name, comment: "Selected Tab")
         }
     }
 }

+ 2 - 2
Trio/Sources/Modules/Adjustments/AdjustmentsStateModel+Extensions/AdjustmentsStateModel+Overrides.swift

@@ -224,7 +224,7 @@ extension Adjustments.StateModel {
 
             if let overrideToEdit = try viewContext.existingObject(with: firstID) as? OverrideStored {
                 currentActiveOverride = overrideToEdit
-                activeOverrideName = overrideToEdit.name ?? "Custom Override"
+                activeOverrideName = overrideToEdit.name ?? String(localized: "Custom Override")
             }
         } catch {
             debugPrint(
@@ -248,7 +248,7 @@ extension Adjustments.StateModel {
 
             if let overrideToEdit = try viewContext.existingObject(with: duplicateId) as? OverrideStored {
                 currentActiveOverride = overrideToEdit
-                activeOverrideName = overrideToEdit.name ?? "Custom Override"
+                activeOverrideName = overrideToEdit.name ?? String(localized: "Custom Override")
             }
         } catch {
             debugPrint(

+ 5 - 5
Trio/Sources/Modules/Adjustments/AdjustmentsStateModel+Extensions/AdjustmentsStateModel+TempTargets.swift

@@ -44,7 +44,7 @@ extension Adjustments.StateModel {
 
             if let tempTargetToEdit = try viewContext.existingObject(with: firstID) as? TempTargetStored {
                 currentActiveTempTarget = tempTargetToEdit
-                activeTempTargetName = tempTargetToEdit.name ?? "Custom Temp Target"
+                activeTempTargetName = tempTargetToEdit.name ?? String(localized: "Custom Temp Target")
                 tempTargetTarget = tempTargetToEdit.target?.decimalValue ?? 0
             }
         } catch {
@@ -254,7 +254,7 @@ extension Adjustments.StateModel {
             )
             tempTargetStorage.saveTempTargetsToStorage([tempTarget])
         } catch {
-            debugPrint("\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to enact Override Preset")
+            debugPrint("\(DebuggingIdentifiers.failed) \(#file) \(#function) Failed to enact TempTarget Preset")
         }
     }
 
@@ -313,7 +313,7 @@ extension Adjustments.StateModel {
 
     /// Duplicates the current preset and cancels the previous one.
     @MainActor func duplicateTempTargetPresetAndCancelPreviousTempTarget() async {
-        // We get the current active Preset by using currentActiveTempTarget which can either be a Preset or a custom Override
+        // We get the current active Preset by using currentActiveTempTarget which can either be a Preset or a custom TempTarget
         guard let tempTargetPresetToDuplicate = currentActiveTempTarget,
               tempTargetPresetToDuplicate.isPreset == true else { return }
 
@@ -333,7 +333,7 @@ extension Adjustments.StateModel {
             if let tempTargetToEdit = try viewContext.existingObject(with: duplidateId) as? TempTargetStored
             {
                 currentActiveTempTarget = tempTargetToEdit
-                activeTempTargetName = tempTargetToEdit.name ?? "Custom Temp Target"
+                activeTempTargetName = tempTargetToEdit.name ?? String(localized: "Custom Temp Target")
             }
         } catch {
             debugPrint(
@@ -344,7 +344,7 @@ extension Adjustments.StateModel {
 
     /// Deletes a Temp Target preset.
     func invokeTempTargetPresetDeletion(_ objectID: NSManagedObjectID) async {
-        await tempTargetStorage.deleteOverridePreset(objectID)
+        await tempTargetStorage.deleteTempTargetPreset(objectID)
         setupTempTargetPresetsArray()
     }
 

+ 66 - 35
Trio/Sources/Modules/AlgorithmAdvancedSettings/View/AlgorithmAdvancedSettingsRootView.swift

@@ -38,13 +38,16 @@ extension AlgorithmAdvancedSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = NSLocalizedString("Max Daily Safety Multiplier", comment: "Max Daily Safety Multiplier")
+                            hintLabel = String(localized: "Max Daily Safety Multiplier", comment: "Max Daily Safety Multiplier")
                         }
                     ),
                     units: state.units,
                     type: .decimal("maxDailySafetyMultiplier"),
-                    label: NSLocalizedString("Max Daily Safety Multiplier", comment: "Max Daily Safety Multiplier"),
-                    miniHint: "Limits temporary basal rates to this percentage of your largest basal rate.",
+                    label: String(localized: "Max Daily Safety Multiplier", comment: "Max Daily Safety Multiplier"),
+                    miniHint: String(
+                        localized: "Limits temporary basal rates to this percentage of your largest basal rate.",
+                        comment: "Mini Hint for Max Daily Safety Multiplier"
+                    ),
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: 300%").bold()
@@ -64,7 +67,8 @@ extension AlgorithmAdvancedSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = NSLocalizedString(
+                            hintLabel = String(
+                                localized:
                                 "Current Basal Safety Multiplier",
                                 comment: "Current Basal Safety Multiplier"
                             )
@@ -72,8 +76,11 @@ extension AlgorithmAdvancedSettings {
                     ),
                     units: state.units,
                     type: .decimal("currentBasalSafetyMultiplier"),
-                    label: NSLocalizedString("Current Basal Safety Multiplier", comment: "Current Basal Safety Multiplier"),
-                    miniHint: "Limits temporary basal rates to this percentage of the current basal rate.",
+                    label: String(localized: "Current Basal Safety Multiplier", comment: "Current Basal Safety Multiplier"),
+                    miniHint: String(
+                        localized: "Limits temporary basal rates to this percentage of the current basal rate.",
+                        comment: "Mini Hint for Current Basal Safety Multiplier"
+                    ),
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: 400%").bold()
@@ -95,13 +102,16 @@ extension AlgorithmAdvancedSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = "Duration of Insulin Action"
+                            hintLabel = String(localized: "Duration of Insulin Action", comment: "Duration of Insulin Action")
                         }
                     ),
                     units: state.units,
                     type: .decimal("dia"),
-                    label: "Duration of Insulin Action",
-                    miniHint: "Number of hours insulin is active in your body.",
+                    label: String(localized: "Duration of Insulin Action", comment: "Duration of Insulin Action"),
+                    miniHint: String(
+                        localized: "Number of hours insulin is active in your body.",
+                        comment: "Mini Hint for Duration of Insulin Action"
+                    ),
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: 10 hours").bold()
@@ -125,14 +135,17 @@ extension AlgorithmAdvancedSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = NSLocalizedString("Use Custom Peak Time", comment: "Use Custom Peak Time")
+                            hintLabel = String(localized: "Use Custom Peak Time", comment: "Use Custom Peak Time")
                         }
                     ),
                     units: state.units,
                     type: .conditionalDecimal("insulinPeakTime"),
-                    label: NSLocalizedString("Use Custom Peak Time", comment: "Use Custom Peak Time"),
-                    conditionalLabel: NSLocalizedString("Insulin Peak Time", comment: "Insulin Peak Time"),
-                    miniHint: "Set a custom time for peak insulin effect.",
+                    label: String(localized: "Use Custom Peak Time", comment: "Use Custom Peak Time"),
+                    conditionalLabel: String(localized: "Insulin Peak Time", comment: "Insulin Peak Time"),
+                    miniHint: String(
+                        localized: "Set a custom time for peak insulin effect.",
+                        comment: "Mini Hint for Insulin Peak Time"
+                    ),
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: Set by Insulin Type").bold()
@@ -156,13 +169,16 @@ extension AlgorithmAdvancedSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = NSLocalizedString("Skip Neutral Temps", comment: "Skip Neutral Temps")
+                            hintLabel = String(localized: "Skip Neutral Temps", comment: "Skip Neutral Temps")
                         }
                     ),
                     units: state.units,
                     type: .boolean,
-                    label: NSLocalizedString("Skip Neutral Temps", comment: "Skip Neutral Temps"),
-                    miniHint: "Skip neutral temporary basal rates to reduce MDT pump alerts.",
+                    label: String(localized: "Skip Neutral Temps", comment: "Skip Neutral Temps"),
+                    miniHint: String(
+                        localized: "Skip neutral temporary basal rates to reduce MDT pump alerts.",
+                        comment: "Mini Hint for Skip Neutral Temps"
+                    ),
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: OFF").bold()
@@ -183,13 +199,16 @@ extension AlgorithmAdvancedSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = NSLocalizedString("Unsuspend If No Temp", comment: "Unsuspend If No Temp")
+                            hintLabel = String(localized: "Unsuspend If No Temp", comment: "Unsuspend If No Temp")
                         }
                     ),
                     units: state.units,
                     type: .boolean,
-                    label: NSLocalizedString("Unsuspend If No Temp", comment: "Unsuspend If No Temp"),
-                    miniHint: "Resume pump automatically after suspension.",
+                    label: String(localized: "Unsuspend If No Temp", comment: "Unsuspend If No Temp"),
+                    miniHint: String(
+                        localized: "Resume pump automatically after suspension.",
+                        comment: "Mini Hint for Unsuspend If No Temp"
+                    ),
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: OFF").bold()
@@ -208,13 +227,16 @@ extension AlgorithmAdvancedSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = NSLocalizedString("Suspend Zeros IOB", comment: "Suspend Zeros IOB")
+                            hintLabel = String(localized: "Suspend Zeros IOB", comment: "Suspend Zeros IOB")
                         }
                     ),
                     units: state.units,
                     type: .boolean,
-                    label: NSLocalizedString("Suspend Zeros IOB", comment: "Suspend Zeros IOB"),
-                    miniHint: "Clear temporary basal rates and reset IOB when suspended.",
+                    label: String(localized: "Suspend Zeros IOB", comment: "Suspend Zeros IOB"),
+                    miniHint: String(
+                        localized: "Clear temporary basal rates and reset IOB when suspended.",
+                        comment: "Mini Hint for Suspend Zeros IOB"
+                    ),
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: OFF").bold()
@@ -236,12 +258,12 @@ extension AlgorithmAdvancedSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = NSLocalizedString("Min 5m Carb Impact", comment: "Min 5m Carb Impact")
+                            hintLabel = String(localized: "Min 5m Carb Impact", comment: "Min 5m Carb Impact")
                         }
                     ),
                     units: state.units,
                     type: .decimal("min5mCarbimpact"),
-                    label: NSLocalizedString("Min 5m Carb Impact", comment: "Min 5m Carb Impact"),
+                    label: String(localized: "Min 5m Carb Impact", comment: "Min 5m Carb Impact"),
                     miniHint: "Default impact of carb absorption over a 5 minute interval.",
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
@@ -249,7 +271,7 @@ extension AlgorithmAdvancedSettings {
                             "Min 5m Carb Impact sets the expected glucose rise from carbs over 5 minutes when absorption isn't obvious from glucose data."
                         )
                         Text(
-                            "The default value of 8 mg/dL per 5 minutes corresponds to an absorption rate of 24 g of carbs per hour."
+                            "The default is an expected \(state.units == .mgdL ? "8" : 8.formattedAsMmolL) \(state.units.rawValue)/5min. This affects how fast COB is decayed in situations when carb absorption is not visible in BG deviations. The default of \(state.units == .mgdL ? "8" : 8.formattedAsMmolL) \(state.units.rawValue)/5min corresponds to a minimum carb absorption rate of 24 g/hr at a CSF of \(state.units == .mgdL ? "4" : 4.formattedAsMmolL) \(state.units.rawValue)/g."
                         )
                         Text(
                             "This setting helps the system estimate how much glucose your body is absorbing, even when it's not immediately visible in your glucose data, ensuring more accurate insulin dosing during carb absorption."
@@ -265,13 +287,16 @@ extension AlgorithmAdvancedSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = NSLocalizedString("Remaining Carbs Percentage", comment: "Remaining Carbs Percentage")
+                            hintLabel = String(localized: "Remaining Carbs Percentage", comment: "Remaining Carbs Percentage")
                         }
                     ),
                     units: state.units,
                     type: .decimal("remainingCarbsFraction"),
-                    label: NSLocalizedString("Remaining Carbs Percentage", comment: "Remaining Carbs Percentage"),
-                    miniHint: "Percentage of carbs still available if no absorption is detected.",
+                    label: String(localized: "Remaining Carbs Percentage", comment: "Remaining Carbs Percentage"),
+                    miniHint: String(
+                        localized: "Percentage of carbs still available if no absorption is detected.",
+                        comment: "Mini Hint for Remaining Carbs Percentage"
+                    ),
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: 100%").bold()
@@ -292,13 +317,16 @@ extension AlgorithmAdvancedSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = NSLocalizedString("Remaining Carbs Cap", comment: "Remaining Carbs Cap")
+                            hintLabel = String(localized: "Remaining Carbs Cap", comment: "Remaining Carbs Cap")
                         }
                     ),
                     units: state.units,
                     type: .decimal("remainingCarbsCap"),
-                    label: NSLocalizedString("Remaining Carbs Cap", comment: "Remaining Carbs Cap"),
-                    miniHint: "Maximum amount of carbs still available if no absorption is detected.",
+                    label: String(localized: "Remaining Carbs Cap", comment: "Remaining Carbs Cap"),
+                    miniHint: String(
+                        localized: "Maximum amount of carbs still available if no absorption is detected.",
+                        comment: "Mini hint for Remaining Carbs Cap"
+                    ),
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: 90 g").bold()
@@ -319,13 +347,16 @@ extension AlgorithmAdvancedSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = NSLocalizedString("Noisy CGM Target Multiplier", comment: "Noisy CGM Target Multiplier")
+                            hintLabel = String(localized: "Noisy CGM Target Multiplier", comment: "Noisy CGM Target Multiplier")
                         }
                     ),
                     units: state.units,
                     type: .decimal("noisyCGMTargetMultiplier"),
-                    label: NSLocalizedString("Noisy CGM Target Increase", comment: "Noisy CGM Target Increase"),
-                    miniHint: "Percentage increase of glucose target when CGM is inconsistent.",
+                    label: String(localized: "Noisy CGM Target Increase", comment: "Noisy CGM Target Increase"),
+                    miniHint: String(
+                        localized: "Percentage increase of glucose target when CGM is inconsistent.",
+                        comment: "Mini Hint for Noisy CGM Target Increase"
+                    ),
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: 130%").bold()
@@ -346,7 +377,7 @@ extension AlgorithmAdvancedSettings {
                     shouldDisplayHint: $shouldDisplayHint,
                     hintLabel: hintLabel ?? "",
                     hintText: selectedVerboseHint ?? AnyView(EmptyView()),
-                    sheetTitle: "Help"
+                    sheetTitle: String(localized: "Help", comment: "Help sheet title")
                 )
             }
             .scrollContentBackground(.hidden)

+ 9 - 9
Trio/Sources/Modules/AutosensSettings/View/AutosensSettingsRootView.swift

@@ -105,7 +105,7 @@ extension AutosensSettings {
                         Spacer()
                         Button(
                             action: {
-                                hintLabel = "Autosens"
+                                hintLabel = String(localized: "Autosens")
                                 selectedVerboseHint = AnyView(autosensVerboseHint)
                                 shouldDisplayHint.toggle()
                             },
@@ -134,12 +134,12 @@ extension AutosensSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = NSLocalizedString("Autosens Max", comment: "Autosens Max")
+                            hintLabel = String(localized: "Autosens Max", comment: "Autosens Max")
                         }
                     ),
                     units: state.units,
                     type: .decimal("autosensMax"),
-                    label: NSLocalizedString("Autosens Max", comment: "Autosens Max"),
+                    label: String(localized: "Autosens Max", comment: "Autosens Max"),
                     miniHint: "Upper limit of the Autosens Ratio.",
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
@@ -154,7 +154,7 @@ extension AutosensSettings {
                             "Tip: Increasing this value allows automatic adjustments of basal rates to be higher, ISF to be lower, and CR to be lower."
                         )
                     },
-                    headerText: "Glucose Deviations Algorithm"
+                    headerText: String(localized: "Glucose Deviations Algorithm")
                 )
 
                 SettingInputSection(
@@ -165,12 +165,12 @@ extension AutosensSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = NSLocalizedString("Autosens Min", comment: "Autosens Min")
+                            hintLabel = String(localized: "Autosens Min", comment: "Autosens Min")
                         }
                     ),
                     units: state.units,
                     type: .decimal("autosensMin"),
-                    label: NSLocalizedString("Autosens Min", comment: "Autosens Min"),
+                    label: String(localized: "Autosens Min", comment: "Autosens Min"),
                     miniHint: "Lower limit of the Autosens Ratio.",
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
@@ -195,12 +195,12 @@ extension AutosensSettings {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = NSLocalizedString("Rewind Resets Autosens", comment: "Rewind Resets Autosens")
+                            hintLabel = String(localized: "Rewind Resets Autosens", comment: "Rewind Resets Autosens")
                         }
                     ),
                     units: state.units,
                     type: .boolean,
-                    label: NSLocalizedString("Rewind Resets Autosens", comment: "Rewind Resets Autosens"),
+                    label: String(localized: "Rewind Resets Autosens", comment: "Rewind Resets Autosens"),
                     miniHint: "Pump rewind initiates a reset in Autosens Ratio.",
                     verboseHint: VStack(alignment: .leading, spacing: 5) {
                         Text("Default: ON").bold()
@@ -226,7 +226,7 @@ extension AutosensSettings {
                     shouldDisplayHint: $shouldDisplayHint,
                     hintLabel: hintLabel ?? "",
                     hintText: selectedVerboseHint ?? AnyView(EmptyView()),
-                    sheetTitle: "Help"
+                    sheetTitle: String(localized: "Help", comment: "Help sheet title")
                 )
             }
             .scrollContentBackground(.hidden).background(appState.trioBackgroundColor(for: colorScheme))

+ 0 - 5
Trio/Sources/Modules/BasalProfileEditor/View/BasalProfileEditorRootView.swift

@@ -172,11 +172,6 @@ extension BasalProfileEditor {
             .navigationTitle("Basal Profile")
             .navigationBarTitleDisplayMode(.automatic)
             .toolbar(content: {
-                if state.items.isNotEmpty {
-                    ToolbarItem(placement: .topBarTrailing) {
-                        EditButton()
-                    }
-                }
                 ToolbarItem(placement: .topBarTrailing) {
                     Button(action: { state.add() }) {
                         HStack {

+ 4 - 0
Trio/Sources/Modules/Base/BaseStateModel.swift

@@ -11,6 +11,10 @@ protocol StateModel: ObservableObject {
     func view(for screen: Screen) -> AnyView
 }
 
+protocol CGMStateModel: StateModel {
+    var cgmCurrent: CGMType { get }
+}
+
 class BaseStateModel<Provider>: StateModel, Injectable where Provider: Trio.Provider {
     @Injected() var router: Router!
     @Injected() var settingsManager: SettingsManager!

+ 15 - 15
Trio/Sources/Modules/BolusCalculatorConfig/View/BolusCalculatorConfigRootView.swift

@@ -41,12 +41,12 @@ extension BolusCalculatorConfig {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = "Display Meal Presets"
+                            hintLabel = String(localized: "Display Meal Presets")
                         }
                     ),
                     units: state.units,
                     type: .boolean,
-                    label: "Display Meal Presets",
+                    label: String(localized: "Display Meal Presets"),
                     miniHint: "Allow the creation of saved, preset meals.",
                     verboseHint: VStack(alignment: .leading, spacing: 10) {
                         Text("Default: ON").bold()
@@ -62,13 +62,13 @@ extension BolusCalculatorConfig {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = "Recommended Bolus Percentage"
+                            hintLabel = String(localized: "Recommended Bolus Percentage")
                         }
                     ),
                     units: state.units,
                     type: .decimal("overrideFactor"),
-                    label: "Recommended Bolus Percentage",
-                    miniHint: "Percentage of bolus suggested in bolus calculator.",
+                    label: String(localized: "Recommended Bolus Percentage"),
+                    miniHint: String(localized: "Percentage of bolus suggested in bolus calculator."),
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: 80%").bold()
@@ -82,7 +82,7 @@ extension BolusCalculatorConfig {
                             "Tip: If you are a new Trio user, it is not advised to set this to 100% until you have verified that your core settings (basal rates, ISF, and CR) do not need adjusting."
                         )
                     },
-                    headerText: "Calculator Configuration"
+                    headerText: String(localized: "Calculator Configuration")
                 )
 
                 SettingInputSection(
@@ -93,14 +93,14 @@ extension BolusCalculatorConfig {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = "Fatty Meal"
+                            hintLabel = String(localized: "Fatty Meal")
                         }
                     ),
                     units: state.units,
                     type: .conditionalDecimal("fattyMealFactor"),
-                    label: "Enable Fatty Meal Option",
-                    conditionalLabel: "Fatty Meal Bolus Percentage",
-                    miniHint: "Add and set a bolus option for meals that absorb slowly.",
+                    label: String(localized: "Enable Fatty Meal Option"),
+                    conditionalLabel: String(localized: "Fatty Meal Bolus Percentage"),
+                    miniHint: String(localized: "Add and set a bolus option for meals that absorb slowly."),
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: OFF").bold()
@@ -127,14 +127,14 @@ extension BolusCalculatorConfig {
                         get: { selectedVerboseHint },
                         set: {
                             selectedVerboseHint = $0.map { AnyView($0) }
-                            hintLabel = "Super Bolus"
+                            hintLabel = String(localized: "Super Bolus")
                         }
                     ),
                     units: state.units,
                     type: .conditionalDecimal("sweetMealFactor"),
-                    label: "Enable Super Bolus Option",
-                    conditionalLabel: "Super Bolus Percentage",
-                    miniHint: "Add and set a bolus option for meals that absorb quickly.",
+                    label: String(localized: "Enable Super Bolus Option"),
+                    conditionalLabel: String(localized: "Super Bolus Percentage"),
+                    miniHint: String(localized: "Add and set a bolus option for meals that absorb quickly."),
                     verboseHint:
                     VStack(alignment: .leading, spacing: 10) {
                         Text("Default: OFF").bold()
@@ -160,7 +160,7 @@ extension BolusCalculatorConfig {
                     shouldDisplayHint: $shouldDisplayHint,
                     hintLabel: hintLabel ?? "",
                     hintText: selectedVerboseHint ?? AnyView(EmptyView()),
-                    sheetTitle: "Help"
+                    sheetTitle: String(localized: "Help", comment: "Help sheet title")
                 )
             }
             .scrollContentBackground(.hidden).background(appState.trioBackgroundColor(for: colorScheme))

+ 0 - 9
Trio/Sources/Modules/CGM/CGMDataFlow.swift

@@ -1,9 +0,0 @@
-enum CGM {
-    enum Config {}
-}
-
-enum cgmConfig {
-    enum Config {}
-}
-
-protocol CGMProvider: Provider {}

+ 0 - 5
Trio/Sources/Modules/CGM/CGMProvider.swift

@@ -1,5 +0,0 @@
-extension CGM {
-    final class Provider: BaseProvider, CGMProvider {
-        @Injected() var apsManager: APSManager!
-    }
-}

+ 0 - 266
Trio/Sources/Modules/CGM/View/CGMRootView.swift

@@ -1,266 +0,0 @@
-import LoopKitUI
-import SwiftUI
-import Swinject
-
-extension CGM {
-    struct RootView: BaseView {
-        let resolver: Resolver
-        let displayClose: Bool
-        @StateObject var state = StateModel()
-        @State private var setupCGM = false
-
-        @State private var shouldDisplayHint: Bool = false
-        @State var hintDetent = PresentationDetent.large
-        @State var selectedVerboseHint: AnyView?
-        @State var hintLabel: String?
-        @State private var decimalPlaceholder: Decimal = 0.0
-        @State private var booleanPlaceholder: Bool = false
-
-        @Environment(\.colorScheme) var colorScheme
-        @Environment(AppState.self) var appState
-
-        var body: some View {
-            NavigationView {
-                List {
-                    Section(
-                        header: Text("CGM Integration to Trio"),
-                        content: {
-                            VStack {
-                                Picker("Type", selection: $state.cgmCurrent) {
-                                    ForEach(state.listOfCGM) { type in
-                                        VStack(alignment: .leading) {
-                                            Text(type.displayName)
-                                            Text(type.subtitle).font(.caption).foregroundColor(.secondary)
-                                        }.tag(type)
-                                    }
-                                }.padding(.top)
-
-                                HStack(alignment: .center) {
-                                    Text(
-                                        "Select your CGM. See hint for compatible devices."
-                                    )
-                                    .font(.footnote)
-                                    .foregroundColor(.secondary)
-                                    .lineLimit(nil)
-                                    Spacer()
-                                    Button(
-                                        action: {
-                                            hintLabel = "Available CGM Types for Trio"
-                                            selectedVerboseHint =
-                                                AnyView(
-                                                    Text(
-                                                        "• Dexcom G5 \n• Dexcom G6 / ONE \n• Dexcom G7 / ONE+ \n• Dexcom Share \n• Freestyle Libre \n• Freestyle Libre Demo \n• Glucose Simulator \n• Medtronic Enlite \n• Nightscout \n• xDrip4iOS"
-                                                    )
-                                                )
-                                            shouldDisplayHint.toggle()
-                                        },
-                                        label: {
-                                            HStack {
-                                                Image(systemName: "questionmark.circle")
-                                            }
-                                        }
-                                    ).buttonStyle(BorderlessButtonStyle())
-                                }.padding(.top)
-                            }.padding(.bottom)
-
-                            if let link = state.cgmCurrent.type.externalLink {
-                                Button {
-                                    UIApplication.shared.open(link, options: [:], completionHandler: nil)
-                                } label: {
-                                    HStack {
-                                        Text("About this source")
-                                        Spacer()
-                                        Image(systemName: "chevron.right")
-                                    }
-                                }
-                                .frame(maxWidth: .infinity, alignment: .leading)
-                            }
-
-                            if state.cgmCurrent.type == .plugin {
-                                Button {
-                                    setupCGM.toggle()
-                                } label: {
-                                    HStack {
-                                        Text("CGM Configuration")
-                                        Spacer()
-                                        Image(systemName: "chevron.right")
-                                    }
-                                }
-                                .frame(maxWidth: .infinity, alignment: .leading)
-                            }
-                        }
-                    ).listRowBackground(Color.chart)
-
-                    if let appURL = state.urlOfApp() {
-                        Section {
-                            Button {
-                                UIApplication.shared.open(appURL, options: [:]) { success in
-                                    if !success {
-                                        self.router.alertMessage
-                                            .send(MessageContent(content: "Unable to open the app", type: .warning))
-                                    }
-                                }
-                            }
-
-                            label: {
-                                Label(state.displayNameOfApp() ?? "-", systemImage: "waveform.path.ecg.rectangle").font(.title3)
-                                    .padding() }
-                                .frame(maxWidth: .infinity, alignment: .center)
-                                .buttonStyle(.bordered)
-                        }
-                        .listRowBackground(Color.clear)
-                    } else if state.cgmCurrent.type == .nightscout {
-                        if let url = state.url {
-                            Section {
-                                Button {
-                                    UIApplication.shared.open(url, options: [:]) { success in
-                                        if !success {
-                                            self.router.alertMessage
-                                                .send(MessageContent(content: "No URL available", type: .warning))
-                                        }
-                                    }
-                                }
-                                label: { Label("Open URL", systemImage: "waveform.path.ecg.rectangle").font(.title3).padding() }
-                                    .frame(maxWidth: .infinity, alignment: .center)
-                                    .buttonStyle(.bordered)
-                            }
-                            .listRowBackground(Color.clear)
-                        } else {
-                            Section {
-                                Button {
-                                    state.showModal(for: .nighscoutConfigDirect)
-                                }
-                                label: {
-                                    Label("Config Nightscout", systemImage: "waveform.path.ecg.rectangle").font(.title3).padding()
-                                }
-                                .frame(maxWidth: .infinity, alignment: .center)
-                                .buttonStyle(.bordered)
-                            }
-                            .listRowBackground(Color.clear)
-                        }
-                    }
-
-                    if state.cgmCurrent.type == .xdrip {
-                        Section(header: Text("Heartbeat")) {
-                            VStack(alignment: .leading) {
-                                if let cgmTransmitterDeviceAddress = state.cgmTransmitterDeviceAddress {
-                                    Text("CGM address :").padding(.top)
-                                    Text(cgmTransmitterDeviceAddress)
-                                } else {
-                                    Text("CGM is not used as heartbeat.").padding(.top)
-                                }
-
-                                HStack(alignment: .center) {
-                                    Text(
-                                        "A heartbeat tells Trio to start a loop cycle. This is required for closed loop."
-                                    )
-                                    .font(.footnote)
-                                    .foregroundColor(.secondary)
-                                    .lineLimit(nil)
-                                    Spacer()
-                                    Button(
-                                        action: {
-                                            hintLabel = "CGM Heartbeat"
-                                            selectedVerboseHint =
-                                                AnyView(
-                                                    Text(
-                                                        "The CGM Heartbeat can come from either a CGM or a pump to wake up Trio when phone is locked or in the background. If CGM is on the same phone as Trio and xDrip4iOS is configured to use the same AppGroup as Trio and the heartbeat feature is turned on in xDrip4iOS, then the CGM can provide a heartbeat to wake up Trio when phone is locked or app is in the background."
-                                                    )
-                                                )
-                                            shouldDisplayHint.toggle()
-                                        },
-                                        label: {
-                                            HStack {
-                                                Image(systemName: "questionmark.circle")
-                                            }
-                                        }
-                                    ).buttonStyle(BorderlessButtonStyle())
-                                }.padding(.vertical)
-                            }
-                        }.listRowBackground(Color.chart)
-                    }
-
-                    if state.cgmCurrent.type == .plugin && state.cgmCurrent.id.contains("Libre") {
-                        Section {
-                            Text("Libre Calibrations").navigationLink(to: .calibrations, from: self)
-                        }.listRowBackground(Color.chart)
-                    }
-
-                    SettingInputSection(
-                        decimalValue: $decimalPlaceholder,
-                        booleanValue: $state.smoothGlucose,
-                        shouldDisplayHint: $shouldDisplayHint,
-                        selectedVerboseHint: Binding(
-                            get: { selectedVerboseHint },
-                            set: {
-                                selectedVerboseHint = $0.map { AnyView($0) }
-                                hintLabel = "Smooth Glucose Value"
-                            }
-                        ),
-                        units: state.units,
-                        type: .boolean,
-                        label: "Smooth Glucose Value",
-                        miniHint: "Smooth CGM readings using Savitzky-Golay filtering.",
-                        verboseHint:
-                        VStack(alignment: .leading, spacing: 10) {
-                            Text("Default: OFF").bold()
-                            Text(
-                                "This filter looks at small groups of nearby readings and fits them to a simple mathematical curve. This process doesn't change the overall pattern of your glucose data but helps smooth out the \"noise\" or irregular fluctuations that could lead to false highs or lows."
-                            )
-                            Text(
-                                "It's designed to keep the important trends in your data while minimizing those small, misleading variations, giving you and Trio a clearer sense of where your blood sugar is really headed. This type of filtering is useful in Trio, as it can help prevent over-corrections based on inaccurate glucose readings. This can help reduce the impact of sudden spikes or dips that might not reflect your true blood glucose levels."
-                            )
-                            Text(
-                                "Note: If enabled, the smoothed values you see in Trio may differ from what is shown in your CGM app."
-                            )
-                        }
-                    )
-                }
-                .scrollContentBackground(.hidden).background(appState.trioBackgroundColor(for: colorScheme))
-                .onAppear(perform: configureView)
-                .navigationTitle("CGM")
-                .navigationBarTitleDisplayMode(.automatic)
-                .navigationBarItems(leading: displayClose ? Button("Close", action: state.hideModal) : nil)
-                .sheet(isPresented: $shouldDisplayHint) {
-                    SettingInputHintView(
-                        hintDetent: $hintDetent,
-                        shouldDisplayHint: $shouldDisplayHint,
-                        hintLabel: hintLabel ?? "",
-                        hintText: selectedVerboseHint ?? AnyView(EmptyView()),
-                        sheetTitle: "Help"
-                    )
-                }
-                .onChange(of: setupCGM) { _, setupCGM in
-                    state.setupCGM = setupCGM
-                }
-                .onChange(of: state.setupCGM) { _, setupCGM in
-                    self.setupCGM = setupCGM
-                }
-                .screenNavigation(self)
-            }
-            .sheet(isPresented: $setupCGM) {
-                if let cgmFetchManager = state.cgmManager,
-                   let cgmManager = cgmFetchManager.cgmManager,
-                   state.cgmCurrent.type == cgmFetchManager.cgmGlucoseSourceType,
-                   state.cgmCurrent.id == cgmFetchManager.cgmGlucosePluginId
-                {
-                    CGMSettingsView(
-                        cgmManager: cgmManager,
-                        bluetoothManager: state.provider.apsManager.bluetoothManager!,
-                        unit: state.settingsManager.settings.units,
-                        completionDelegate: state
-                    )
-                } else {
-                    CGMSetupView(
-                        CGMType: state.cgmCurrent,
-                        bluetoothManager: state.provider.apsManager.bluetoothManager!,
-                        unit: state.settingsManager.settings.units,
-                        completionDelegate: state,
-                        setupDelegate: state,
-                        pluginCGMManager: self.state.pluginCGMManager
-                    )
-                }
-            }
-        }
-    }
-}

+ 5 - 0
Trio/Sources/Modules/CGMSettings/CGMSettingsDataFlow.swift

@@ -0,0 +1,5 @@
+enum CGMSettings {
+    enum Config {}
+}
+
+protocol CGMSettingsProvider: Provider {}

+ 0 - 0
Trio/Sources/Modules/CGMSettings/CGMSettingsProvider.swift


Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików