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

* scroll view fixes
* increase width of basal rulemark
* minor UI fixes
* ensure that predictions cannot be lower than 0 on chart
* small UI fixes
* code simplification
* add comment and rename mainchart file

polscm32 2 лет назад
Родитель
Сommit
4c3439ee0a

+ 6 - 6
FreeAPS.xcodeproj/project.pbxproj

@@ -318,7 +318,7 @@
 		BD188BED2B1B805B00B183BF /* WidgetBobble.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD188BEB2B1B805A00B183BF /* WidgetBobble.swift */; };
 		BD2B464E0745FBE7B79913F4 /* NightscoutConfigProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BF768BD6264FF7D71D66767 /* NightscoutConfigProvider.swift */; };
 		BD2FF1A02AE29D43005D1C5D /* CheckboxToggleStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD2FF19F2AE29D43005D1C5D /* CheckboxToggleStyle.swift */; };
-		BD3CC0722B0B89D50013189E /* MainChartView2.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD3CC0712B0B89D50013189E /* MainChartView2.swift */; };
+		BD3CC0722B0B89D50013189E /* MainChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD3CC0712B0B89D50013189E /* MainChartView.swift */; };
 		BD7DA9A52AE06DFC00601B20 /* BolusCalculatorConfigDataFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD7DA9A42AE06DFC00601B20 /* BolusCalculatorConfigDataFlow.swift */; };
 		BD7DA9A72AE06E2B00601B20 /* BolusCalculatorConfigProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD7DA9A62AE06E2B00601B20 /* BolusCalculatorConfigProvider.swift */; };
 		BD7DA9A92AE06E9200601B20 /* BolusCalculatorStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD7DA9A82AE06E9200601B20 /* BolusCalculatorStateModel.swift */; };
@@ -878,7 +878,7 @@
 		BC210C0F3CB6D3C86E5DED4E /* LibreConfigRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = LibreConfigRootView.swift; sourceTree = "<group>"; };
 		BD188BEB2B1B805A00B183BF /* WidgetBobble.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetBobble.swift; sourceTree = "<group>"; };
 		BD2FF19F2AE29D43005D1C5D /* CheckboxToggleStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxToggleStyle.swift; sourceTree = "<group>"; };
-		BD3CC0712B0B89D50013189E /* MainChartView2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainChartView2.swift; sourceTree = "<group>"; };
+		BD3CC0712B0B89D50013189E /* MainChartView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainChartView.swift; sourceTree = "<group>"; };
 		BD7DA9A42AE06DFC00601B20 /* BolusCalculatorConfigDataFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BolusCalculatorConfigDataFlow.swift; sourceTree = "<group>"; };
 		BD7DA9A62AE06E2B00601B20 /* BolusCalculatorConfigProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BolusCalculatorConfigProvider.swift; sourceTree = "<group>"; };
 		BD7DA9A82AE06E9200601B20 /* BolusCalculatorStateModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BolusCalculatorStateModel.swift; sourceTree = "<group>"; };
@@ -1565,7 +1565,7 @@
 		3833B51E260264AC003021B3 /* Chart */ = {
 			isa = PBXGroup;
 			children = (
-				BD3CC0712B0B89D50013189E /* MainChartView2.swift */,
+				BD3CC0712B0B89D50013189E /* MainChartView.swift */,
 			);
 			path = Chart;
 			sourceTree = "<group>";
@@ -2700,7 +2700,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				3811DE2325C9D48300A708ED /* MainDataFlow.swift in Sources */,
-				BD3CC0722B0B89D50013189E /* MainChartView2.swift in Sources */,
+				BD3CC0722B0B89D50013189E /* MainChartView.swift in Sources */,
 				3811DEEB25CA063400A708ED /* PersistedProperty.swift in Sources */,
 				38E44537274E411700EC9A94 /* Disk+Helpers.swift in Sources */,
 				388E5A6025B6F2310019842D /* Autosens.swift in Sources */,
@@ -3297,7 +3297,7 @@
 					"$(PROJECT_DIR)/Dependencies/ios-armv7_arm64",
 				);
 				INFOPLIST_FILE = FreeAPS/Resources/Info.plist;
-				IPHONEOS_DEPLOYMENT_TARGET = 16.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 17.0;
 				LD_RUNPATH_SEARCH_PATHS = (
 					"$(inherited)",
 					"@executable_path/Frameworks",
@@ -3339,7 +3339,7 @@
 					"$(PROJECT_DIR)/Dependencies/ios-armv7_arm64",
 				);
 				INFOPLIST_FILE = FreeAPS/Resources/Info.plist;
-				IPHONEOS_DEPLOYMENT_TARGET = 16.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 17.0;
 				LD_RUNPATH_SEARCH_PATHS = (
 					"$(inherited)",
 					"@executable_path/Frameworks",

+ 1 - 1
FreeAPS.xcworkspace/xcshareddata/swiftpm/Package.resolved

@@ -30,7 +30,7 @@
       },
       {
         "package": "SwiftCharts",
-        "repositoryURL": "https://github.com/ivanschuetz/SwiftCharts.git",
+        "repositoryURL": "https://github.com/ivanschuetz/SwiftCharts",
         "state": {
           "branch": "master",
           "revision": "c354c1945bb35a1f01b665b22474f6db28cba4a2",

+ 68 - 79
FreeAPS/Sources/Modules/Home/View/Chart/MainChartView2.swift

@@ -50,7 +50,7 @@ private enum PredictionType: Hashable {
     case uam
 }
 
-struct MainChartView2: View {
+struct MainChartView: View {
     private enum Config {
         static let bolusSize: CGFloat = 5
         static let bolusScale: CGFloat = 1
@@ -108,17 +108,8 @@ struct MainChartView2: View {
         return formatter
     }
 
-    private var fpuFormatter: NumberFormatter {
-        let formatter = NumberFormatter()
-        formatter.numberStyle = .decimal
-        formatter.maximumFractionDigits = 1
-        formatter.decimalSeparator = "."
-        formatter.minimumIntegerDigits = 0
-        return formatter
-    }
-
     var body: some View {
-        VStack(alignment: .center, spacing: 8, content: {
+        VStack {
             ScrollViewReader { scroller in
                 ScrollView(.horizontal, showsIndicators: false) {
                     VStack {
@@ -126,37 +117,44 @@ struct MainChartView2: View {
 
                         MainChart()
 
-                    }.onChange(of: screenHours) { _ in
+                    }.onChange(of: screenHours) {
+                        updateStartEndMarkers()
                         scroller.scrollTo("MainChart", anchor: .trailing)
-                    }.onAppear {
+                    }.onChange(of: glucose) {
+                        updateStartEndMarkers()
                         scroller.scrollTo("MainChart", anchor: .trailing)
-                    }.onChange(of: glucose) { _ in
+                    }
+                    .onChange(of: suggestion) {
+                        updateStartEndMarkers()
                         scroller.scrollTo("MainChart", anchor: .trailing)
                     }
-                    .onChange(of: suggestion) { _ in
+                    .onChange(of: tempBasals) {
+                        updateStartEndMarkers()
                         scroller.scrollTo("MainChart", anchor: .trailing)
                     }
-                    .onChange(of: tempBasals) { _ in
+                    .onAppear {
+                        updateStartEndMarkers()
                         scroller.scrollTo("MainChart", anchor: .trailing)
                     }
                 }
             }
-            Legend().padding(.vertical, 4)
-        })
+            //            Legend().padding(.vertical, 4)
+        }
     }
 }
 
 // MARK: Components
 
-extension MainChartView2 {
+extension MainChartView {
     private func MainChart() -> some View {
         VStack {
             Chart {
+                /// high and low treshold lines
                 if thresholdLines {
                     RuleMark(y: .value("High", highGlucose)).foregroundStyle(Color.loopYellow)
-                        .lineStyle(.init(lineWidth: 1, dash: [2]))
+                        .lineStyle(.init(lineWidth: 2, dash: [2]))
                     RuleMark(y: .value("Low", lowGlucose)).foregroundStyle(Color.loopRed)
-                        .lineStyle(.init(lineWidth: 1, dash: [2]))
+                        .lineStyle(.init(lineWidth: 2, dash: [2]))
                 }
                 RuleMark(
                     x: .value(
@@ -164,7 +162,7 @@ extension MainChartView2 {
                         Date(timeIntervalSince1970: TimeInterval(NSDate().timeIntervalSince1970)),
                         unit: .second
                     )
-                ).lineStyle(.init(lineWidth: 1, dash: [2]))
+                ).lineStyle(.init(lineWidth: 2, dash: [2]))
                 RuleMark(
                     x: .value(
                         "",
@@ -179,6 +177,7 @@ extension MainChartView2 {
                         unit: .second
                     )
                 ).foregroundStyle(Color.clear)
+                /// carbs
                 ForEach(ChartCarbs, id: \.self) { carb in
                     let carbAmount = carb.amount
                     PointMark(
@@ -188,20 +187,19 @@ extension MainChartView2 {
                     .symbolSize((Config.carbsSize + CGFloat(carbAmount) * Config.carbsScale) * 10)
                     .foregroundStyle(Color.orange)
                     .annotation(position: .bottom) {
-                        Text(bolusFormatter.string(from: carbAmount as NSNumber)!).font(.caption2).foregroundStyle(Color.orange)
+                        Text(carbsFormatter.string(from: carbAmount as NSNumber)!).font(.caption2).foregroundStyle(Color.orange)
                     }
                 }
+                /// fpus
                 ForEach(ChartFpus, id: \.self) { fpu in
-//                    let fpuAmount = fpu.amount
                     PointMark(
                         x: .value("Time", fpu.timestamp, unit: .second),
                         y: .value("Value", fpu.nearestGlucose.sgv ?? 120)
                     )
-//                    .symbolSize((Config.fpuSize + CGFloat(fpuAmount) * Config.carbsScale) * 10)
                     .symbolSize(22)
                     .foregroundStyle(Color.brown)
                 }
-
+                /// smbs in triangle form
                 ForEach(ChartBoluses, id: \.self) { bolus in
                     let bolusAmount = bolus.amount
                     let size = (Config.bolusSize + CGFloat(bolusAmount) * Config.bolusScale) * 1.8
@@ -218,52 +216,53 @@ extension MainChartView2 {
                         Text(bolusFormatter.string(from: bolusAmount as NSNumber)!).font(.caption2).foregroundStyle(Color.insulin)
                     }
                 }
-
-                ForEach(ChartTempTargets, id: \.self) { tt in
-                    let randomString = UUID().uuidString
-                    LineMark(
-                        x: .value("Time", tt.start),
-                        y: .value("Value", tt.amount),
-                        series: .value("tt", randomString)
-                    )
-                    .foregroundStyle(Color.purple).lineStyle(.init(lineWidth: 8, dash: [2, 3]))
-                    LineMark(
-                        x: .value("Time", tt.end),
-                        y: .value("Value", tt.amount),
-                        series: .value("tt", randomString)
+                /// temp targets
+                ForEach(tempTargets, id: \.self) { tt in
+                    let duration = tt.duration
+                    let end = tt.createdAt.addingTimeInterval(TimeInterval(duration * 60))
+                    RuleMark(
+                        xStart: .value("Start", tt.createdAt),
+                        xEnd: .value("End", end),
+                        y: .value("Value", tt.targetTop ?? 0)
                     )
                     .foregroundStyle(Color.purple).lineStyle(.init(lineWidth: 8, dash: [2, 3]))
                 }
+                /// predictions
                 ForEach(Predictions, id: \.self) { info in
+
+                    /// ensure that there are no values below 0 in the chart
+                    let yValue = max(info.amount, 0)
+
                     if info.type == .uam {
                         LineMark(
                             x: .value("Time", info.timestamp, unit: .second),
-                            y: .value("Value", info.amount),
+                            y: .value("Value", yValue),
                             series: .value("uam", "uam")
                         ).foregroundStyle(Color.uam).symbolSize(16)
                     }
                     if info.type == .cob {
                         LineMark(
                             x: .value("Time", info.timestamp, unit: .second),
-                            y: .value("Value", info.amount),
+                            y: .value("Value", yValue),
                             series: .value("cob", "cob")
                         ).foregroundStyle(Color.orange).symbolSize(16)
                     }
                     if info.type == .iob {
                         LineMark(
                             x: .value("Time", info.timestamp, unit: .second),
-                            y: .value("Value", info.amount),
+                            y: .value("Value", yValue),
                             series: .value("iob", "iob")
                         ).foregroundStyle(Color.insulin).symbolSize(16)
                     }
                     if info.type == .zt {
                         LineMark(
                             x: .value("Time", info.timestamp, unit: .second),
-                            y: .value("Value", info.amount),
+                            y: .value("Value", yValue),
                             series: .value("zt", "zt")
                         ).foregroundStyle(Color.zt).symbolSize(16)
                     }
                 }
+                /// glucose point mark
                 ForEach(glucose) {
                     if let sgv = $0.sgv {
                         PointMark(
@@ -281,23 +280,23 @@ extension MainChartView2 {
                     }
                 }
             }.id("MainChart")
-                .onChange(of: glucose) { _ in
+                .onChange(of: glucose) {
                     calculatePredictions()
                 }
-                .onChange(of: carbs) { _ in
+                .onChange(of: carbs) {
                     calculateCarbs()
                     calculateFpus()
                 }
-                .onChange(of: boluses) { _ in
+                .onChange(of: boluses) {
                     calculateBoluses()
                 }
-                .onChange(of: tempTargets) { _ in
+                .onChange(of: tempTargets) {
                     calculateTTs()
                 }
-                .onChange(of: didAppearTrigger) { _ in
+                .onChange(of: didAppearTrigger) {
                     calculatePredictions()
                     calculateTTs()
-                }.onChange(of: suggestion) { _ in
+                }.onChange(of: suggestion) {
                     calculatePredictions()
                 }
                 .onReceive(
@@ -308,13 +307,13 @@ extension MainChartView2 {
                 }
                 .frame(
                     width: max(0, screenSize.width - 20, fullWidth(viewWidth: screenSize.width)),
-                    height: UIScreen.main.bounds.height / 3.3
+                    height: UIScreen.main.bounds.height / 2.9
                 )
                 .chartXScale(domain: startMarker ... endMarker)
                 .chartXAxis {
                     AxisMarks(values: .stride(by: .hour, count: screenHours == 24 ? 4 : 2)) { _ in
                         if displayXgridLines {
-                            AxisGridLine(stroke: .init(lineWidth: 0.5, dash: [2, 3]))
+                            AxisGridLine(stroke: .init(lineWidth: 0.3, dash: [2, 3]))
                         } else {
                             AxisGridLine(stroke: .init(lineWidth: 0, dash: [2, 3]))
                         }
@@ -322,17 +321,14 @@ extension MainChartView2 {
                     }
                 }
                 .chartYAxis {
-                    AxisMarks(position: .trailing) { value in
+                    AxisMarks { _ in
                         if displayYgridLines {
-                            AxisGridLine(stroke: .init(lineWidth: 0.5, dash: [2, 3]))
+                            AxisGridLine(stroke: .init(lineWidth: 0.3, dash: [2, 3]))
                         } else {
                             AxisGridLine(stroke: .init(lineWidth: 0, dash: [2, 3]))
                         }
-                        if let glucoseValue = value.as(Double.self), glucoseValue > 0 {
-                            AxisTick(length: 4, stroke: .init(lineWidth: 4))
-                                .foregroundStyle(Color.gray)
-                            AxisValueLabel()
-                        }
+                        AxisTick(length: 4, stroke: .init(lineWidth: 4)).foregroundStyle(Color.gray)
+                        AxisValueLabel()
                     }
                 }
         }
@@ -347,7 +343,7 @@ extension MainChartView2 {
                         Date(timeIntervalSince1970: TimeInterval(NSDate().timeIntervalSince1970)),
                         unit: .second
                     )
-                ).lineStyle(.init(lineWidth: 1, dash: [2]))
+                ).lineStyle(.init(lineWidth: 2, dash: [2]))
                 RuleMark(
                     x: .value(
                         "",
@@ -378,25 +374,24 @@ extension MainChartView2 {
                         x: .value("End Date", profile.endDate ?? endMarker),
                         y: .value("Amount", profile.amount),
                         series: .value("profile", "profile")
-                    ).lineStyle(.init(lineWidth: 2, dash: [2, 3]))
+                    ).lineStyle(.init(lineWidth: 2.5, dash: [2, 3]))
                 }
-            }.onChange(of: tempBasals) { _ in
+            }.onChange(of: tempBasals) {
                 calculateBasals()
                 calculateTempBasals()
             }
-            .onChange(of: maxBasal) { _ in
+            .onChange(of: maxBasal) {
                 calculateBasals()
                 calculateTempBasals()
             }
-            .onChange(of: autotunedBasalProfile) { _ in
+            .onChange(of: autotunedBasalProfile) {
                 calculateBasals()
                 calculateTempBasals()
             }
-            .onChange(of: didAppearTrigger) { _ in
+            .onChange(of: didAppearTrigger) {
                 calculateBasals()
                 calculateTempBasals()
-            }.onChange(of: basalProfile) { _ in
-                calculateBasals()
+            }.onChange(of: basalProfile) {
                 calculateTempBasals()
             }
             .frame(
@@ -409,22 +404,10 @@ extension MainChartView2 {
             .chartXAxis(.hidden)
             .chartXAxis {
                 AxisMarks(values: .stride(by: .hour, count: screenHours == 24 ? 4 : 2)) { _ in
-                    if displayXgridLines {
-                        AxisGridLine(stroke: .init(lineWidth: 0.5, dash: [2, 3]))
-                    } else {
-                        AxisGridLine(stroke: .init(lineWidth: 0, dash: [2, 3]))
-                    }
-                    //                     AxisValueLabel(format: .dateTime.hour(.defaultDigits(amPM: .narrow)), anchor: .top)
-                    //                    AxisValueLabel(format: .dateTime.hour())
                 }
             }
             .chartYAxis {
                 AxisMarks(position: .trailing) { _ in
-                    if displayYgridLines {
-                        AxisGridLine(stroke: .init(lineWidth: 0.5, dash: [2, 3]))
-                    } else {
-                        AxisGridLine(stroke: .init(lineWidth: 0, dash: [2, 3]))
-                    }
                     AxisTick(length: 30, stroke: .init(lineWidth: 4))
                         .foregroundStyle(Color.clear)
                 }
@@ -485,7 +468,7 @@ extension MainChartView2 {
 
 // MARK: Calculations
 
-extension MainChartView2 {
+extension MainChartView {
     private func timeToNearestGlucose(time: TimeInterval) -> BloodGlucose {
         var nextIndex = 0
         if glucose.last?.dateString.timeIntervalSince1970 ?? Date().timeIntervalSince1970 < time {
@@ -707,6 +690,12 @@ extension MainChartView2 {
         return basalTruncatedPoints
     }
 
+    ///update start and  end marker to fix scroll update problem with x axis
+    private func updateStartEndMarkers() {
+        startMarker = Date(timeIntervalSince1970: TimeInterval(NSDate().timeIntervalSince1970 - 86400))
+        endMarker = Date(timeIntervalSince1970: TimeInterval(NSDate().timeIntervalSince1970 + 10800))
+    }
+
     private func calculateBasals() {
         let dayAgoTime = Date().addingTimeInterval(-1.days.timeInterval).timeIntervalSince1970
         let firstTempTime = (tempBasals.first?.timestamp ?? Date()).timeIntervalSince1970

+ 16 - 16
FreeAPS/Sources/Modules/Home/View/HomeRootView.swift

@@ -450,7 +450,7 @@ extension Home {
                         .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
                 }
 
-                MainChartView2(
+                MainChartView(
                     glucose: $state.glucose,
                     eventualBG: $state.eventualBG,
                     suggestion: $state.suggestion,
@@ -529,7 +529,7 @@ extension Home {
                     Button { state.showModal(for: .addCarbs(editMode: false, override: false)) }
                     label: {
                         ZStack(alignment: Alignment(horizontal: .trailing, vertical: .bottom)) {
-                            Image("carbs")
+                            Image(systemName: "fork.knife")
                                 .renderingMode(.template)
                                 .resizable()
                                 .frame(width: 24, height: 24)
@@ -544,17 +544,17 @@ extension Home {
                             }
                         }
                     }.buttonStyle(.borderless)
-//                    Spacer()
-//                    Button { state.showModal(for: .addTempTarget) }
-//                    label: {
-//                        Image("target")
-//                            .renderingMode(.template)
-//                            .resizable()
-//                            .frame(width: 24, height: 24)
-//                            .padding(8)
-//                    }
-//                    .foregroundColor(colorIcon)
-//                    .buttonStyle(.borderless)
+                    Spacer()
+                    Button { state.showModal(for: .addTempTarget) }
+                    label: {
+                        Image(systemName: "target")
+                            .renderingMode(.template)
+                            .resizable()
+                            .frame(width: 24, height: 24)
+                            .padding(8)
+                    }
+                    .foregroundColor(colorIcon)
+                    .buttonStyle(.borderless)
                     Spacer()
                     Button {
                         state.showModal(for: .bolus(
@@ -563,7 +563,7 @@ extension Home {
                         ))
                     }
                     label: {
-                        Image("bolus")
+                        Image(systemName: "syringe")
                             .renderingMode(.template)
                             .resizable()
                             .frame(width: 24, height: 24)
@@ -604,7 +604,7 @@ extension Home {
                     Button { state.showModal(for: .statistics)
                     }
                     label: {
-                        Image(systemName: "list.bullet")
+                        Image(systemName: "chart.bar")
                             .renderingMode(.template)
                             .resizable()
                             .frame(width: 24, height: 24)
@@ -615,7 +615,7 @@ extension Home {
                     Spacer()
                     Button { state.showModal(for: .settings) }
                     label: {
-                        Image("settings1")
+                        Image(systemName: "gear")
                             .renderingMode(.template)
                             .resizable()
                             .frame(width: 24, height: 24)