Преглед изворни кода

refine therapy settings UI in app (carb ratio)

Marvin Polscheit пре 7 месеци
родитељ
комит
5de9c8bd77

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

@@ -23036,6 +23036,7 @@
       }
       }
     },
     },
     "Add an entry by tapping 'Add Ratio +' in the top right-hand corner of the screen." : {
     "Add an entry by tapping 'Add Ratio +' in the top right-hand corner of the screen." : {
+      "extractionState" : "stale",
       "localizations" : {
       "localizations" : {
         "bg" : {
         "bg" : {
           "stringUnit" : {
           "stringUnit" : {
@@ -26493,6 +26494,7 @@
       }
       }
     },
     },
     "Add Ratio" : {
     "Add Ratio" : {
+      "extractionState" : "stale",
       "localizations" : {
       "localizations" : {
         "bg" : {
         "bg" : {
           "stringUnit" : {
           "stringUnit" : {
@@ -54049,6 +54051,7 @@
       }
       }
     },
     },
     "Carb Ratios cover 24 hours. You cannot add more rates. Please remove or adjust existing rates to make space." : {
     "Carb Ratios cover 24 hours. You cannot add more rates. Please remove or adjust existing rates to make space." : {
+      "extractionState" : "stale",
       "localizations" : {
       "localizations" : {
         "bg" : {
         "bg" : {
           "stringUnit" : {
           "stringUnit" : {
@@ -182067,6 +182070,7 @@
       }
       }
     },
     },
     "Set Ratio" : {
     "Set Ratio" : {
+      "extractionState" : "stale",
       "localizations" : {
       "localizations" : {
         "bg" : {
         "bg" : {
           "stringUnit" : {
           "stringUnit" : {
@@ -196806,6 +196810,7 @@
       }
       }
     },
     },
     "Swipe to delete a single entry. Tap on it, to edit its time or rate." : {
     "Swipe to delete a single entry. Tap on it, to edit its time or rate." : {
+      "extractionState" : "stale",
       "localizations" : {
       "localizations" : {
         "bg" : {
         "bg" : {
           "stringUnit" : {
           "stringUnit" : {

+ 20 - 0
Trio/Sources/Modules/CarbRatioEditor/CarbRatioEditorStateModel.swift

@@ -5,6 +5,7 @@ extension CarbRatioEditor {
         @Injected() private var nightscout: NightscoutManager!
         @Injected() private var nightscout: NightscoutManager!
         @Published var items: [Item] = []
         @Published var items: [Item] = []
         @Published var initialItems: [Item] = []
         @Published var initialItems: [Item] = []
+        @Published var therapyItems: [TherapySettingItem] = []
         @Published var shouldDisplaySaving: Bool = false
         @Published var shouldDisplaySaving: Bool = false
 
 
         let timeValues = stride(from: 0.0, to: 1.days.timeInterval, by: 30.minutes.timeInterval).map { $0 }
         let timeValues = stride(from: 0.0, to: 1.days.timeInterval, by: 30.minutes.timeInterval).map { $0 }
@@ -30,6 +31,25 @@ extension CarbRatioEditor {
             return false
             return false
         }
         }
 
 
+        // Convert items to TherapySettingItem format
+        func getTherapyItems() -> [TherapySettingItem] {
+            items.map { item in
+                TherapySettingItem(
+                    time: timeValues[item.timeIndex],
+                    value: rateValues[item.rateIndex]
+                )
+            }
+        }
+
+        // Update items from TherapySettingItem format
+        func updateFromTherapyItems(_ therapyItems: [TherapySettingItem]) {
+            items = therapyItems.map { therapyItem in
+                let timeIndex = timeValues.firstIndex(where: { abs($0 - therapyItem.time) < 1 }) ?? 0
+                let rateIndex = rateValues.firstIndex(of: therapyItem.value) ?? 0
+                return Item(rateIndex: rateIndex, timeIndex: timeIndex)
+            }
+        }
+
         override func subscribe() {
         override func subscribe() {
             items = provider.profile.schedule.map { value in
             items = provider.profile.schedule.map { value in
                 let timeIndex = timeValues.firstIndex(of: Double(value.offset * 60)) ?? 0
                 let timeIndex = timeValues.firstIndex(of: Double(value.offset * 60)) ?? 0

+ 74 - 125
Trio/Sources/Modules/CarbRatioEditor/View/CarbRatioEditorRootView.swift

@@ -6,21 +6,17 @@ extension CarbRatioEditor {
     struct RootView: BaseView {
     struct RootView: BaseView {
         let resolver: Resolver
         let resolver: Resolver
         @StateObject var state = StateModel()
         @StateObject var state = StateModel()
-        @State private var editMode = EditMode.inactive
+        @State private var refreshUI = UUID()
+        @State private var now = Date()
+        @Namespace private var bottomID
 
 
         @Environment(\.colorScheme) var colorScheme
         @Environment(\.colorScheme) var colorScheme
         @Environment(AppState.self) var appState
         @Environment(AppState.self) var appState
 
 
-        private var dateFormatter: DateFormatter {
-            let formatter = DateFormatter()
-            formatter.timeZone = TimeZone(secondsFromGMT: 0)
-            formatter.timeStyle = .short
-            return formatter
-        }
-
-        private var rateFormatter: NumberFormatter {
+        private var formatter: NumberFormatter {
             let formatter = NumberFormatter()
             let formatter = NumberFormatter()
             formatter.numberStyle = .decimal
             formatter.numberStyle = .decimal
+            formatter.maximumFractionDigits = 1
             return formatter
             return formatter
         }
         }
 
 
@@ -69,130 +65,88 @@ extension CarbRatioEditor {
         }
         }
 
 
         var body: some View {
         var body: some View {
-            Form {
-                if !state.canAdd {
-                    Section {
-                        VStack(alignment: .leading) {
-                            Text(
-                                "Carb Ratios cover 24 hours. You cannot add more rates. Please remove or adjust existing rates to make space."
-                            ).bold()
-                        }
-                    }.listRowBackground(Color.tabBar)
-                }
-
-                Section(header: Text("Schedule")) {
-                    list
-                }.listRowBackground(Color.chart)
+            ScrollViewReader { proxy in
+                VStack(spacing: 0) {
+                    ScrollView {
+                        LazyVStack {
+                            VStack(alignment: .leading, spacing: 0) {
+                                // Chart visualization
+                                if !state.items.isEmpty {
+                                    VStack(alignment: .leading) {
+                                        carbRatioChart
+                                            .frame(height: 180)
+                                            .padding(.horizontal)
+                                    }
+                                    .padding(.vertical)
+                                    .background(Color.chart.opacity(0.65))
+                                    .clipShape(
+                                        .rect(
+                                            topLeadingRadius: 10,
+                                            bottomLeadingRadius: 0,
+                                            bottomTrailingRadius: 0,
+                                            topTrailingRadius: 10
+                                        )
+                                    )
+                                    .padding(.horizontal)
+                                    .padding(.top)
+                                }
 
 
-                Section {} header: {
-                    VStack(alignment: .leading, spacing: 10) {
-                        HStack {
-                            Image(systemName: "note.text.badge.plus").foregroundStyle(.primary)
-                            Text("Add an entry by tapping 'Add Ratio +' in the top right-hand corner of the screen.")
-                        }
-                        HStack {
-                            Image(systemName: "hand.draw.fill").foregroundStyle(.primary)
-                            Text("Swipe to delete a single entry. Tap on it, to edit its time or rate.")
+                                // Carb ratio list
+                                TherapySettingEditorView(
+                                    items: $state.therapyItems,
+                                    unit: .gramPerUnit,
+                                    timeOptions: state.timeValues,
+                                    valueOptions: state.rateValues,
+                                    validateOnDelete: state.validate,
+                                    onItemAdded: {
+                                        withAnimation {
+                                            proxy.scrollTo(bottomID, anchor: .bottom)
+                                        }
+                                    }
+                                )
+                                .padding(.horizontal)
+                                .id(bottomID)
+                            }
                         }
                         }
                     }
                     }
-                    .textCase(nil)
+
+                    saveButton
                 }
                 }
-            }
-            .safeAreaInset(edge: .bottom, spacing: 30) { saveButton }
-            .scrollContentBackground(.hidden).background(appState.trioBackgroundColor(for: colorScheme))
-            .onAppear(perform: configureView)
-            .navigationTitle("Carb Ratios")
-            .navigationBarTitleDisplayMode(.automatic)
-            .toolbar(content: {
-                ToolbarItem(placement: .topBarTrailing) {
-                    Button(action: { state.add() }) {
-                        HStack {
-                            Text("Add Ratio")
-                            Image(systemName: "plus")
-                        }
-                    }.disabled(!state.canAdd)
+                .background(appState.trioBackgroundColor(for: colorScheme))
+                .onAppear(perform: configureView)
+                .navigationTitle("Carb Ratios")
+                .navigationBarTitleDisplayMode(.automatic)
+                .onAppear {
+                    state.validate()
+                    state.therapyItems = state.getTherapyItems()
                 }
                 }
-            })
-            .environment(\.editMode, $editMode)
-            .onAppear {
-                state.validate()
-            }
-        }
-
-        private func pickers(for index: Int) -> some View {
-            Form {
-                Section {
-                    Picker(selection: $state.items[index].rateIndex, label: Text("Ratio")) {
-                        ForEach(0 ..< state.rateValues.count, id: \.self) { i in
-                            Text(
-                                (self.rateFormatter.string(from: state.rateValues[i] as NSNumber) ?? "") + " " +
-                                    String(localized: "g/U")
-                            ).tag(i)
-                        }
-                    }
-                }.listRowBackground(Color.chart)
-
-                Section {
-                    Picker(selection: $state.items[index].timeIndex, label: Text("Time")) {
-                        ForEach(0 ..< state.timeValues.count, id: \.self) { i in
-                            Text(
-                                self.dateFormatter
-                                    .string(from: Date(
-                                        timeIntervalSince1970: state
-                                            .timeValues[i]
-                                    ))
-                            ).tag(i)
-                        }
-                    }
-
-                }.listRowBackground(Color.chart)
-            }
-            .padding(.top)
-            .scrollContentBackground(.hidden).background(appState.trioBackgroundColor(for: colorScheme))
-            .navigationTitle("Set Ratio")
-            .navigationBarTitleDisplayMode(.automatic)
-        }
-
-        private var list: some View {
-            List {
-                chart.padding(.vertical)
-                ForEach(state.items.indexed(), id: \.1.id) { index, item in
-                    NavigationLink(destination: pickers(for: index)) {
-                        HStack {
-                            Text("Ratio").foregroundColor(.secondary)
-                            Text(
-                                (rateFormatter.string(from: state.rateValues[item.rateIndex] as NSNumber) ?? "0") + " " +
-                                    String(localized: "g/U")
-                            )
-                            Spacer()
-                            Text("starts at").foregroundColor(.secondary)
-                            Text(
-                                "\(dateFormatter.string(from: Date(timeIntervalSince1970: state.timeValues[item.timeIndex])))"
-                            )
-                        }
-                    }
-                    .moveDisabled(true)
+                .onChange(of: state.therapyItems) { _, newItems in
+                    state.updateFromTherapyItems(newItems)
+                    refreshUI = UUID()
                 }
                 }
-                .onDelete(perform: onDelete)
             }
             }
         }
         }
 
 
-        var now = Date()
-        var chart: some View {
+        // Chart for visualizing carb ratios
+        private var carbRatioChart: some View {
             Chart {
             Chart {
-                ForEach(state.items.indexed(), id: \.1.id) { index, item in
+                ForEach(Array(state.items.enumerated()), id: \.element.id) { index, item in
                     let displayValue = state.rateValues[item.rateIndex]
                     let displayValue = state.rateValues[item.rateIndex]
 
 
                     let startDate = Calendar.current
                     let startDate = Calendar.current
                         .startOfDay(for: now)
                         .startOfDay(for: now)
                         .addingTimeInterval(state.timeValues[item.timeIndex])
                         .addingTimeInterval(state.timeValues[item.timeIndex])
-                    let endDate = state.items
-                        .count > index + 1 ?
-                        Calendar.current.startOfDay(for: now)
-                        .addingTimeInterval(state.timeValues[state.items[index + 1].timeIndex])
-                        :
-                        Calendar.current.startOfDay(for: now)
-                        .addingTimeInterval(state.timeValues.last! + 30 * 60)
+
+                    var offset: TimeInterval {
+                        if state.items.count > index + 1 {
+                            return state.timeValues[state.items[index + 1].timeIndex]
+                        } else {
+                            return state.timeValues.last! + 30 * 60
+                        }
+                    }
+
+                    let endDate = Calendar.current.startOfDay(for: now).addingTimeInterval(offset)
+
                     RectangleMark(
                     RectangleMark(
                         xStart: .value("start", startDate),
                         xStart: .value("start", startDate),
                         xEnd: .value("end", endDate),
                         xEnd: .value("end", endDate),
@@ -216,6 +170,7 @@ extension CarbRatioEditor {
                         .lineStyle(.init(lineWidth: 1)).foregroundStyle(Color.orange)
                         .lineStyle(.init(lineWidth: 1)).foregroundStyle(Color.orange)
                 }
                 }
             }
             }
+            .id(refreshUI) // Force chart update
             .chartXAxis {
             .chartXAxis {
                 AxisMarks(values: .automatic(desiredCount: 6)) { _ in
                 AxisMarks(values: .automatic(desiredCount: 6)) { _ in
                     AxisValueLabel(format: .dateTime.hour())
                     AxisValueLabel(format: .dateTime.hour())
@@ -223,8 +178,7 @@ extension CarbRatioEditor {
                 }
                 }
             }
             }
             .chartXScale(
             .chartXScale(
-                domain: Calendar.current.startOfDay(for: now) ... Calendar
-                    .current.startOfDay(for: now)
+                domain: Calendar.current.startOfDay(for: now) ... Calendar.current.startOfDay(for: now)
                     .addingTimeInterval(60 * 60 * 24)
                     .addingTimeInterval(60 * 60 * 24)
             )
             )
             .chartYAxis {
             .chartYAxis {
@@ -234,10 +188,5 @@ extension CarbRatioEditor {
                 }
                 }
             }
             }
         }
         }
-
-        private func onDelete(offsets: IndexSet) {
-            state.items.remove(atOffsets: offsets)
-            state.validate()
-        }
     }
     }
 }
 }