Bladeren bron

carb Ratios

polscm32 1 jaar geleden
bovenliggende
commit
3b1526898d

+ 12 - 0
Trio.xcodeproj/project.pbxproj

@@ -317,6 +317,7 @@
 		BD47FD172D88AAF50043966B /* OnboardingStepViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD47FD162D88AAEF0043966B /* OnboardingStepViews.swift */; };
 		BD47FD192D88AAFE0043966B /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD47FD182D88AAF90043966B /* OnboardingView.swift */; };
 		BD47FD1B2D88AB4F0043966B /* Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD47FD1A2D88AB4A0043966B /* Model.swift */; };
+		BD47FDD72D8B64D20043966B /* CarbRatioStepView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD47FDD62D8B64CC0043966B /* CarbRatioStepView.swift */; };
 		BD4D738D2D15A4080052227B /* TDDStored+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD4D738B2D15A4080052227B /* TDDStored+CoreDataClass.swift */; };
 		BD4D738E2D15A4080052227B /* TDDStored+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD4D738C2D15A4080052227B /* TDDStored+CoreDataProperties.swift */; };
 		BD4D73A22D15A42A0052227B /* TDDStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD4D73A12D15A4220052227B /* TDDStorage.swift */; };
@@ -1045,6 +1046,7 @@
 		BD47FD162D88AAEF0043966B /* OnboardingStepViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingStepViews.swift; sourceTree = "<group>"; };
 		BD47FD182D88AAF90043966B /* OnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingView.swift; sourceTree = "<group>"; };
 		BD47FD1A2D88AB4A0043966B /* Model.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Model.swift; sourceTree = "<group>"; };
+		BD47FDD62D8B64CC0043966B /* CarbRatioStepView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarbRatioStepView.swift; sourceTree = "<group>"; };
 		BD4D738B2D15A4080052227B /* TDDStored+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TDDStored+CoreDataClass.swift"; sourceTree = SOURCE_ROOT; };
 		BD4D738C2D15A4080052227B /* TDDStored+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TDDStored+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
 		BD4D73A12D15A4220052227B /* TDDStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TDDStorage.swift; sourceTree = "<group>"; };
@@ -1752,6 +1754,7 @@
 		3811DE1F25C9D48300A708ED /* View */ = {
 			isa = PBXGroup;
 			children = (
+				BD47FDD52D8B64AE0043966B /* Setup */,
 				BD47FD182D88AAF90043966B /* OnboardingView.swift */,
 				BD47FD162D88AAEF0043966B /* OnboardingStepViews.swift */,
 				3BAD36B12D7CDC1400CC298D /* MainLoadingView.swift */,
@@ -2596,6 +2599,14 @@
 			path = View;
 			sourceTree = "<group>";
 		};
+		BD47FDD52D8B64AE0043966B /* Setup */ = {
+			isa = PBXGroup;
+			children = (
+				BD47FDD62D8B64CC0043966B /* CarbRatioStepView.swift */,
+			);
+			path = Setup;
+			sourceTree = "<group>";
+		};
 		BD793CAD2CE7660C00D669AC /* Overrides */ = {
 			isa = PBXGroup;
 			children = (
@@ -4170,6 +4181,7 @@
 				38E44534274E411700EC9A94 /* Disk+InternalHelpers.swift in Sources */,
 				38A00B2325FC2B55006BC0B0 /* LRUCache.swift in Sources */,
 				DDD163122C4C689900CD525A /* AdjustmentsStateModel.swift in Sources */,
+				BD47FDD72D8B64D20043966B /* CarbRatioStepView.swift in Sources */,
 				3B2F77862D7E52ED005ED9FA /* TDD.swift in Sources */,
 				DD1745132C54169400211FAC /* DevicesView.swift in Sources */,
 				7F7B756BE8543965D9FDF1A2 /* DataTableDataFlow.swift in Sources */,

+ 0 - 117
Trio/Sources/Modules/Main/View/OnboardingStepViews.swift

@@ -365,123 +365,6 @@ struct BasalProfileStepView: View {
     }
 }
 
-/// Carb ratio step view for setting insulin-to-carb ratio.
-struct CarbRatioStepView: View {
-    @State var onboardingData: OnboardingData
-
-    private var formatter: NumberFormatter {
-        let formatter = NumberFormatter()
-        formatter.numberStyle = .decimal
-        formatter.maximumFractionDigits = 1
-        return formatter
-    }
-
-    var body: some View {
-        VStack(alignment: .leading, spacing: 20) {
-            Text("Your carb ratio tells how many grams of carbohydrates one unit of insulin will cover.")
-                .font(.subheadline)
-                .foregroundColor(.secondary)
-
-            VStack(alignment: .leading, spacing: 12) {
-                Text("Carb Ratio")
-                    .font(.headline)
-
-                HStack {
-                    Slider(
-                        value: Binding(
-                            get: { Double(truncating: onboardingData.carbRatio as NSNumber) },
-                            set: { onboardingData.carbRatio = Decimal($0) }
-                        ),
-                        in: 2 ... 30,
-                        step: 0.5
-                    )
-                    .accentColor(.orange)
-
-                    // Display the current value
-                    Text("\(formatter.string(from: onboardingData.carbRatio as NSNumber) ?? "--") g/U")
-                        .frame(width: 80, alignment: .trailing)
-                }
-
-                // Example calculation
-                VStack(alignment: .leading, spacing: 8) {
-                    Text("Example Calculation")
-                        .font(.headline)
-                        .padding(.top)
-
-                    VStack(alignment: .leading, spacing: 4) {
-                        Text("For 45g of carbs, you would need:")
-                            .font(.subheadline)
-
-                        let insulinNeeded = 45 / Double(truncating: onboardingData.carbRatio as NSNumber)
-                        Text(
-                            "45g ÷ \(formatter.string(from: onboardingData.carbRatio as NSNumber) ?? "--") = \(String(format: "%.1f", insulinNeeded)) units of insulin"
-                        )
-                        .font(.system(.body, design: .monospaced))
-                        .foregroundColor(.orange)
-                        .padding(.vertical, 8)
-                        .padding(.horizontal, 12)
-                        .background(Color.orange.opacity(0.1))
-                        .cornerRadius(8)
-                    }
-                    .padding(.vertical, 4)
-
-                    // Information about the carb ratio
-                    VStack(alignment: .leading, spacing: 8) {
-                        Text("What This Means")
-                            .font(.headline)
-                            .padding(.top, 8)
-
-                        VStack(alignment: .leading, spacing: 4) {
-                            Text("• A ratio of 10 g/U means 1 unit of insulin covers 10g of carbs")
-                            Text("• A lower number means you need more insulin for the same amount of carbs")
-                            Text("• A higher number means you need less insulin for the same amount of carbs")
-                        }
-                        .font(.caption)
-                        .foregroundColor(.secondary)
-                    }
-                }
-            }
-
-            // Visualization of carb ratio
-            VStack(alignment: .leading, spacing: 8) {
-                Text("Visual Reference")
-                    .font(.headline)
-                    .padding(.top)
-
-                HStack(spacing: 20) {
-                    VStack {
-                        Image(systemName: "fork.knife")
-                            .font(.system(size: 40))
-                            .foregroundColor(.orange)
-                        Text("\(formatter.string(from: onboardingData.carbRatio as NSNumber) ?? "--")g")
-                            .font(.headline)
-                        Text("Carbs")
-                            .font(.caption)
-                    }
-
-                    Text("=")
-                        .font(.title)
-
-                    VStack {
-                        Image(systemName: "drop.fill")
-                            .font(.system(size: 40))
-                            .foregroundColor(.blue)
-                        Text("1U")
-                            .font(.headline)
-                        Text("Insulin")
-                            .font(.caption)
-                    }
-                }
-                .frame(maxWidth: .infinity)
-                .padding()
-                .background(Color.orange.opacity(0.1))
-                .cornerRadius(12)
-            }
-        }
-        .padding()
-    }
-}
-
 /// Insulin sensitivity step view for setting insulin sensitivity factor.
 struct InsulinSensitivityStepView: View {
     @State var onboardingData: OnboardingData

+ 227 - 0
Trio/Sources/Modules/Main/View/Setup/CarbRatioStepView.swift

@@ -0,0 +1,227 @@
+//
+//  CarbRatioStepView.swift
+//  Trio
+//
+//  Created by Marvin Polscheit on 19.03.25.
+//
+import SwiftUI
+
+/// Carb ratio step view for setting insulin-to-carb ratio.
+struct CarbRatioStepView: View {
+    @State var onboardingData: OnboardingData
+    @State private var showTimeSelector = false
+    @State private var selectedRatioIndex: Int?
+
+    private var formatter: NumberFormatter {
+        let formatter = NumberFormatter()
+        formatter.numberStyle = .decimal
+        formatter.maximumFractionDigits = 1
+        return formatter
+    }
+
+    private var dateFormatter: DateFormatter {
+        let formatter = DateFormatter()
+        formatter.timeZone = TimeZone(secondsFromGMT: 0)
+        formatter.timeStyle = .short
+        return formatter
+    }
+
+    var body: some View {
+        VStack(alignment: .leading, spacing: 20) {
+            Text("Your carb ratio tells how many grams of carbohydrates one unit of insulin will cover.")
+                .font(.subheadline)
+                .foregroundColor(.secondary)
+
+            // Carb ratios list
+            VStack(alignment: .leading, spacing: 10) {
+                Text("Carb Ratios")
+                    .font(.headline)
+
+                if onboardingData.items.isEmpty {
+                    // Add default entry if no items exist
+                    Button("Add Initial Carb Ratio") {
+                        onboardingData.addCarbRatio()
+                    }
+                    .foregroundColor(.orange)
+                    .padding(.vertical, 8)
+                } else {
+                    ForEach(Array(onboardingData.items.enumerated()), id: \.element.id) { index, item in
+                        HStack {
+                            // Time display
+                            Text(
+                                dateFormatter
+                                    .string(from: Date(timeIntervalSince1970: onboardingData.timeValues[item.timeIndex]))
+                            )
+                            .frame(width: 80, alignment: .leading)
+
+                            // Ratio slider
+                            Slider(
+                                value: Binding(
+                                    get: { Double(truncating: onboardingData.rateValues[item.rateIndex] as NSNumber) },
+                                    set: { newValue in
+                                        // Find closest match in rateValues array
+                                        let newIndex = onboardingData.rateValues
+                                            .firstIndex { abs(Double($0) - newValue) < 0.05 } ?? item.rateIndex
+                                        onboardingData.items[index].rateIndex = newIndex
+                                    }
+                                ),
+                                in: Double(truncating: onboardingData.rateValues.first! as NSNumber) ...
+                                    Double(truncating: onboardingData.rateValues.last! as NSNumber),
+                                step: 0.5
+                            )
+                            .accentColor(.orange)
+
+                            // Display the current value
+                            Text("\(formatter.string(from: onboardingData.rateValues[item.rateIndex] as NSNumber) ?? "--") g/U")
+                                .frame(width: 70, alignment: .trailing)
+
+                            // Delete button (not for the first entry at 00:00)
+                            if index > 0 {
+                                Button(action: {
+                                    onboardingData.items.remove(at: index)
+                                }) {
+                                    Image(systemName: "trash")
+                                        .foregroundColor(.red)
+                                }
+                            }
+                        }
+                        .padding(.vertical, 8)
+                        .background(Color.orange.opacity(0.05))
+                        .cornerRadius(8)
+                    }
+                }
+            }
+
+            // Add new carb ratio button
+            if !onboardingData.items.isEmpty && onboardingData.items.count < 24 {
+                Button(action: {
+                    showTimeSelector = true
+                }) {
+                    HStack {
+                        Image(systemName: "plus.circle.fill")
+                        Text("Add Carb Ratio")
+                    }
+                    .foregroundColor(.orange)
+                    .padding(.vertical, 8)
+                }
+            }
+
+            Divider()
+
+            // Example calculation based on first carb ratio
+            if !onboardingData.items.isEmpty {
+                VStack(alignment: .leading, spacing: 8) {
+                    Text("Example Calculation")
+                        .font(.headline)
+                        .padding(.top)
+
+                    VStack(alignment: .leading, spacing: 4) {
+                        Text("For 45g of carbs, you would need:")
+                            .font(.subheadline)
+
+                        let insulinNeeded = 45 /
+                            Double(truncating: onboardingData.rateValues[onboardingData.items.first!.rateIndex] as NSNumber)
+                        Text(
+                            "45g ÷ \(formatter.string(from: onboardingData.rateValues[onboardingData.items.first!.rateIndex] as NSNumber) ?? "--") = \(String(format: "%.1f", insulinNeeded)) units of insulin"
+                        )
+                        .font(.system(.body, design: .monospaced))
+                        .foregroundColor(.orange)
+                        .padding(.vertical, 8)
+                        .padding(.horizontal, 12)
+                        .background(Color.orange.opacity(0.1))
+                        .cornerRadius(8)
+                    }
+                    .padding(.vertical, 4)
+                }
+
+                // Information about the carb ratio
+                VStack(alignment: .leading, spacing: 8) {
+                    Text("What This Means")
+                        .font(.headline)
+                        .padding(.top, 8)
+
+                    VStack(alignment: .leading, spacing: 4) {
+                        Text("• A ratio of 10 g/U means 1 unit of insulin covers 10g of carbs")
+                        Text("• A lower number means you need more insulin for the same amount of carbs")
+                        Text("• A higher number means you need less insulin for the same amount of carbs")
+                        Text("• Different times of day may require different ratios")
+                    }
+                    .font(.caption)
+                    .foregroundColor(.secondary)
+                }
+            }
+
+            // Visualization of carb ratio if we have ratios defined
+            if !onboardingData.items.isEmpty {
+                VStack(alignment: .leading, spacing: 8) {
+                    Text("Visual Reference")
+                        .font(.headline)
+                        .padding(.top)
+
+                    HStack(spacing: 20) {
+                        VStack {
+                            Image(systemName: "fork.knife")
+                                .font(.system(size: 40))
+                                .foregroundColor(.orange)
+                            Text(
+                                "\(formatter.string(from: onboardingData.rateValues[onboardingData.items.first!.rateIndex] as NSNumber) ?? "--")g"
+                            )
+                            .font(.headline)
+                            Text("Carbs")
+                                .font(.caption)
+                        }
+
+                        Text("=")
+                            .font(.title)
+
+                        VStack {
+                            Image(systemName: "drop.fill")
+                                .font(.system(size: 40))
+                                .foregroundColor(.blue)
+                            Text("1U")
+                                .font(.headline)
+                            Text("Insulin")
+                                .font(.caption)
+                        }
+                    }
+                    .frame(maxWidth: .infinity)
+                    .padding()
+                    .background(Color.orange.opacity(0.1))
+                    .cornerRadius(12)
+                }
+            }
+        }
+        .padding()
+        .actionSheet(isPresented: $showTimeSelector) {
+            var buttons: [ActionSheet.Button] = []
+
+            // Find available time slots in 1-hour increments
+            for hour in 0 ..< 24 {
+                let hourInMinutes = hour * 60
+                // Calculate timeIndex for this hour
+                let timeIndex = onboardingData.timeValues.firstIndex { abs($0 - Double(hourInMinutes * 60)) < 10 } ?? 0
+
+                // Check if this hour is already in the profile
+                if !onboardingData.items.contains(where: { $0.timeIndex == timeIndex }) {
+                    buttons.append(.default(Text("\(String(format: "%02d:00", hour))")) {
+                        // Get the current ratio from the last item
+                        let rateIndex = onboardingData.items.last?.rateIndex ?? 0
+                        // Create new item with the specified time
+                        let newItem = CarbRatioEditor.Item(rateIndex: rateIndex, timeIndex: timeIndex)
+                        // Add the new item and sort the list
+                        onboardingData.items.append(newItem)
+                        onboardingData.items.sort(by: { $0.timeIndex < $1.timeIndex })
+                    })
+                }
+            }
+
+            buttons.append(.cancel())
+
+            return ActionSheet(
+                title: Text("Select Start Time"),
+                message: Text("Choose when this carb ratio should start"),
+                buttons: buttons
+            )
+        }
+    }
+}

+ 8 - 10
Trio/Sources/Modules/Onboarding/Model.swift

@@ -105,13 +105,13 @@ enum OnboardingStep: Int, CaseIterable, Identifiable {
 @Observable class OnboardingData: Injectable {
     @ObservationIgnored @Injected() var settingsManager: SettingsManager!
     @ObservationIgnored @Injected() var storage: FileStorage!
-    
+
     // Carb Ratio related
     var items: [CarbRatioEditor.Item] = []
     var initialItems: [CarbRatioEditor.Item] = []
     let timeValues = stride(from: 0.0, to: 1.days.timeInterval, by: 30.minutes.timeInterval).map { $0 }
     let rateValues = stride(from: 30.0, to: 501.0, by: 1.0).map { ($0.decimal ?? .zero) / 10 }
-    
+
     // Glucose Target
     var targetLow: Decimal = 70
     var targetHigh: Decimal = 180
@@ -152,15 +152,12 @@ enum OnboardingStep: Int, CaseIterable, Identifiable {
         // Apply glucose units
         settingsCopy.units = units
 
+        // Apply basal profile
+
         // Apply carb ratio
         saveCarbRatios()
 
-        // Apply ISF (if the property exists in TrioSettings)
-        if let isfValue = Double(exactly: NSDecimalNumber(decimal: isf)) {
-            // Assuming there is a related property for insulin sensitivity factor in TrioSettings
-            // This might need to be adjusted based on the actual property name
-            // settingsCopy.insulinSensitivityFactor = isfValue
-        }
+        // Apply ISF values
 
         // Instead of using updateSettings which doesn't exist,
         // we'll directly set the settings property which will trigger the didSet observer
@@ -169,6 +166,7 @@ enum OnboardingStep: Int, CaseIterable, Identifiable {
 }
 
 // MARK: - Setup Carb Ratios
+
 extension OnboardingData {
     var hasChanges: Bool {
         if initialItems.count != items.count {
@@ -183,7 +181,7 @@ extension OnboardingData {
 
         return false
     }
-    
+
     func addCarbRatio() {
         var time = 0
         var rate = 0
@@ -224,7 +222,7 @@ extension OnboardingData {
 //            }
 //        }
 //    }
-    
+
     func saveProfile(_ profile: CarbRatios) {
         storage.save(profile, as: OpenAPS.Settings.carbRatios)
     }