| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- import Charts
- import CoreData
- import SwiftUI
- let screenSize: CGRect = UIScreen.main.bounds
- let calendar = Calendar.current
- struct MainChartView: View {
- var geo: GeometryProxy
- var safeAreaSize: CGFloat
- var units: GlucoseUnits
- var hours: Int
- var highGlucose: Decimal
- var lowGlucose: Decimal
- var currentGlucoseTarget: Decimal
- var glucoseColorScheme: GlucoseColorScheme
- var screenHours: Int16
- var displayXgridLines: Bool
- var displayYgridLines: Bool
- var thresholdLines: Bool
- var state: Home.StateModel
- @State var basalProfiles: [BasalProfile] = []
- @State var preparedTempBasals: [(start: Date, end: Date, rate: Double)] = []
- @State var selection: Date? = nil
- @State var mainChartHasInitialized = false
- let now = Date.now
- private let context = CoreDataStack.shared.persistentContainer.viewContext
- @Environment(\.colorScheme) var colorScheme
- @Environment(\.calendar) var calendar
- var upperLimit: Decimal {
- units == .mgdL ? 400 : 22.2
- }
- private var selectedGlucose: GlucoseStored? {
- guard let selection = selection else { return nil }
- let range = selection.addingTimeInterval(-150) ... selection.addingTimeInterval(150)
- return state.glucoseFromPersistence.first { $0.date.map(range.contains) ?? false }
- }
- private func findDetermination(in range: ClosedRange<Date>) -> OrefDetermination? {
- state.enactedAndNonEnactedDeterminations.first {
- $0.deliverAt ?? now >= range.lowerBound && $0.deliverAt ?? now <= range.upperBound
- }
- }
- var selectedCOBValue: OrefDetermination? {
- guard let selection = selection else { return nil }
- let range = selection.addingTimeInterval(-150) ... selection.addingTimeInterval(150)
- return findDetermination(in: range)
- }
- var selectedIOBValue: OrefDetermination? {
- guard let selection = selection else { return nil }
- let range = selection.addingTimeInterval(-150) ... selection.addingTimeInterval(150)
- return findDetermination(in: range)
- }
- var body: some View {
- VStack {
- ZStack {
- VStack(spacing: 5) {
- dummyBasalChart
- staticYAxisChart
- Spacer()
- dummyCobChart
- }
- ScrollViewReader { scroller in
- ScrollView(.horizontal, showsIndicators: false) {
- VStack(spacing: 5) {
- basalChart
- mainChart
- Spacer()
- cobIobChart
- }.onChange(of: screenHours) {
- scroller.scrollTo("MainChart", anchor: .trailing)
- }
- .onChange(of: state.glucoseFromPersistence.last?.glucose) {
- scroller.scrollTo("MainChart", anchor: .trailing)
- state.updateStartEndMarkers()
- }
- .onChange(of: state.enactedAndNonEnactedDeterminations.first?.deliverAt) {
- scroller.scrollTo("MainChart", anchor: .trailing)
- }
- .onChange(of: units) {
- // TODO: - Refactor this to only update the Y Axis Scale
- state.setupGlucoseArray()
- }
- .onAppear {
- if !mainChartHasInitialized {
- scroller.scrollTo("MainChart", anchor: .trailing)
- state.updateStartEndMarkers()
- calculateTempBasalsInBackground()
- mainChartHasInitialized = true
- }
- }
- }
- }
- }
- }
- }
- }
- // MARK: - Main Chart with selection Popover
- extension MainChartView {
- private var mainChart: some View {
- VStack {
- Chart {
- drawStartRuleMark()
- drawEndRuleMark()
- drawCurrentTimeMarker()
- GlucoseTargetsView(
- targetProfiles: state.targetProfiles
- )
- OverrideView(
- state: state,
- overrides: state.overrides,
- overrideRunStored: state.overrideRunStored,
- units: state.units,
- viewContext: context
- )
- TempTargetView(
- tempTargetStored: state.tempTargetStored,
- tempTargetRunStored: state.tempTargetRunStored,
- units: state.units,
- viewContext: context
- )
- GlucoseChartView(
- glucoseData: state.glucoseFromPersistence,
- units: state.units,
- highGlucose: state.highGlucose,
- lowGlucose: state.lowGlucose,
- currentGlucoseTarget: state.currentGlucoseTarget,
- isSmoothingEnabled: state.isSmoothingEnabled,
- glucoseColorScheme: state.glucoseColorScheme
- )
- InsulinView(
- glucoseData: state.glucoseFromPersistence,
- insulinData: state.insulinFromPersistence,
- units: state.units
- )
- CarbView(
- glucoseData: state.glucoseFromPersistence,
- units: state.units,
- carbData: state.carbsFromPersistence,
- fpuData: state.fpusFromPersistence,
- minValue: units == .mgdL ? state.minYAxisValue : state.minYAxisValue
- .asMmolL
- )
- ForecastView(
- preprocessedData: state.preprocessedData,
- minForecast: state.minForecast,
- maxForecast: state.maxForecast,
- units: state.units,
- maxValue: state.maxYAxisValue,
- forecastDisplayType: state.forecastDisplayType
- )
- /// show glucose value when hovering over it
- if let selectedGlucose {
- SelectionPopoverView(
- selectedGlucose: selectedGlucose,
- selectedIOBValue: selectedIOBValue,
- selectedCOBValue: selectedCOBValue,
- units: units,
- highGlucose: highGlucose,
- lowGlucose: lowGlucose,
- currentGlucoseTarget: currentGlucoseTarget,
- glucoseColorScheme: glucoseColorScheme
- )
- }
- }
- .id("MainChart")
- .onChange(of: state.insulinFromPersistence) {
- state.roundedTotalBolus = state.calculateTINS()
- }
- .frame(
- minHeight: geo.size.height * (0.28 - safeAreaSize)
- )
- .frame(width: fullWidth(viewWidth: screenSize.width))
- .chartXScale(domain: state.startMarker ... state.endMarker)
- .chartXAxis { mainChartXAxis }
- .chartYAxis { mainChartYAxis }
- .chartYAxis(.hidden)
- .chartXSelection(value: $selection)
- .chartYScale(
- domain: units == .mgdL ? state.minYAxisValue ... state.maxYAxisValue : state.minYAxisValue
- .asMmolL ... state.maxYAxisValue.asMmolL
- )
- .chartLegend(.hidden)
- }
- }
- }
|