polscm32 aka Marvout hace 1 año
padre
commit
7a4067c592

+ 53 - 28
FreeAPS/Sources/Modules/Stat/View/ViewElements/BolusStatsView.swift

@@ -115,6 +115,15 @@ struct BolusStatsView: View {
         return "\(start.formatted()) - \(end.formatted())"
     }
 
+    private func isSameTimeUnit(_ date1: Date, _ date2: Date) -> Bool {
+        switch selectedDuration {
+        case .Day:
+            return Calendar.current.isDate(date1, equalTo: date2, toGranularity: .hour)
+        default:
+            return Calendar.current.isDate(date1, inSameDayAs: date2)
+        }
+    }
+
     /// Returns the initial scroll position date based on the selected duration
     private func getInitialScrollPosition() -> Date {
         let calendar = Calendar.current
@@ -204,33 +213,49 @@ struct BolusStatsView: View {
     }
 
     private var chartsView: some View {
-        Chart(bolusStats) { stat in
-            // Total Bolus Bar
-            BarMark(
-                x: .value("Date", stat.date, unit: selectedDuration == .Day ? .hour : .day),
-                y: .value("Amount", stat.manualBolus)
-            )
-            .foregroundStyle(by: .value("Type", "Manual"))
-            .position(by: .value("Type", "Boluses"))
-
-            // Carb Bolus Bar
-            BarMark(
-                x: .value("Date", stat.date, unit: selectedDuration == .Day ? .hour : .day),
-                y: .value("Amount", stat.smb)
-            )
-            .foregroundStyle(by: .value("Type", "SMB"))
-            .position(by: .value("Type", "Boluses"))
+        Chart {
+            ForEach(bolusStats) { stat in
+                // Total Bolus Bar
+                BarMark(
+                    x: .value("Date", stat.date, unit: selectedDuration == .Day ? .hour : .day),
+                    y: .value("Amount", stat.manualBolus)
+                )
+                .foregroundStyle(by: .value("Type", "Manual"))
+                .position(by: .value("Type", "Boluses"))
+                .opacity(
+                    selectedDate.map { date in
+                        isSameTimeUnit(stat.date, date) ? 1 : 0.3
+                    } ?? 1
+                )
 
-            // Correction Bolus Bar
-            BarMark(
-                x: .value("Date", stat.date, unit: selectedDuration == .Day ? .hour : .day),
-                y: .value("Amount", stat.external)
-            )
-            .foregroundStyle(by: .value("Type", "External"))
-            .position(by: .value("Type", "Boluses"))
+                // Carb Bolus Bar
+                BarMark(
+                    x: .value("Date", stat.date, unit: selectedDuration == .Day ? .hour : .day),
+                    y: .value("Amount", stat.smb)
+                )
+                .foregroundStyle(by: .value("Type", "SMB"))
+                .position(by: .value("Type", "Boluses"))
+                .opacity(
+                    selectedDate.map { date in
+                        isSameTimeUnit(stat.date, date) ? 1 : 0.3
+                    } ?? 1
+                )
+                // Correction Bolus Bar
+                BarMark(
+                    x: .value("Date", stat.date, unit: selectedDuration == .Day ? .hour : .day),
+                    y: .value("Amount", stat.external)
+                )
+                .foregroundStyle(by: .value("Type", "External"))
+                .position(by: .value("Type", "Boluses"))
+                .opacity(
+                    selectedDate.map { date in
+                        isSameTimeUnit(stat.date, date) ? 1 : 0.3
+                    } ?? 1
+                )
+            }
 
-            if let selectedDate,
-               let selectedBolus = getBolusForDate(selectedDate)
+            // Selection popover outside of the ForEach loop!
+            if let selectedDate, let selectedBolus = getBolusForDate(selectedDate)
             {
                 RuleMark(
                     x: .value("Selected Date", selectedDate)
@@ -239,7 +264,7 @@ struct BolusStatsView: View {
                 .annotation(
                     position: .top,
                     spacing: 0,
-                    overflowResolution: .init(x: .fit, y: .disabled)
+                    overflowResolution: .init(x: .fit(to: .chart), y: .fit(to: .chart))
                 ) {
                     BolusSelectionPopover(date: selectedDate, bolus: selectedBolus)
                 }
@@ -291,8 +316,8 @@ struct BolusStatsView: View {
                 }
             }
         }
-        .chartXSelection(value: $selectedDate)
         .chartScrollableAxes(.horizontal)
+        .chartXSelection(value: $selectedDate.animation(.easeInOut))
         .chartScrollPosition(x: $scrollPosition)
         .chartScrollTargetBehavior(
             .valueAligned(
@@ -305,7 +330,7 @@ struct BolusStatsView: View {
             )
         )
         .chartXVisibleDomain(length: visibleDomainLength)
-        .frame(height: 200)
+        .frame(height: 250)
     }
 }
 

+ 17 - 15
FreeAPS/Sources/Modules/Stat/View/ViewElements/GlucosePercentileChart.swift

@@ -34,8 +34,8 @@ struct GlucosePercentileChart: View {
                 // TODO: ensure area marks and line mark take color of respective range
 
                 // Statistical view for longer periods
-                // 10-90 percentile area
                 ForEach(hourlyStats, id: \.hour) { stats in
+                    // 10-90 percentile area
                     AreaMark(
                         x: .value("Hour", Calendar.current.dateForChartHour(stats.hour)),
                         yStart: .value("10th Percentile", stats.percentile10),
@@ -43,10 +43,8 @@ struct GlucosePercentileChart: View {
                         series: .value("10-90", "10-90")
                     )
                     .foregroundStyle(.blue.opacity(stats.median > 0 ? 0.2 : 0))
-                }
 
-                // 25-75 percentile area
-                ForEach(hourlyStats, id: \.hour) { stats in
+                    // 25-75 percentile area
                     AreaMark(
                         x: .value("Hour", Calendar.current.dateForChartHour(stats.hour)),
                         yStart: .value("25th Percentile", stats.percentile25),
@@ -54,17 +52,17 @@ struct GlucosePercentileChart: View {
                         series: .value("25-75", "25-75")
                     )
                     .foregroundStyle(.blue.opacity(stats.median > 0 ? 0.3 : 0))
-                }
 
-                // Median line
-                ForEach(hourlyStats.filter { $0.median > 0 }, id: \.hour) { stats in
-                    LineMark(
-                        x: .value("Hour", Calendar.current.dateForChartHour(stats.hour)),
-                        y: .value("Median", stats.median),
-                        series: .value("Median", "Median")
-                    )
-                    .lineStyle(StrokeStyle(lineWidth: 2))
-                    .foregroundStyle(.blue)
+                    // Median line
+                    if stats.median > 0 {
+                        LineMark(
+                            x: .value("Hour", Calendar.current.dateForChartHour(stats.hour)),
+                            y: .value("Median", stats.median),
+                            series: .value("Median", "Median")
+                        )
+                        .lineStyle(StrokeStyle(lineWidth: 2))
+                        .foregroundStyle(.blue)
+                    }
                 }
 
                 // High/Low limit lines
@@ -82,7 +80,11 @@ struct GlucosePercentileChart: View {
                         .annotation(
                             position: .top,
                             spacing: 0,
-                            overflowResolution: .init(x: .fit, y: .disabled)
+//                            overflowResolution: .init(x: .fit, y: .disabled)
+                            overflowResolution: .init(
+                                x: .fit(to: .chart),
+                                y: .fit(to: .chart)
+                            ) // for coherence with the other charts
                         ) {
                             AGPSelectionPopover(
                                 stats: selectedStats,

+ 52 - 27
FreeAPS/Sources/Modules/Stat/View/ViewElements/MealStatsView.swift

@@ -184,6 +184,15 @@ struct MealStatsView: View {
         }
     }
 
+    private func isSameTimeUnit(_ date1: Date, _ date2: Date) -> Bool {
+        switch selectedDuration {
+        case .Day:
+            return Calendar.current.isDate(date1, equalTo: date2, toGranularity: .hour)
+        default:
+            return Calendar.current.isDate(date1, inSameDayAs: date2)
+        }
+    }
+
     var body: some View {
         VStack(alignment: .leading, spacing: 8) {
             statsView
@@ -257,31 +266,47 @@ struct MealStatsView: View {
     }
 
     private var chartsView: some View {
-        Chart(mealStats) { stat in
-            // Carbs Bar (bottom)
-            BarMark(
-                x: .value("Date", stat.date, unit: selectedDuration == .Day ? .hour : .day),
-                y: .value("Amount", stat.carbs)
-            )
-            .foregroundStyle(by: .value("Type", "Carbs"))
-            .position(by: .value("Type", "Macros"))
-
-            // Fat Bar (middle)
-            BarMark(
-                x: .value("Date", stat.date, unit: selectedDuration == .Day ? .hour : .day),
-                y: .value("Amount", stat.fat)
-            )
-            .foregroundStyle(by: .value("Type", "Fat"))
-            .position(by: .value("Type", "Macros"))
-
-            // Protein Bar (top)
-            BarMark(
-                x: .value("Date", stat.date, unit: selectedDuration == .Day ? .hour : .day),
-                y: .value("Amount", stat.protein)
-            )
-            .foregroundStyle(by: .value("Type", "Protein"))
-            .position(by: .value("Type", "Macros"))
+        Chart {
+            ForEach(mealStats) { stat in
+                // Carbs Bar (bottom)
+                BarMark(
+                    x: .value("Date", stat.date, unit: selectedDuration == .Day ? .hour : .day),
+                    y: .value("Amount", stat.carbs)
+                )
+                .foregroundStyle(by: .value("Type", "Carbs"))
+                .position(by: .value("Type", "Macros"))
+                .opacity(
+                    selectedDate.map { date in
+                        isSameTimeUnit(stat.date, date) ? 1 : 0.3
+                    } ?? 1
+                )
+                // Fat Bar (middle)
+                BarMark(
+                    x: .value("Date", stat.date, unit: selectedDuration == .Day ? .hour : .day),
+                    y: .value("Amount", stat.fat)
+                )
+                .foregroundStyle(by: .value("Type", "Fat"))
+                .position(by: .value("Type", "Macros"))
+                .opacity(
+                    selectedDate.map { date in
+                        isSameTimeUnit(stat.date, date) ? 1 : 0.3
+                    } ?? 1
+                )
+                // Protein Bar (top)
+                BarMark(
+                    x: .value("Date", stat.date, unit: selectedDuration == .Day ? .hour : .day),
+                    y: .value("Amount", stat.protein)
+                )
+                .foregroundStyle(by: .value("Type", "Protein"))
+                .position(by: .value("Type", "Macros"))
+                .opacity(
+                    selectedDate.map { date in
+                        isSameTimeUnit(stat.date, date) ? 1 : 0.3
+                    } ?? 1
+                )
+            }
 
+            // Selection popover outside of the ForEach loop!
             if let selectedDate,
                let selectedMeal = getMealForDate(selectedDate)
             {
@@ -292,7 +317,7 @@ struct MealStatsView: View {
                 .annotation(
                     position: .top,
                     spacing: 0,
-                    overflowResolution: .init(x: .fit, y: .disabled)
+                    overflowResolution: .init(x: .fit(to: .chart), y: .fit(to: .chart))
                 ) {
                     MealSelectionPopover(date: selectedDate, meal: selectedMeal)
                 }
@@ -343,8 +368,8 @@ struct MealStatsView: View {
                 }
             }
         }
-        .chartXSelection(value: $selectedDate)
         .chartScrollableAxes(.horizontal)
+        .chartXSelection(value: $selectedDate.animation(.easeInOut))
         .chartScrollPosition(x: $scrollPosition)
         .chartScrollTargetBehavior(
             .valueAligned(
@@ -355,7 +380,7 @@ struct MealStatsView: View {
             )
         )
         .chartXVisibleDomain(length: visibleDomainLength)
-        .frame(height: 200)
+        .frame(height: 250)
     }
 }
 

+ 26 - 9
FreeAPS/Sources/Modules/Stat/View/ViewElements/TDDChart.swift

@@ -125,6 +125,15 @@ struct TDDChartView: View {
         }
     }
 
+    private func isSameTimeUnit(_ date1: Date, _ date2: Date) -> Bool {
+        switch selectedDuration {
+        case .Day:
+            return Calendar.current.isDate(date1, equalTo: date2, toGranularity: .hour)
+        default:
+            return Calendar.current.isDate(date1, inSameDayAs: date2)
+        }
+    }
+
     var body: some View {
         VStack(alignment: .leading, spacing: 8) {
             statsView
@@ -168,13 +177,21 @@ struct TDDChartView: View {
     }
 
     private var chartsView: some View {
-        Chart(tddStats) { stat in
-            BarMark(
-                x: .value("Date", stat.date, unit: selectedDuration == .Day ? .hour : .day),
-                y: .value("Amount", stat.amount)
-            )
-            .foregroundStyle(Color.insulin)
+        Chart {
+            ForEach(tddStats) { stat in
+                BarMark(
+                    x: .value("Date", stat.date, unit: selectedDuration == .Day ? .hour : .day),
+                    y: .value("Amount", stat.amount)
+                )
+                .foregroundStyle(Color.insulin)
+                .opacity(
+                    selectedDate.map { date in
+                        isSameTimeUnit(stat.date, date) ? 1 : 0.3
+                    } ?? 1
+                )
+            }
 
+            // Selection popover outside of the ForEach loop!
             if let selectedDate,
                let selectedTDD = getTDDForDate(selectedDate)
             {
@@ -185,7 +202,7 @@ struct TDDChartView: View {
                 .annotation(
                     position: .top,
                     spacing: 0,
-                    overflowResolution: .init(x: .fit, y: .disabled)
+                    overflowResolution: .init(x: .fit(to: .chart), y: .fit(to: .chart))
                 ) {
                     TDDSelectionPopover(date: selectedDate, tdd: selectedTDD)
                 }
@@ -230,8 +247,8 @@ struct TDDChartView: View {
                 }
             }
         }
-        .chartXSelection(value: $selectedDate)
         .chartScrollableAxes(.horizontal)
+        .chartXSelection(value: $selectedDate.animation(.easeInOut))
         .chartScrollPosition(x: $scrollPosition)
         .chartScrollTargetBehavior(
             .valueAligned(
@@ -242,7 +259,7 @@ struct TDDChartView: View {
             )
         )
         .chartXVisibleDomain(length: visibleDomainLength)
-        .frame(height: 200)
+        .frame(height: 250)
     }
 }