瀏覽代碼

Refactoring

polscm32 1 年之前
父節點
當前提交
8008fb3c3f

+ 8 - 0
FreeAPS.xcodeproj/project.pbxproj

@@ -265,6 +265,8 @@
 		58D08B322C8DF88900AA37D3 /* DummyCharts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58D08B312C8DF88900AA37D3 /* DummyCharts.swift */; };
 		58D08B322C8DF88900AA37D3 /* DummyCharts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58D08B312C8DF88900AA37D3 /* DummyCharts.swift */; };
 		58D08B342C8DF9A700AA37D3 /* CobChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58D08B332C8DF9A700AA37D3 /* CobChart.swift */; };
 		58D08B342C8DF9A700AA37D3 /* CobChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58D08B332C8DF9A700AA37D3 /* CobChart.swift */; };
 		58D08B362C8DFAC600AA37D3 /* IobChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58D08B352C8DFAC600AA37D3 /* IobChart.swift */; };
 		58D08B362C8DFAC600AA37D3 /* IobChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58D08B352C8DFAC600AA37D3 /* IobChart.swift */; };
+		58D08B382C8DFB6000AA37D3 /* BasalChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58D08B372C8DFB6000AA37D3 /* BasalChart.swift */; };
+		58D08B3A2C8DFECD00AA37D3 /* TempTargets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58D08B392C8DFECD00AA37D3 /* TempTargets.swift */; };
 		58F107742BD1A4D000B1A680 /* Determination+helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F107732BD1A4D000B1A680 /* Determination+helper.swift */; };
 		58F107742BD1A4D000B1A680 /* Determination+helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F107732BD1A4D000B1A680 /* Determination+helper.swift */; };
 		5A2325522BFCBF55003518CA /* NightscoutUploadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A2325512BFCBF55003518CA /* NightscoutUploadView.swift */; };
 		5A2325522BFCBF55003518CA /* NightscoutUploadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A2325512BFCBF55003518CA /* NightscoutUploadView.swift */; };
 		5A2325542BFCBF66003518CA /* NightscoutFetchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A2325532BFCBF65003518CA /* NightscoutFetchView.swift */; };
 		5A2325542BFCBF66003518CA /* NightscoutFetchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A2325532BFCBF65003518CA /* NightscoutFetchView.swift */; };
@@ -917,6 +919,8 @@
 		58D08B312C8DF88900AA37D3 /* DummyCharts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DummyCharts.swift; sourceTree = "<group>"; };
 		58D08B312C8DF88900AA37D3 /* DummyCharts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DummyCharts.swift; sourceTree = "<group>"; };
 		58D08B332C8DF9A700AA37D3 /* CobChart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CobChart.swift; sourceTree = "<group>"; };
 		58D08B332C8DF9A700AA37D3 /* CobChart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CobChart.swift; sourceTree = "<group>"; };
 		58D08B352C8DFAC600AA37D3 /* IobChart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IobChart.swift; sourceTree = "<group>"; };
 		58D08B352C8DFAC600AA37D3 /* IobChart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IobChart.swift; sourceTree = "<group>"; };
+		58D08B372C8DFB6000AA37D3 /* BasalChart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BasalChart.swift; sourceTree = "<group>"; };
+		58D08B392C8DFECD00AA37D3 /* TempTargets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TempTargets.swift; sourceTree = "<group>"; };
 		58F107732BD1A4D000B1A680 /* Determination+helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Determination+helper.swift"; sourceTree = "<group>"; };
 		58F107732BD1A4D000B1A680 /* Determination+helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Determination+helper.swift"; sourceTree = "<group>"; };
 		5A2325512BFCBF55003518CA /* NightscoutUploadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutUploadView.swift; sourceTree = "<group>"; };
 		5A2325512BFCBF55003518CA /* NightscoutUploadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutUploadView.swift; sourceTree = "<group>"; };
 		5A2325532BFCBF65003518CA /* NightscoutFetchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutFetchView.swift; sourceTree = "<group>"; };
 		5A2325532BFCBF65003518CA /* NightscoutFetchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutFetchView.swift; sourceTree = "<group>"; };
@@ -1786,6 +1790,8 @@
 				58D08B312C8DF88900AA37D3 /* DummyCharts.swift */,
 				58D08B312C8DF88900AA37D3 /* DummyCharts.swift */,
 				58D08B332C8DF9A700AA37D3 /* CobChart.swift */,
 				58D08B332C8DF9A700AA37D3 /* CobChart.swift */,
 				58D08B352C8DFAC600AA37D3 /* IobChart.swift */,
 				58D08B352C8DFAC600AA37D3 /* IobChart.swift */,
+				58D08B372C8DFB6000AA37D3 /* BasalChart.swift */,
+				58D08B392C8DFECD00AA37D3 /* TempTargets.swift */,
 			);
 			);
 			path = Chart;
 			path = Chart;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
@@ -3302,6 +3308,7 @@
 				DD57C4C62C4C7103001A5B28 /* CarbEntryStored+CoreDataClass.swift in Sources */,
 				DD57C4C62C4C7103001A5B28 /* CarbEntryStored+CoreDataClass.swift in Sources */,
 				DD57C4C72C4C7103001A5B28 /* CarbEntryStored+CoreDataProperties.swift in Sources */,
 				DD57C4C72C4C7103001A5B28 /* CarbEntryStored+CoreDataProperties.swift in Sources */,
 				DD57C4C82C4C7103001A5B28 /* OpenAPS_Battery+CoreDataClass.swift in Sources */,
 				DD57C4C82C4C7103001A5B28 /* OpenAPS_Battery+CoreDataClass.swift in Sources */,
+				58D08B382C8DFB6000AA37D3 /* BasalChart.swift in Sources */,
 				DD57C4C92C4C7103001A5B28 /* OpenAPS_Battery+CoreDataProperties.swift in Sources */,
 				DD57C4C92C4C7103001A5B28 /* OpenAPS_Battery+CoreDataProperties.swift in Sources */,
 				118DF76D2C5ECBC60067FEB7 /* OverridePresetEntity.swift in Sources */,
 				118DF76D2C5ECBC60067FEB7 /* OverridePresetEntity.swift in Sources */,
 				DD57C4CA2C4C7103001A5B28 /* GlucoseStored+CoreDataClass.swift in Sources */,
 				DD57C4CA2C4C7103001A5B28 /* GlucoseStored+CoreDataClass.swift in Sources */,
@@ -3491,6 +3498,7 @@
 				CEE9A65C2BBB41C800EB5194 /* CalibrationService.swift in Sources */,
 				CEE9A65C2BBB41C800EB5194 /* CalibrationService.swift in Sources */,
 				110AEDED2C51A0AE00615CC9 /* ShortcutsConfigProvider.swift in Sources */,
 				110AEDED2C51A0AE00615CC9 /* ShortcutsConfigProvider.swift in Sources */,
 				38E4453D274E411700EC9A94 /* Disk+Errors.swift in Sources */,
 				38E4453D274E411700EC9A94 /* Disk+Errors.swift in Sources */,
+				58D08B3A2C8DFECD00AA37D3 /* TempTargets.swift in Sources */,
 				38E98A2325F52C9300C0CED0 /* Signpost.swift in Sources */,
 				38E98A2325F52C9300C0CED0 /* Signpost.swift in Sources */,
 				CE7CA3542A064973004BE681 /* TempPresetsIntentRequest.swift in Sources */,
 				CE7CA3542A064973004BE681 /* TempPresetsIntentRequest.swift in Sources */,
 				DD6B7CB42C7B71F700B75029 /* ForecastDisplayType.swift in Sources */,
 				DD6B7CB42C7B71F700B75029 /* ForecastDisplayType.swift in Sources */,

+ 239 - 0
FreeAPS/Sources/Modules/Home/View/Chart/BasalChart.swift

@@ -0,0 +1,239 @@
+import Charts
+import Foundation
+import SwiftUI
+
+struct BasalProfile: Hashable {
+    let amount: Double
+    var isOverwritten: Bool
+    let startDate: Date
+    let endDate: Date?
+    init(amount: Double, isOverwritten: Bool, startDate: Date, endDate: Date? = nil) {
+        self.amount = amount
+        self.isOverwritten = isOverwritten
+        self.startDate = startDate
+        self.endDate = endDate
+    }
+}
+
+extension MainChartView {
+    var basalChart: some View {
+        VStack {
+            Chart {
+                drawStartRuleMark()
+                drawEndRuleMark()
+                drawCurrentTimeMarker()
+                drawTempBasals(dummy: false)
+                drawBasalProfile()
+                drawSuspensions()
+            }.onChange(of: state.tempBasals) { _ in
+                calculateBasals()
+            }
+            .onChange(of: state.maxBasal) { _ in
+                calculateBasals()
+            }
+            .onChange(of: state.autotunedBasalProfile) { _ in
+                calculateBasals()
+            }
+            .onChange(of: state.basalProfile) { _ in
+                calculateBasals()
+            }
+            .frame(minHeight: geo.size.height * 0.05)
+            .frame(width: fullWidth(viewWidth: screenSize.width))
+            .chartXScale(domain: startMarker ... endMarker)
+            .chartXAxis { basalChartXAxis }
+            .chartXAxis(.hidden)
+            .chartYAxis(.hidden)
+            .chartPlotStyle { basalChartPlotStyle($0) }
+        }
+    }
+}
+
+// MARK: - Draw functions
+
+extension MainChartView {
+    func drawTempBasals(dummy: Bool) -> some ChartContent {
+        ForEach(prepareTempBasals(), id: \.rate) { basal in
+            if dummy {
+                RectangleMark(
+                    xStart: .value("start", basal.start),
+                    xEnd: .value("end", basal.end),
+                    yStart: .value("rate-start", 0),
+                    yEnd: .value("rate-end", basal.rate)
+                ).foregroundStyle(Color.clear)
+
+                LineMark(x: .value("Start Date", basal.start), y: .value("Amount", basal.rate))
+                    .lineStyle(.init(lineWidth: 1)).foregroundStyle(Color.clear)
+
+                LineMark(x: .value("End Date", basal.end), y: .value("Amount", basal.rate))
+                    .lineStyle(.init(lineWidth: 1)).foregroundStyle(Color.clear)
+            } else {
+                RectangleMark(
+                    xStart: .value("start", basal.start),
+                    xEnd: .value("end", basal.end),
+                    yStart: .value("rate-start", 0),
+                    yEnd: .value("rate-end", basal.rate)
+                ).foregroundStyle(
+                    LinearGradient(
+                        gradient: Gradient(
+                            colors: [
+                                Color.insulin.opacity(0.6),
+                                Color.insulin.opacity(0.1)
+                            ]
+                        ),
+                        startPoint: .top,
+                        endPoint: .bottom
+                    )
+                )
+
+                LineMark(x: .value("Start Date", basal.start), y: .value("Amount", basal.rate))
+                    .lineStyle(.init(lineWidth: 1)).foregroundStyle(Color.insulin)
+
+                LineMark(x: .value("End Date", basal.end), y: .value("Amount", basal.rate))
+                    .lineStyle(.init(lineWidth: 1)).foregroundStyle(Color.insulin)
+            }
+        }
+    }
+
+    func drawBasalProfile() -> some ChartContent {
+        /// dashed profile line
+        ForEach(basalProfiles, id: \.self) { profile in
+            LineMark(
+                x: .value("Start Date", profile.startDate),
+                y: .value("Amount", profile.amount),
+                series: .value("profile", "profile")
+            ).lineStyle(.init(lineWidth: 2, dash: [2, 4])).foregroundStyle(Color.insulin)
+            LineMark(
+                x: .value("End Date", profile.endDate ?? endMarker),
+                y: .value("Amount", profile.amount),
+                series: .value("profile", "profile")
+            ).lineStyle(.init(lineWidth: 2.5, dash: [2, 4])).foregroundStyle(Color.insulin)
+        }
+    }
+
+    func drawSuspensions() -> some ChartContent {
+        let suspensions = state.suspensions
+        return ForEach(suspensions) { suspension in
+            let now = Date()
+
+            if let type = suspension.type, type == EventType.pumpSuspend.rawValue, let suspensionStart = suspension.timestamp {
+                let suspensionEnd = min(
+                    (
+                        suspensions
+                            .first(where: {
+                                $0.timestamp ?? now > suspensionStart && $0.type == EventType.pumpResume.rawValue })?
+                            .timestamp
+                    ) ?? now,
+                    now
+                )
+
+                let basalProfileDuringSuspension = basalProfiles.first(where: { $0.startDate <= suspensionStart })
+                let suspensionMarkHeight = basalProfileDuringSuspension?.amount ?? 1
+
+                RectangleMark(
+                    xStart: .value("start", suspensionStart),
+                    xEnd: .value("end", suspensionEnd),
+                    yStart: .value("suspend-start", 0),
+                    yEnd: .value("suspend-end", suspensionMarkHeight)
+                )
+                .foregroundStyle(Color.loopGray.opacity(colorScheme == .dark ? 0.3 : 0.8))
+            }
+        }
+    }
+}
+
+// MARK: - Calculation
+
+extension MainChartView {
+    func prepareTempBasals() -> [(start: Date, end: Date, rate: Double)] {
+        let now = Date()
+        let tempBasals = state.tempBasals
+
+        return tempBasals.compactMap { temp -> (start: Date, end: Date, rate: Double)? in
+            let duration = temp.tempBasal?.duration ?? 0
+            let timestamp = temp.timestamp ?? Date()
+            let end = min(timestamp + duration.minutes, now)
+            let isInsulinSuspended = state.suspensions.contains { $0.timestamp ?? now >= timestamp && $0.timestamp ?? now <= end }
+
+            let rate = Double(truncating: temp.tempBasal?.rate ?? Decimal.zero as NSDecimalNumber) * (isInsulinSuspended ? 0 : 1)
+
+            // Check if there's a subsequent temp basal to determine the end time
+            guard let nextTemp = state.tempBasals.first(where: { $0.timestamp ?? .distantPast > timestamp }) else {
+                return (timestamp, end, rate)
+            }
+            return (timestamp, nextTemp.timestamp ?? Date(), rate) // end defaults to current time
+        }
+    }
+
+    func findRegularBasalPoints(
+        timeBegin: TimeInterval,
+        timeEnd: TimeInterval,
+        autotuned: Bool
+    ) async -> [BasalProfile] {
+        guard timeBegin < timeEnd else { return [] }
+
+        let beginDate = Date(timeIntervalSince1970: timeBegin)
+        let startOfDay = Calendar.current.startOfDay(for: beginDate)
+        let profile = autotuned ? state.autotunedBasalProfile : state.basalProfile
+        var basalPoints: [BasalProfile] = []
+
+        // Iterate over the next three days, multiplying the time intervals
+        for dayOffset in 0 ..< 3 {
+            let dayTimeOffset = TimeInterval(dayOffset * 24 * 60 * 60) // One Day in seconds
+            for entry in profile {
+                let basalTime = startOfDay.addingTimeInterval(entry.minutes.minutes.timeInterval + dayTimeOffset)
+                let basalTimeInterval = basalTime.timeIntervalSince1970
+
+                // Only append points within the timeBegin and timeEnd range
+                if basalTimeInterval >= timeBegin, basalTimeInterval < timeEnd {
+                    basalPoints.append(BasalProfile(
+                        amount: Double(entry.rate),
+                        isOverwritten: false,
+                        startDate: basalTime
+                    ))
+                }
+            }
+        }
+
+        return basalPoints
+    }
+
+    func calculateBasals() {
+        Task {
+            let dayAgoTime = Date().addingTimeInterval(-1.days.timeInterval).timeIntervalSince1970
+
+            // Get Regular and Autotuned Basal parallel
+            async let getRegularBasalPoints = findRegularBasalPoints(
+                timeBegin: dayAgoTime,
+                timeEnd: endMarker.timeIntervalSince1970,
+                autotuned: false
+            )
+
+            async let getAutotunedBasalPoints = findRegularBasalPoints(
+                timeBegin: dayAgoTime,
+                timeEnd: endMarker.timeIntervalSince1970,
+                autotuned: true
+            )
+
+            let (regularPoints, autotunedBasalPoints) = await (getRegularBasalPoints, getAutotunedBasalPoints)
+
+            var totalBasal = regularPoints + autotunedBasalPoints
+            totalBasal.sort {
+                $0.startDate.timeIntervalSince1970 < $1.startDate.timeIntervalSince1970
+            }
+
+            var basals: [BasalProfile] = []
+            totalBasal.indices.forEach { index in
+                basals.append(BasalProfile(
+                    amount: totalBasal[index].amount,
+                    isOverwritten: totalBasal[index].isOverwritten,
+                    startDate: totalBasal[index].startDate,
+                    endDate: totalBasal.count > index + 1 ? totalBasal[index + 1].startDate : endMarker
+                ))
+            }
+
+            await MainActor.run {
+                basalProfiles = basals
+            }
+        }
+    }
+}

+ 30 - 0
FreeAPS/Sources/Modules/Home/View/Chart/CobChart.swift

@@ -34,4 +34,34 @@ extension MainChartView {
         .chartYAxis { cobChartYAxis }
         .chartYAxis { cobChartYAxis }
         .chartYScale(domain: minValueCobChart ... maxValueCobChart)
         .chartYScale(domain: minValueCobChart ... maxValueCobChart)
     }
     }
+
+    func drawCOB(dummy: Bool) -> some ChartContent {
+        ForEach(state.enactedAndNonEnactedDeterminations) { cob in
+            let amount = Int(cob.cob)
+            let date: Date = cob.deliverAt ?? Date()
+
+            if dummy {
+                LineMark(x: .value("Time", date), y: .value("Value", amount))
+                    .foregroundStyle(Color.clear)
+                AreaMark(x: .value("Time", date), y: .value("Value", amount)).foregroundStyle(
+                    Color.clear
+                )
+            } else {
+                LineMark(x: .value("Time", date), y: .value("Value", amount))
+                    .foregroundStyle(Color.orange.gradient)
+                AreaMark(x: .value("Time", date), y: .value("Value", amount)).foregroundStyle(
+                    LinearGradient(
+                        gradient: Gradient(
+                            colors: [
+                                Color.orange.opacity(0.8),
+                                Color.orange.opacity(0.01)
+                            ]
+                        ),
+                        startPoint: .top,
+                        endPoint: .bottom
+                    )
+                )
+            }
+        }
+    }
 }
 }

+ 24 - 0
FreeAPS/Sources/Modules/Home/View/Chart/IobChart.swift

@@ -36,4 +36,28 @@ extension MainChartView {
             .chartYAxis(.hidden)
             .chartYAxis(.hidden)
         }
         }
     }
     }
+
+    func drawIOB() -> some ChartContent {
+        ForEach(state.enactedAndNonEnactedDeterminations) { iob in
+            let rawAmount = iob.iob?.doubleValue ?? 0
+            let amount: Double = rawAmount > 0 ? rawAmount : rawAmount * 2 // weigh negative iob with factor 2
+            let date: Date = iob.deliverAt ?? Date()
+
+            LineMark(x: .value("Time", date), y: .value("Amount", amount))
+                .foregroundStyle(Color.darkerBlue)
+            AreaMark(x: .value("Time", date), y: .value("Amount", amount))
+                .foregroundStyle(
+                    LinearGradient(
+                        gradient: Gradient(
+                            colors: [
+                                Color.darkerBlue.opacity(0.8),
+                                Color.darkerBlue.opacity(0.01)
+                            ]
+                        ),
+                        startPoint: .top,
+                        endPoint: .bottom
+                    )
+                )
+        }
+    }
 }
 }

+ 47 - 431
FreeAPS/Sources/Modules/Home/View/Chart/MainChartView.swift

@@ -5,35 +5,11 @@ import SwiftUI
 let screenSize: CGRect = UIScreen.main.bounds
 let screenSize: CGRect = UIScreen.main.bounds
 let calendar = Calendar.current
 let calendar = Calendar.current
 
 
-private struct BasalProfile: Hashable {
-    let amount: Double
-    var isOverwritten: Bool
-    let startDate: Date
-    let endDate: Date?
-    init(amount: Double, isOverwritten: Bool, startDate: Date, endDate: Date? = nil) {
-        self.amount = amount
-        self.isOverwritten = isOverwritten
-        self.startDate = startDate
-        self.endDate = endDate
-    }
-}
-
-private struct ChartTempTarget: Hashable {
-    let amount: Decimal
-    let start: Date
-    let end: Date
-}
-
 struct MainChartView: View {
 struct MainChartView: View {
     var geo: GeometryProxy
     var geo: GeometryProxy
     @Binding var units: GlucoseUnits
     @Binding var units: GlucoseUnits
-    @Binding var announcement: [Announcement]
     @Binding var hours: Int
     @Binding var hours: Int
-    @Binding var maxBasal: Decimal
-    @Binding var autotunedBasalProfile: [BasalProfileEntry]
-    @Binding var basalProfile: [BasalProfileEntry]
     @Binding var tempTargets: [TempTarget]
     @Binding var tempTargets: [TempTarget]
-    @Binding var smooth: Bool
     @Binding var highGlucose: Decimal
     @Binding var highGlucose: Decimal
     @Binding var lowGlucose: Decimal
     @Binding var lowGlucose: Decimal
     @Binding var screenHours: Int16
     @Binding var screenHours: Int16
@@ -43,10 +19,8 @@ struct MainChartView: View {
 
 
     @StateObject var state: Home.StateModel
     @StateObject var state: Home.StateModel
 
 
-    @State var didAppearTrigger = false
-    @State private var basalProfiles: [BasalProfile] = []
-    @State private var chartTempTargets: [ChartTempTarget] = []
-    @State private var count: Decimal = 1
+    @State var basalProfiles: [BasalProfile] = []
+    @State var chartTempTargets: [ChartTempTarget] = []
     @State var startMarker =
     @State var startMarker =
         Date(timeIntervalSinceNow: TimeInterval(hours: -24))
         Date(timeIntervalSinceNow: TimeInterval(hours: -24))
     @State var endMarker = Date(timeIntervalSinceNow: TimeInterval(hours: 3))
     @State var endMarker = Date(timeIntervalSinceNow: TimeInterval(hours: 3))
@@ -66,7 +40,7 @@ struct MainChartView: View {
     @Environment(\.colorScheme) var colorScheme
     @Environment(\.colorScheme) var colorScheme
     @Environment(\.calendar) var calendar
     @Environment(\.calendar) var calendar
 
 
-    private var upperLimit: Decimal {
+    var upperLimit: Decimal {
         units == .mgdL ? 400 : 22.2
         units == .mgdL ? 400 : 22.2
     }
     }
 
 
@@ -160,7 +134,7 @@ struct MainChartView: View {
     }
     }
 }
 }
 
 
-// MARK: - Components
+// MARK: - Main Chart with selection Popover
 
 
 extension MainChartView {
 extension MainChartView {
     private var mainChart: some View {
     private var mainChart: some View {
@@ -260,11 +234,6 @@ extension MainChartView {
                     await calculateTTs()
                     await calculateTTs()
                 }
                 }
             }
             }
-            .onChange(of: didAppearTrigger) { _ in
-                Task {
-                    await calculateTTs()
-                }
-            }
             .frame(minHeight: geo.size.height * 0.28)
             .frame(minHeight: geo.size.height * 0.28)
             .frame(width: fullWidth(viewWidth: screenSize.width))
             .frame(width: fullWidth(viewWidth: screenSize.width))
             .chartXScale(domain: startMarker ... endMarker)
             .chartXScale(domain: startMarker ... endMarker)
@@ -326,42 +295,9 @@ extension MainChartView {
             }
             }
         }
         }
     }
     }
-
-    private var basalChart: some View {
-        VStack {
-            Chart {
-                drawStartRuleMark()
-                drawEndRuleMark()
-                drawCurrentTimeMarker()
-                drawTempBasals(dummy: false)
-                drawBasalProfile()
-                drawSuspensions()
-            }.onChange(of: state.tempBasals) { _ in
-                calculateBasals()
-            }
-            .onChange(of: maxBasal) { _ in
-                calculateBasals()
-            }
-            .onChange(of: autotunedBasalProfile) { _ in
-                calculateBasals()
-            }
-            .onChange(of: didAppearTrigger) { _ in
-                calculateBasals()
-            }.onChange(of: basalProfile) { _ in
-                calculateBasals()
-            }
-            .frame(minHeight: geo.size.height * 0.05)
-            .frame(width: fullWidth(viewWidth: screenSize.width))
-            .chartXScale(domain: startMarker ... endMarker)
-            .chartXAxis { basalChartXAxis }
-            .chartXAxis(.hidden)
-            .chartYAxis(.hidden)
-            .chartPlotStyle { basalChartPlotStyle($0) }
-        }
-    }
 }
 }
 
 
-// MARK: - Calculations
+// MARK: - Rule Marks and Charts configurations
 
 
 extension MainChartView {
 extension MainChartView {
     func drawCurrentTimeMarker() -> some ChartContent {
     func drawCurrentTimeMarker() -> some ChartContent {
@@ -374,7 +310,7 @@ extension MainChartView {
         ).lineStyle(.init(lineWidth: 2, dash: [3])).foregroundStyle(Color(.systemGray2))
         ).lineStyle(.init(lineWidth: 2, dash: [3])).foregroundStyle(Color(.systemGray2))
     }
     }
 
 
-    private func drawStartRuleMark() -> some ChartContent {
+    func drawStartRuleMark() -> some ChartContent {
         RuleMark(
         RuleMark(
             x: .value(
             x: .value(
                 "",
                 "",
@@ -384,7 +320,7 @@ extension MainChartView {
         ).foregroundStyle(Color.clear)
         ).foregroundStyle(Color.clear)
     }
     }
 
 
-    private func drawEndRuleMark() -> some ChartContent {
+    func drawEndRuleMark() -> some ChartContent {
         RuleMark(
         RuleMark(
             x: .value(
             x: .value(
                 "",
                 "",
@@ -394,280 +330,73 @@ extension MainChartView {
         ).foregroundStyle(Color.clear)
         ).foregroundStyle(Color.clear)
     }
     }
 
 
-    private func drawTempTargets() -> some ChartContent {
-        /// temp targets
-        ForEach(chartTempTargets, id: \.self) { target in
-            let targetLimited = min(max(target.amount, 0), upperLimit)
-
-            RuleMark(
-                xStart: .value("Start", target.start),
-                xEnd: .value("End", target.end),
-                y: .value("Value", targetLimited)
-            )
-            .foregroundStyle(Color.purple.opacity(0.75)).lineStyle(.init(lineWidth: 8))
-        }
+    func basalChartPlotStyle(_ plotContent: ChartPlotContent) -> some View {
+        plotContent
+            .rotationEffect(.degrees(180))
+            .scaleEffect(x: -1, y: 1)
     }
     }
 
 
-    private func drawSuspensions() -> some ChartContent {
-        let suspensions = state.suspensions
-        return ForEach(suspensions) { suspension in
-            let now = Date()
-
-            if let type = suspension.type, type == EventType.pumpSuspend.rawValue, let suspensionStart = suspension.timestamp {
-                let suspensionEnd = min(
-                    (
-                        suspensions
-                            .first(where: {
-                                $0.timestamp ?? now > suspensionStart && $0.type == EventType.pumpResume.rawValue })?
-                            .timestamp
-                    ) ?? now,
-                    now
-                )
-
-                let basalProfileDuringSuspension = basalProfiles.first(where: { $0.startDate <= suspensionStart })
-                let suspensionMarkHeight = basalProfileDuringSuspension?.amount ?? 1
-
-                RectangleMark(
-                    xStart: .value("start", suspensionStart),
-                    xEnd: .value("end", suspensionEnd),
-                    yStart: .value("suspend-start", 0),
-                    yEnd: .value("suspend-end", suspensionMarkHeight)
-                )
-                .foregroundStyle(Color.loopGray.opacity(colorScheme == .dark ? 0.3 : 0.8))
+    var mainChartXAxis: some AxisContent {
+        AxisMarks(values: .stride(by: .hour, count: screenHours > 6 ? (screenHours > 12 ? 4 : 2) : 1)) { _ in
+            if displayXgridLines {
+                AxisGridLine(stroke: .init(lineWidth: 0.5, dash: [2, 3]))
+            } else {
+                AxisGridLine(stroke: .init(lineWidth: 0, dash: [2, 3]))
             }
             }
         }
         }
     }
     }
 
 
-    func drawIOB() -> some ChartContent {
-        ForEach(state.enactedAndNonEnactedDeterminations) { iob in
-            let rawAmount = iob.iob?.doubleValue ?? 0
-            let amount: Double = rawAmount > 0 ? rawAmount : rawAmount * 2 // weigh negative iob with factor 2
-            let date: Date = iob.deliverAt ?? Date()
-
-            LineMark(x: .value("Time", date), y: .value("Amount", amount))
-                .foregroundStyle(Color.darkerBlue)
-            AreaMark(x: .value("Time", date), y: .value("Amount", amount))
-                .foregroundStyle(
-                    LinearGradient(
-                        gradient: Gradient(
-                            colors: [
-                                Color.darkerBlue.opacity(0.8),
-                                Color.darkerBlue.opacity(0.01)
-                            ]
-                        ),
-                        startPoint: .top,
-                        endPoint: .bottom
-                    )
-                )
-        }
-    }
-
-    func drawCOB(dummy: Bool) -> some ChartContent {
-        ForEach(state.enactedAndNonEnactedDeterminations) { cob in
-            let amount = Int(cob.cob)
-            let date: Date = cob.deliverAt ?? Date()
-
-            if dummy {
-                LineMark(x: .value("Time", date), y: .value("Value", amount))
-                    .foregroundStyle(Color.clear)
-                AreaMark(x: .value("Time", date), y: .value("Value", amount)).foregroundStyle(
-                    Color.clear
-                )
+    var basalChartXAxis: some AxisContent {
+        AxisMarks(values: .stride(by: .hour, count: screenHours > 6 ? (screenHours > 12 ? 4 : 2) : 1)) { _ in
+            if displayXgridLines {
+                AxisGridLine(stroke: .init(lineWidth: 0.5, dash: [2, 3]))
             } else {
             } else {
-                LineMark(x: .value("Time", date), y: .value("Value", amount))
-                    .foregroundStyle(Color.orange.gradient)
-                AreaMark(x: .value("Time", date), y: .value("Value", amount)).foregroundStyle(
-                    LinearGradient(
-                        gradient: Gradient(
-                            colors: [
-                                Color.orange.opacity(0.8),
-                                Color.orange.opacity(0.01)
-                            ]
-                        ),
-                        startPoint: .top,
-                        endPoint: .bottom
-                    )
-                )
+                AxisGridLine(stroke: .init(lineWidth: 0, dash: [2, 3]))
             }
             }
+            AxisValueLabel(format: .dateTime.hour(.defaultDigits(amPM: .narrow)), anchor: .top)
+                .font(.footnote).foregroundStyle(Color.primary)
         }
         }
     }
     }
 
 
-    private func prepareTempBasals() -> [(start: Date, end: Date, rate: Double)] {
-        let now = Date()
-        let tempBasals = state.tempBasals
-
-        return tempBasals.compactMap { temp -> (start: Date, end: Date, rate: Double)? in
-            let duration = temp.tempBasal?.duration ?? 0
-            let timestamp = temp.timestamp ?? Date()
-            let end = min(timestamp + duration.minutes, now)
-            let isInsulinSuspended = state.suspensions.contains { $0.timestamp ?? now >= timestamp && $0.timestamp ?? now <= end }
+    var mainChartYAxis: some AxisContent {
+        AxisMarks(position: .trailing) { value in
 
 
-            let rate = Double(truncating: temp.tempBasal?.rate ?? Decimal.zero as NSDecimalNumber) * (isInsulinSuspended ? 0 : 1)
+            if displayYgridLines {
+                AxisGridLine(stroke: .init(lineWidth: 0.5, dash: [2, 3]))
+            } else {
+                AxisGridLine(stroke: .init(lineWidth: 0, dash: [2, 3]))
+            }
 
 
-            // Check if there's a subsequent temp basal to determine the end time
-            guard let nextTemp = state.tempBasals.first(where: { $0.timestamp ?? .distantPast > timestamp }) else {
-                return (timestamp, end, rate)
+            if let glucoseValue = value.as(Double.self), glucoseValue > 0 {
+                /// fix offset between the two charts...
+                if units == .mmolL {
+                    AxisTick(length: 7, stroke: .init(lineWidth: 7)).foregroundStyle(Color.clear)
+                }
+                AxisValueLabel().font(.footnote).foregroundStyle(Color.primary)
             }
             }
-            return (timestamp, nextTemp.timestamp ?? Date(), rate) // end defaults to current time
         }
         }
     }
     }
 
 
-    private func drawTempBasals(dummy: Bool) -> some ChartContent {
-        ForEach(prepareTempBasals(), id: \.rate) { basal in
-            if dummy {
-                RectangleMark(
-                    xStart: .value("start", basal.start),
-                    xEnd: .value("end", basal.end),
-                    yStart: .value("rate-start", 0),
-                    yEnd: .value("rate-end", basal.rate)
-                ).foregroundStyle(Color.clear)
-
-                LineMark(x: .value("Start Date", basal.start), y: .value("Amount", basal.rate))
-                    .lineStyle(.init(lineWidth: 1)).foregroundStyle(Color.clear)
-
-                LineMark(x: .value("End Date", basal.end), y: .value("Amount", basal.rate))
-                    .lineStyle(.init(lineWidth: 1)).foregroundStyle(Color.clear)
+    var cobChartYAxis: some AxisContent {
+        AxisMarks(position: .trailing) { _ in
+            if displayYgridLines {
+                AxisGridLine(stroke: .init(lineWidth: 0.5, dash: [2, 3]))
             } else {
             } else {
-                RectangleMark(
-                    xStart: .value("start", basal.start),
-                    xEnd: .value("end", basal.end),
-                    yStart: .value("rate-start", 0),
-                    yEnd: .value("rate-end", basal.rate)
-                ).foregroundStyle(
-                    LinearGradient(
-                        gradient: Gradient(
-                            colors: [
-                                Color.insulin.opacity(0.6),
-                                Color.insulin.opacity(0.1)
-                            ]
-                        ),
-                        startPoint: .top,
-                        endPoint: .bottom
-                    )
-                )
-
-                LineMark(x: .value("Start Date", basal.start), y: .value("Amount", basal.rate))
-                    .lineStyle(.init(lineWidth: 1)).foregroundStyle(Color.insulin)
-
-                LineMark(x: .value("End Date", basal.end), y: .value("Amount", basal.rate))
-                    .lineStyle(.init(lineWidth: 1)).foregroundStyle(Color.insulin)
+                AxisGridLine(stroke: .init(lineWidth: 0, dash: [2, 3]))
             }
             }
         }
         }
     }
     }
+}
 
 
-    private func drawBasalProfile() -> some ChartContent {
-        /// dashed profile line
-        ForEach(basalProfiles, id: \.self) { profile in
-            LineMark(
-                x: .value("Start Date", profile.startDate),
-                y: .value("Amount", profile.amount),
-                series: .value("profile", "profile")
-            ).lineStyle(.init(lineWidth: 2, dash: [2, 4])).foregroundStyle(Color.insulin)
-            LineMark(
-                x: .value("End Date", profile.endDate ?? endMarker),
-                y: .value("Amount", profile.amount),
-                series: .value("profile", "profile")
-            ).lineStyle(.init(lineWidth: 2.5, dash: [2, 4])).foregroundStyle(Color.insulin)
-        }
-    }
+// MARK: - Calculations and formatting
 
 
+extension MainChartView {
     func fullWidth(viewWidth: CGFloat) -> CGFloat {
     func fullWidth(viewWidth: CGFloat) -> CGFloat {
         viewWidth * CGFloat(hours) / CGFloat(min(max(screenHours, 2), 24))
         viewWidth * CGFloat(hours) / CGFloat(min(max(screenHours, 2), 24))
     }
     }
 
 
-    /// calculations for temp target bar mark
-    private func calculateTTs() async {
-        // Perform calculations off the main thread
-        let calculatedTTs = await Task.detached { () -> [ChartTempTarget] in
-            var groupedPackages: [[TempTarget]] = []
-            var currentPackage: [TempTarget] = []
-            var calculatedTTs: [ChartTempTarget] = []
-
-            for target in await tempTargets {
-                if target.duration > 0 {
-                    if !currentPackage.isEmpty {
-                        groupedPackages.append(currentPackage)
-                        currentPackage = []
-                    }
-                    currentPackage.append(target)
-                } else if let lastNonZeroTempTarget = currentPackage.last(where: { $0.duration > 0 }) {
-                    // Ensure this cancel target is within the valid time range
-                    if target.createdAt >= lastNonZeroTempTarget.createdAt,
-                       target.createdAt <= lastNonZeroTempTarget.createdAt
-                       .addingTimeInterval(TimeInterval(lastNonZeroTempTarget.duration * 60))
-                    {
-                        currentPackage.append(target)
-                    }
-                }
-            }
-
-            // Append the last group, if any
-            if !currentPackage.isEmpty {
-                groupedPackages.append(currentPackage)
-            }
-
-            for package in groupedPackages {
-                guard let firstNonZeroTarget = package.first(where: { $0.duration > 0 }) else { continue }
-
-                var end = firstNonZeroTarget.createdAt.addingTimeInterval(TimeInterval(firstNonZeroTarget.duration * 60))
-
-                let earliestCancelTarget = package.filter({ $0.duration == 0 }).min(by: { $0.createdAt < $1.createdAt })
-
-                if let earliestCancelTarget = earliestCancelTarget {
-                    end = min(earliestCancelTarget.createdAt, end)
-                }
-
-                if let targetTop = firstNonZeroTarget.targetTop {
-                    let adjustedTarget = await units == .mgdL ? targetTop : targetTop.asMmolL
-                    calculatedTTs
-                        .append(ChartTempTarget(amount: adjustedTarget, start: firstNonZeroTarget.createdAt, end: end))
-                }
-            }
-
-            return calculatedTTs
-        }.value
-
-        // Update chartTempTargets on the main thread
-        await MainActor.run {
-            self.chartTempTargets = calculatedTTs
-        }
-    }
-
-    private func findRegularBasalPoints(
-        timeBegin: TimeInterval,
-        timeEnd: TimeInterval,
-        autotuned: Bool
-    ) async -> [BasalProfile] {
-        guard timeBegin < timeEnd else { return [] }
-
-        let beginDate = Date(timeIntervalSince1970: timeBegin)
-        let startOfDay = Calendar.current.startOfDay(for: beginDate)
-        let profile = autotuned ? autotunedBasalProfile : basalProfile
-        var basalPoints: [BasalProfile] = []
-
-        // Iterate over the next three days, multiplying the time intervals
-        for dayOffset in 0 ..< 3 {
-            let dayTimeOffset = TimeInterval(dayOffset * 24 * 60 * 60) // One Day in seconds
-            for entry in profile {
-                let basalTime = startOfDay.addingTimeInterval(entry.minutes.minutes.timeInterval + dayTimeOffset)
-                let basalTimeInterval = basalTime.timeIntervalSince1970
-
-                // Only append points within the timeBegin and timeEnd range
-                if basalTimeInterval >= timeBegin, basalTimeInterval < timeEnd {
-                    basalPoints.append(BasalProfile(
-                        amount: Double(entry.rate),
-                        isOverwritten: false,
-                        startDate: basalTime
-                    ))
-                }
-            }
-        }
-
-        return basalPoints
-    }
-
-    /// update start and  end marker to fix scroll update problem with x axis
-    private func updateStartEndMarkers() {
+    // Update start and  end marker to fix scroll update problem with x axis
+    func updateStartEndMarkers() {
         startMarker = Date(timeIntervalSince1970: TimeInterval(NSDate().timeIntervalSince1970 - 86400))
         startMarker = Date(timeIntervalSince1970: TimeInterval(NSDate().timeIntervalSince1970 - 86400))
 
 
         let threeHourSinceNow = Date(timeIntervalSinceNow: TimeInterval(hours: 3))
         let threeHourSinceNow = Date(timeIntervalSinceNow: TimeInterval(hours: 3))
@@ -683,48 +412,6 @@ extension MainChartView {
             dynamicFutureDateForCone.addingTimeInterval(TimeInterval(minutes: 30)) : threeHourSinceNow
             dynamicFutureDateForCone.addingTimeInterval(TimeInterval(minutes: 30)) : threeHourSinceNow
     }
     }
 
 
-    private func calculateBasals() {
-        Task {
-            let dayAgoTime = Date().addingTimeInterval(-1.days.timeInterval).timeIntervalSince1970
-
-            // Get Regular and Autotuned Basal parallel
-            async let getRegularBasalPoints = findRegularBasalPoints(
-                timeBegin: dayAgoTime,
-                timeEnd: endMarker.timeIntervalSince1970,
-                autotuned: false
-            )
-
-            async let getAutotunedBasalPoints = findRegularBasalPoints(
-                timeBegin: dayAgoTime,
-                timeEnd: endMarker.timeIntervalSince1970,
-                autotuned: true
-            )
-
-            let (regularPoints, autotunedBasalPoints) = await (getRegularBasalPoints, getAutotunedBasalPoints)
-
-            var totalBasal = regularPoints + autotunedBasalPoints
-            totalBasal.sort {
-                $0.startDate.timeIntervalSince1970 < $1.startDate.timeIntervalSince1970
-            }
-
-            var basals: [BasalProfile] = []
-            totalBasal.indices.forEach { index in
-                basals.append(BasalProfile(
-                    amount: totalBasal[index].amount,
-                    isOverwritten: totalBasal[index].isOverwritten,
-                    startDate: totalBasal[index].startDate,
-                    endDate: totalBasal.count > index + 1 ? totalBasal[index + 1].startDate : endMarker
-                ))
-            }
-
-            await MainActor.run {
-                basalProfiles = basals
-            }
-        }
-    }
-
-    // MARK: - Chart formatting
-
     private func yAxisChartData() {
     private func yAxisChartData() {
         Task {
         Task {
             let (minGlucose, maxGlucose, minForecast, maxForecast) = await Task
             let (minGlucose, maxGlucose, minForecast, maxForecast) = await Task
@@ -802,77 +489,6 @@ extension MainChartView {
         minValueIobChart = minValue
         minValueIobChart = minValue
         maxValueIobChart = maxValue
         maxValueIobChart = maxValue
     }
     }
-
-    func basalChartPlotStyle(_ plotContent: ChartPlotContent) -> some View {
-        plotContent
-            .rotationEffect(.degrees(180))
-            .scaleEffect(x: -1, y: 1)
-    }
-
-    var mainChartXAxis: some AxisContent {
-        AxisMarks(values: .stride(by: .hour, count: screenHours > 6 ? (screenHours > 12 ? 4 : 2) : 1)) { _ in
-            if displayXgridLines {
-                AxisGridLine(stroke: .init(lineWidth: 0.5, dash: [2, 3]))
-            } else {
-                AxisGridLine(stroke: .init(lineWidth: 0, dash: [2, 3]))
-            }
-        }
-    }
-
-    var basalChartXAxis: some AxisContent {
-        AxisMarks(values: .stride(by: .hour, count: screenHours > 6 ? (screenHours > 12 ? 4 : 2) : 1)) { _ 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)
-                .font(.footnote).foregroundStyle(Color.primary)
-        }
-    }
-
-    var mainChartYAxis: some AxisContent {
-        AxisMarks(position: .trailing) { value in
-
-            if displayYgridLines {
-                AxisGridLine(stroke: .init(lineWidth: 0.5, dash: [2, 3]))
-            } else {
-                AxisGridLine(stroke: .init(lineWidth: 0, dash: [2, 3]))
-            }
-
-            if let glucoseValue = value.as(Double.self), glucoseValue > 0 {
-                /// fix offset between the two charts...
-                if units == .mmolL {
-                    AxisTick(length: 7, stroke: .init(lineWidth: 7)).foregroundStyle(Color.clear)
-                }
-                AxisValueLabel().font(.footnote).foregroundStyle(Color.primary)
-            }
-        }
-    }
-
-    var cobChartYAxis: some AxisContent {
-        AxisMarks(position: .trailing) { _ in
-            if displayYgridLines {
-                AxisGridLine(stroke: .init(lineWidth: 0.5, dash: [2, 3]))
-            } else {
-                AxisGridLine(stroke: .init(lineWidth: 0, dash: [2, 3]))
-            }
-        }
-    }
-}
-
-struct LegendItem: View {
-    var color: Color
-    var label: String
-
-    var body: some View {
-        Group {
-            Circle().fill(color).frame(width: 8, height: 8)
-            Text(label)
-                .font(.system(size: 10, weight: .bold))
-                .foregroundColor(color)
-        }
-    }
 }
 }
 
 
 extension Int16 {
 extension Int16 {

+ 82 - 0
FreeAPS/Sources/Modules/Home/View/Chart/TempTargets.swift

@@ -0,0 +1,82 @@
+import Charts
+import Foundation
+import SwiftUI
+
+struct ChartTempTarget: Hashable {
+    let amount: Decimal
+    let start: Date
+    let end: Date
+}
+
+extension MainChartView {
+    func drawTempTargets() -> some ChartContent {
+        ForEach(chartTempTargets, id: \.self) { target in
+            let targetLimited = min(max(target.amount, 0), upperLimit)
+
+            RuleMark(
+                xStart: .value("Start", target.start),
+                xEnd: .value("End", target.end),
+                y: .value("Value", targetLimited)
+            )
+            .foregroundStyle(Color.purple.opacity(0.75)).lineStyle(.init(lineWidth: 8))
+        }
+    }
+
+    // Calculations for temp target bar mark
+    func calculateTTs() async {
+        // Perform calculations off the main thread
+        let calculatedTTs = await Task.detached { () -> [ChartTempTarget] in
+            var groupedPackages: [[TempTarget]] = []
+            var currentPackage: [TempTarget] = []
+            var calculatedTTs: [ChartTempTarget] = []
+
+            for target in await tempTargets {
+                if target.duration > 0 {
+                    if !currentPackage.isEmpty {
+                        groupedPackages.append(currentPackage)
+                        currentPackage = []
+                    }
+                    currentPackage.append(target)
+                } else if let lastNonZeroTempTarget = currentPackage.last(where: { $0.duration > 0 }) {
+                    // Ensure this cancel target is within the valid time range
+                    if target.createdAt >= lastNonZeroTempTarget.createdAt,
+                       target.createdAt <= lastNonZeroTempTarget.createdAt
+                       .addingTimeInterval(TimeInterval(lastNonZeroTempTarget.duration * 60))
+                    {
+                        currentPackage.append(target)
+                    }
+                }
+            }
+
+            // Append the last group, if any
+            if !currentPackage.isEmpty {
+                groupedPackages.append(currentPackage)
+            }
+
+            for package in groupedPackages {
+                guard let firstNonZeroTarget = package.first(where: { $0.duration > 0 }) else { continue }
+
+                var end = firstNonZeroTarget.createdAt.addingTimeInterval(TimeInterval(firstNonZeroTarget.duration * 60))
+
+                let earliestCancelTarget = package.filter({ $0.duration == 0 }).min(by: { $0.createdAt < $1.createdAt })
+
+                if let earliestCancelTarget = earliestCancelTarget {
+                    end = min(earliestCancelTarget.createdAt, end)
+                }
+
+                if let targetTop = firstNonZeroTarget.targetTop {
+                    let adjustedTarget = await units == .mgdL ? targetTop : targetTop.asMmolL
+                    calculatedTTs
+                        .append(ChartTempTarget(amount: adjustedTarget, start: firstNonZeroTarget.createdAt, end: end))
+                }
+            }
+
+            return calculatedTTs
+        }.value
+
+        // Update chartTempTargets on the main thread
+        await MainActor.run {
+            self.chartTempTargets = calculatedTTs
+        }
+    }
+}

+ 0 - 5
FreeAPS/Sources/Modules/Home/View/HomeRootView.swift

@@ -338,13 +338,8 @@ extension Home {
                 MainChartView(
                 MainChartView(
                     geo: geo,
                     geo: geo,
                     units: $state.units,
                     units: $state.units,
-                    announcement: $state.announcement,
                     hours: .constant(state.filteredHours),
                     hours: .constant(state.filteredHours),
-                    maxBasal: $state.maxBasal,
-                    autotunedBasalProfile: $state.autotunedBasalProfile,
-                    basalProfile: $state.basalProfile,
                     tempTargets: $state.tempTargets,
                     tempTargets: $state.tempTargets,
-                    smooth: $state.smooth,
                     highGlucose: $state.highGlucose,
                     highGlucose: $state.highGlucose,
                     lowGlucose: $state.lowGlucose,
                     lowGlucose: $state.lowGlucose,
                     screenHours: $state.hours,
                     screenHours: $state.hours,