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

refactor: Remove forecast lines option from Live Activity, keep cone only

Drops forecastLines and forecastDisplayType from the Live Activity
content state — the per-type line arrays were inflating the payload
and contributing to rendering failures in the widget extension. The
cone (min/max bounds) is the only display mode going forward.

Also fixes chart end date to derive from actual forecast step count
instead of a hardcoded 150-minute ceiling, eliminating the empty
space to the right of the cone.
Magnus Reintz 1 месяц назад
Родитель
Сommit
b97e1dea03

+ 1 - 3
LiveActivity/LiveActivity.swift

@@ -128,9 +128,7 @@ private extension LiveActivityAttributes.ContentState {
             tempTargetTarget: 120,
             widgetItems: LiveActivityAttributes.LiveActivityItem.defaultItems,
             minForecast: [],
-            maxForecast: [],
-            forecastLines: [],
-            forecastDisplayType: "cone"
+            maxForecast: []
         )
 
     // 0 is the widest digit. Use this to get an upper bound on text width.

+ 4 - 39
LiveActivity/Views/LiveActivityChartView.swift

@@ -38,7 +38,7 @@ struct LiveActivityChartView: View {
 
         let isOverrideActive = additionalState.isOverrideActive == true
         let isTempTargetActive = additionalState.isTempTargetActive == true
-        let hasForecast = !additionalState.minForecast.isEmpty || !additionalState.forecastLines.isEmpty
+        let hasForecast = !additionalState.minForecast.isEmpty
 
         let calendar = Calendar.current
         let now = Date()
@@ -47,7 +47,7 @@ struct LiveActivityChartView: View {
         let endDate: Date = {
             let baseEnd = calendar.date(byAdding: .minute, value: isWatchOS ? 5 : 0, to: now) ?? now
             guard hasForecast, let anchorDate = state.date else { return baseEnd }
-            let predictionEnd = anchorDate.addingTimeInterval(TimeInterval(150 * 60)) // 2.5h from determination
+            let predictionEnd = anchorDate.addingTimeInterval(TimeInterval(additionalState.minForecast.count * 300))
             return max(baseEnd, predictionEnd)
         }()
 
@@ -94,11 +94,7 @@ struct LiveActivityChartView: View {
             }
 
             if hasForecast, let anchorDate = state.date {
-                if additionalState.forecastDisplayType == "lines" {
-                    drawForecastLines(anchorDate: anchorDate, isMgdL: isMgdL)
-                } else {
-                    drawForecastCone(anchorDate: anchorDate, isMgdL: isMgdL, maxValue: maxValue)
-                }
+                drawForecastCone(anchorDate: anchorDate, isMgdL: isMgdL, maxValue: maxValue)
             }
 
             drawChart(yAxisRuleMarkMin: yAxisRuleMarkMin, yAxisRuleMarkMax: yAxisRuleMarkMax)
@@ -201,38 +197,7 @@ struct LiveActivityChartView: View {
                 yEnd: .value("Max", coneData[i].yMax)
             )
             .foregroundStyle(Color.blue.opacity(0.5))
-            .interpolationMethod(.catmullRom)
-        }
-    }
-
-    private func drawForecastLines(anchorDate: Date, isMgdL: Bool) -> some ChartContent {
-        // Colors match the main app's chartForegroundStyleScale
-        let colorMap: [String: Color] = [
-            "iob": Color(red: 0.118, green: 0.588, blue: 0.988),
-            "cob": Color.orange,
-            "uam": Color(red: 0.820, green: 0.169, blue: 0.969),
-            "zt": Color(red: 0.443, green: 0.380, blue: 0.937)
-        ]
-
-        // Flatten lines into a single array to avoid nested ForEach in ChartContentBuilder
-        let points: [(series: String, date: Date, value: Decimal)] = additionalState.forecastLines.flatMap { line in
-            line.values.enumerated().map { index, value in
-                let displayValue = isMgdL ? Decimal(value) : Decimal(value).asMmolL
-                return (series: line.type, date: timeForIndex(index, anchorDate: anchorDate), value: displayValue)
-            }
-        }
-
-        let indices = Array(0 ..< points.count)
-        return ForEach(indices, id: \.self) { i in
-            let point = points[i]
-            LineMark(
-                x: .value("Time", point.date),
-                y: .value("Value", point.value),
-                series: .value("Type", point.series)
-            )
-            .foregroundStyle(colorMap[point.series] ?? Color.gray)
-            .lineStyle(.init(lineWidth: 1.5))
-            .interpolationMethod(.catmullRom)
+            .interpolationMethod(.linear)
         }
     }
 

+ 2 - 7
Trio/Sources/Services/LiveActivity/Data/DataManager.swift

@@ -60,18 +60,14 @@ extension LiveActivityManager {
 
             let tddValue = (tddResults.first?["total"] as? NSDecimalNumber)?.decimalValue ?? 0
 
-            // Compute cone bounds and per-type lines from forecast relationships (max 36 values = 3h)
+            // Compute cone bounds from forecast relationships (max 36 values = 3h)
             var allForecastValues = [[Int]]()
-            var forecastLines = [(type: String, values: [Int])]()
 
             if let forecasts = determination.forecasts {
                 for forecast in forecasts.sorted(by: { ($0.type ?? "") < ($1.type ?? "") }) {
                     let values = forecast.forecastValuesArray.prefix(36).map { Int($0.value) }
                     guard !values.isEmpty else { continue }
                     allForecastValues.append(Array(values))
-                    if let type = forecast.type {
-                        forecastLines.append((type: type, values: Array(values)))
-                    }
                 }
             }
 
@@ -91,8 +87,7 @@ extension LiveActivityManager {
                 target: determination.currentTarget?.decimalValue ?? 0,
                 date: determination.deliverAt,
                 minForecast: minForecast,
-                maxForecast: maxForecast,
-                forecastLines: forecastLines
+                maxForecast: maxForecast
             )
         }
     }

+ 0 - 1
Trio/Sources/Services/LiveActivity/Data/DeterminationData.swift

@@ -7,5 +7,4 @@ struct DeterminationData {
     let date: Date?
     let minForecast: [Int]
     let maxForecast: [Int]
-    let forecastLines: [(type: String, values: [Int])]
 }

+ 0 - 7
Trio/Sources/Services/LiveActivity/LiveActitiyAttributes.swift

@@ -51,8 +51,6 @@ struct LiveActivityAttributes: ActivityAttributes {
         let widgetItems: [LiveActivityItem]
         let minForecast: [Int]
         let maxForecast: [Int]
-        let forecastLines: [ForecastLine]
-        let forecastDisplayType: String
     }
 
     struct ChartItem: Codable, Hashable {
@@ -60,10 +58,5 @@ struct LiveActivityAttributes: ActivityAttributes {
         let date: Date
     }
 
-    struct ForecastLine: Codable, Hashable {
-        let type: String
-        let values: [Int]
-    }
-
     let startDate: Date
 }

+ 1 - 5
Trio/Sources/Services/LiveActivity/LiveActivityAttributes+Helper.swift

@@ -120,11 +120,7 @@ extension LiveActivityAttributes.ContentState {
             tempTargetTarget: tempTarget?.target ?? 0,
             widgetItems: widgetItems ?? [], // set empty array here to silence compiler; this can never be nil
             minForecast: determination?.minForecast ?? [],
-            maxForecast: determination?.maxForecast ?? [],
-            forecastLines: (determination?.forecastLines ?? []).map {
-                LiveActivityAttributes.ForecastLine(type: $0.type, values: $0.values)
-            },
-            forecastDisplayType: settings.forecastDisplayType.rawValue
+            maxForecast: determination?.maxForecast ?? []
         )
 
         self.init(

+ 1 - 3
Trio/Sources/Services/LiveActivity/LiveActivityManager.swift

@@ -324,9 +324,7 @@ final class LiveActivityData: ObservableObject {
                                 tempTargetTarget: 0,
                                 widgetItems: [],
                                 minForecast: [],
-                                maxForecast: [],
-                                forecastLines: [],
-                                forecastDisplayType: ForecastDisplayType.cone.rawValue
+                                maxForecast: []
                             ),
                             isInitialState: true
                         ),