Просмотр исходного кода

Add Time buttons under main chart (along button to UX/UI settings) (#358)

Configure X-axis (time in hours) from main chart.

Draft which needs testing.
Jon B Mårtensson 2 лет назад
Родитель
Сommit
08f4501f03

+ 4 - 0
Core_Data.xcdatamodeld/Core_Data.xcdatamodel/contents

@@ -163,4 +163,8 @@
         <attribute name="id" optional="YES" attributeType="String" defaultValueString="empy"/>
         <attribute name="isPreset" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
     </entity>
+    <entity name="UXSettings" representedClassName="UXSettings" syncable="YES" codeGenerationType="class">
+        <attribute name="date" optional="YES" attributeType="Date" defaultDateTimeInterval="722095800" usesScalarValueType="NO"/>
+        <attribute name="hours" optional="YES" attributeType="Integer 16" defaultValueString="6" usesScalarValueType="YES"/>
+    </entity>
 </model>

+ 11 - 0
FreeAPS/Sources/APS/Storage/CoreDataStorage.swift

@@ -31,4 +31,15 @@ final class CoreDataStorage {
         }
         return overrideArray
     }
+
+    func fetchSettings() -> [UXSettings] {
+        var settingsArray = [UXSettings]()
+        coredataContext.performAndWait {
+            let requestSetttings = UXSettings.fetchRequest() as NSFetchRequest<UXSettings>
+            let sortSettings = NSSortDescriptor(key: "date", ascending: false)
+            requestSetttings.sortDescriptors = [sortSettings]
+            try? settingsArray = self.coredataContext.fetch(requestSetttings)
+        }
+        return settingsArray
+    }
 }

+ 15 - 0
FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings

@@ -1655,6 +1655,21 @@ Enact a temp Basal or a temp target */
 /* */
 "Hours X-Axis (6 default)" = "Hours X-Axis (6 default)";
 
+/* */
+"2 hours" = "2 hours";
+
+/* */
+"4 hours" = "4 hours";
+
+/* */
+"6 hours" = "6 hours";
+
+/* */
+"12 hours" = "12 hours";
+
+/* */
+"24 hours" = "24 hours";
+
 /* Average BG = */
 "Average" = "Average";
 

+ 15 - 0
FreeAPS/Sources/Localizations/Main/sv.lproj/Localizable.strings

@@ -1652,6 +1652,21 @@ Enact a temp Basal or a temp target */
 /* */
 "Hours X-Axis (6 default)" = "Antal timmar att visa för X-axel (6 standard)";
 
+/* */
+"2 hours" = "2 timmar";
+
+/* */
+"4 hours" = "4 timmar";
+
+/* */
+"6 hours" = "6 timmar";
+
+/* */
+"12 hours" = "12 timmar";
+
+/* */
+"24 hours" = "24 timmar";
+
 /* Average BG = */
 "Average" = "Medelvärde";
 

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

@@ -56,11 +56,11 @@ extension Home {
         @Published var lowGlucose: Decimal = 4 / 0.0555
         @Published var highGlucose: Decimal = 10 / 0.0555
         @Published var overrideUnit: Bool = false
-        @Published var screenHours: Int = 6
         @Published var displayXgridLines: Bool = false
         @Published var displayYgridLines: Bool = false
         @Published var thresholdLines: Bool = false
         @Published var timeZone: TimeZone?
+        @Published var hours: Int16 = 6
 
         let coredataContext = CoreDataStack.shared.persistentContainer.viewContext
 
@@ -95,7 +95,6 @@ extension Home {
             lowGlucose = settingsManager.settings.low
             highGlucose = settingsManager.settings.high
             overrideUnit = settingsManager.settings.overrideHbA1cUnit
-            screenHours = settingsManager.settings.hours
             displayXgridLines = settingsManager.settings.xGridLines
             displayYgridLines = settingsManager.settings.yGridLines
             thresholdLines = settingsManager.settings.rulerMarks
@@ -218,6 +217,15 @@ extension Home {
             }
         }
 
+        func saveSettings() {
+            coredataContext.perform {
+                let settings = UXSettings(context: self.coredataContext)
+                settings.hours = self.hours
+                settings.date = Date.now
+                try? self.coredataContext.save()
+            }
+        }
+
         private func setupGlucose() {
             DispatchQueue.main.async { [weak self] in
                 guard let self = self else { return }
@@ -427,7 +435,6 @@ extension Home.StateModel:
         lowGlucose = settingsManager.settings.low
         highGlucose = settingsManager.settings.high
         overrideUnit = settingsManager.settings.overrideHbA1cUnit
-        screenHours = settingsManager.settings.hours
         displayXgridLines = settingsManager.settings.xGridLines
         displayYgridLines = settingsManager.settings.yGridLines
         thresholdLines = settingsManager.settings.rulerMarks

+ 7 - 3
FreeAPS/Sources/Modules/Home/View/Chart/MainChartView.swift

@@ -72,7 +72,7 @@ struct MainChartView: View {
     @Binding var smooth: Bool
     @Binding var highGlucose: Decimal
     @Binding var lowGlucose: Decimal
-    @Binding var screenHours: Int
+    @Binding var screenHours: Int16
     @Binding var displayXgridLines: Bool
     @Binding var displayYgridLines: Bool
     @Binding var thresholdLines: Bool
@@ -195,6 +195,10 @@ struct MainChartView: View {
                         .onChange(of: tempBasals) { _ in
                             scroll.scrollTo(Config.endID, anchor: .trailing)
                         }
+                        .onChange(of: screenHours) { _ in
+                            scroll.scrollTo(Config.endID, anchor: .trailing)
+                        }
+
                         .onAppear {
                             // add trigger to the end of main queue
                             DispatchQueue.main.async {
@@ -819,8 +823,8 @@ extension MainChartView {
                 path.addLine(to: CGPoint(x: 0, y: Config.basalHeight))
             }
             let adjustForOptionalExtraHours = screenHours > 12 ? screenHours - 12 : 0
-            let endDateTime = dayAgoTime + min(max(screenHours - adjustForOptionalExtraHours, 12), 24).hours
-                .timeInterval + min(max(screenHours - adjustForOptionalExtraHours, 12), 24).hours
+            let endDateTime = dayAgoTime + min(max(Int(screenHours - adjustForOptionalExtraHours), 12), 24).hours
+                .timeInterval + min(max(Int(screenHours - adjustForOptionalExtraHours), 12), 24).hours
                 .timeInterval
             let autotunedBasalPoints = findRegularBasalPoints(
                 timeBegin: dayAgoTime,

+ 87 - 3
FreeAPS/Sources/Modules/Home/View/HomeRootView.swift

@@ -12,6 +12,24 @@ extension Home {
         @State var isStatusPopupPresented = false
         @State var showCancelAlert = false
 
+        struct Buttons: Identifiable {
+            let label: String
+            let number: String
+            var active: Bool
+            let hours: Int16
+            var id: String { label }
+        }
+
+        @State var timeButtons: [Buttons] = [
+            Buttons(label: "2 hours", number: "2", active: false, hours: 2),
+            Buttons(label: "4 hours", number: "4", active: false, hours: 4),
+            Buttons(label: "6 hours", number: "6", active: false, hours: 6),
+            Buttons(label: "12 hours", number: "12", active: false, hours: 12),
+            Buttons(label: "24 hours", number: "24", active: false, hours: 24)
+        ]
+
+        let buttonFont = Font.custom("TimeButtonFont", size: 16)
+
         @Environment(\.managedObjectContext) var moc
         @Environment(\.colorScheme) var colorScheme
 
@@ -37,6 +55,11 @@ extension Home {
             sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)]
         ) var enactedSliderTT: FetchedResults<TempTargetsSlider>
 
+        @FetchRequest(
+            entity: UXSettings.entity(),
+            sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)]
+        ) var fetchedSettings: FetchedResults<UXSettings>
+
         private var numberFormatter: NumberFormatter {
             let formatter = NumberFormatter()
             formatter.numberStyle = .decimal
@@ -330,6 +353,34 @@ extension Home {
             .frame(maxWidth: .infinity, maxHeight: 30)
         }
 
+        var timeInterval: some View {
+            HStack(alignment: .center) {
+                let saveButton = UXSettings(context: moc)
+                ForEach(timeButtons) { button in
+                    Text(button.active ? NSLocalizedString(button.label, comment: "") : button.number).onTapGesture {
+                        let index = timeButtons.firstIndex(where: { $0.label == button.label }) ?? 0
+                        highlightButtons(index, onAppear: false)
+                        saveButton.hours = button.hours
+                        saveButton.date = Date.now
+                        try? moc.save()
+                        state.hours = button.hours
+                    }
+                    .foregroundStyle(button.active ? .primary : .secondary)
+                    .frame(maxHeight: 20).padding(.horizontal)
+                    .background(button.active ? Color(.systemGray5) : .clear, in: .capsule(style: .circular))
+                }
+                Image(systemName: "ellipsis.circle.fill")
+                    .foregroundStyle(.secondary)
+                    .padding(.leading)
+                    .onTapGesture {
+                        state.showModal(for: .statisticsConfig)
+                    }
+            }
+            .font(buttonFont)
+            .padding(.top, 20)
+            .padding(.bottom, 40)
+        }
+
         var legendPanel: some View {
             ZStack {
                 HStack(alignment: .center) {
@@ -404,13 +455,13 @@ extension Home {
                     smooth: $state.smooth,
                     highGlucose: $state.highGlucose,
                     lowGlucose: $state.lowGlucose,
-                    screenHours: $state.screenHours,
+                    screenHours: $state.hours,
                     displayXgridLines: $state.displayXgridLines,
                     displayYgridLines: $state.displayYgridLines,
                     thresholdLines: $state.thresholdLines
                 )
             }
-            .padding(.bottom)
+            // .padding(.bottom)
             .modal(for: .dataTable, from: self)
         }
 
@@ -479,6 +530,31 @@ extension Home {
             return (name: profileString, isOn: display)
         }
 
+        func highlightButtons(_ int: Int?, onAppear: Bool) {
+            var index = 0
+            if let integer = int, !onAppear {
+                repeat {
+                    if index == integer {
+                        timeButtons[index].active = true
+                    } else {
+                        timeButtons[index].active = false
+                    }
+                    index += 1
+                } while index < timeButtons.count
+            } else if onAppear {
+                let i = timeButtons.firstIndex(where: { $0.hours == (fetchedSettings.first?.hours ?? 6) }) ?? 2
+                index = 0
+                repeat {
+                    if index == i {
+                        timeButtons[index].active = true
+                    } else {
+                        timeButtons[index].active = false
+                    }
+                    index += 1
+                } while index < timeButtons.count
+            }
+        }
+
         @ViewBuilder private func bottomPanel(_ geo: GeometryProxy) -> some View {
             ZStack {
                 Rectangle().fill(Color.gray.opacity(0.3)).frame(height: 50 + geo.safeAreaInsets.bottom)
@@ -577,13 +653,18 @@ extension Home {
                     header(geo)
                     infoPanel
                     mainChart
+                    timeInterval
                     legendPanel
                     profiles(geo)
                     bottomPanel(geo)
                 }
                 .edgesIgnoringSafeArea(.vertical)
             }
-            .onAppear(perform: configureView)
+            .onAppear {
+                configureView {
+                    highlightButtons(nil, onAppear: true)
+                }
+            }
             .navigationTitle("Home")
             .navigationBarHidden(true)
             .ignoresSafeArea(.keyboard)
@@ -606,6 +687,9 @@ extension Home {
                             }
                     )
             }
+            .onDisappear {
+                state.saveSettings()
+            }
         }
 
         private var popup: some View {

+ 0 - 8
FreeAPS/Sources/Modules/StatConfig/StatConfigStateModel.swift

@@ -5,7 +5,6 @@ extension StatConfig {
         @Published var overrideHbA1cUnit = false
         @Published var low: Decimal = 4 / 0.0555
         @Published var high: Decimal = 10 / 0.0555
-        @Published var hours: Decimal = 6
         @Published var xGridLines = false
         @Published var yGridLines: Bool = false
         @Published var oneDimensionalGraph = false
@@ -41,13 +40,6 @@ extension StatConfig {
                 guard units == .mmolL else { return $0 }
                 return $0.asMgdL
             })
-
-            subscribeSetting(\.hours, on: $hours.map(Int.init), initial: {
-                let value = max(min($0, 24), 2)
-                hours = Decimal(value)
-            }, map: {
-                $0
-            })
         }
     }
 }

+ 1 - 6
FreeAPS/Sources/Modules/StatConfig/View/StatConfigRootView.swift

@@ -31,12 +31,6 @@ extension StatConfig {
                     Toggle("Display Chart Y - Grid lines", isOn: $state.yGridLines)
                     Toggle("Display Chart Threshold lines for Low and High", isOn: $state.rulerMarks)
                     Toggle("Standing / Laying TIR Chart", isOn: $state.oneDimensionalGraph)
-                    HStack {
-                        Text("Hours X-Axis (6 default)")
-                        Spacer()
-                        DecimalTextField("6", value: $state.hours, formatter: carbsFormatter)
-                        Text("hours").foregroundColor(.secondary)
-                    }
                 } header: { Text("Home Chart settings ") }
 
                 Section {
@@ -64,6 +58,7 @@ extension StatConfig {
             .onAppear(perform: configureView)
             .navigationBarTitle("UI/UX")
             .navigationBarTitleDisplayMode(.automatic)
+            .navigationBarItems(trailing: Button("Close", action: state.hideModal))
         }
     }
 }