MainChartView2.swift 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. import Charts
  2. import SwiftUI
  3. struct MainChartView2: View {
  4. @Binding var tempBasals: [PumpHistoryEvent]
  5. @Binding var glucose: [BloodGlucose]
  6. @Binding var screenHours: Int16
  7. @Binding var highGlucose: Decimal
  8. @Binding var lowGlucose: Decimal
  9. @Binding var carbs: [CarbsEntry]
  10. var body: some View {
  11. VStack(alignment: .center, spacing: 8, content: {
  12. GlucoseChart(glucose: $glucose, screenHours: $screenHours, highGlucose: $highGlucose, lowGlucose: $lowGlucose)
  13. .padding(.bottom, 20)
  14. CarbsChart(carbs: $carbs, screenHours: $screenHours)
  15. .padding(.bottom, 8)
  16. BasalChart(tempBasals: $tempBasals, screenHours: $screenHours)
  17. .padding(.bottom, 8)
  18. Legend()
  19. })
  20. }
  21. }
  22. // MARK: GLUCOSE FOR CHART
  23. struct GlucoseChart: View {
  24. @Binding var glucose: [BloodGlucose]
  25. @Binding var screenHours: Int16
  26. @Binding var highGlucose: Decimal
  27. @Binding var lowGlucose: Decimal
  28. var body: some View {
  29. VStack {
  30. let filteredGlucose: [BloodGlucose] = filterGlucoseData(for: screenHours)
  31. Chart(filteredGlucose) {
  32. RuleMark(y: .value("High", highGlucose))
  33. .foregroundStyle(Color.loopYellow)
  34. .lineStyle(StrokeStyle(lineWidth: 1, dash: [5]))
  35. RuleMark(y: .value("Low", lowGlucose))
  36. .foregroundStyle(Color.loopRed)
  37. .lineStyle(StrokeStyle(lineWidth: 1, dash: [5]))
  38. // MARK: TO DO -> at the moment this rule mark is not visible because the chart is not scrollable
  39. if let currentTime = getCurrentTime() {
  40. RuleMark(x: .value("Current Time", currentTime))
  41. .foregroundStyle(Color.gray)
  42. .lineStyle(StrokeStyle(lineWidth: 1, dash: [5]))
  43. }
  44. PointMark(
  45. x: .value("Time", $0.dateString),
  46. y: .value("Value", $0.value)
  47. )
  48. .foregroundStyle(
  49. $0.value > Double(highGlucose) ? Color.yellow.gradient :
  50. $0.value < Double(lowGlucose) ? Color.red.gradient : Color.green.gradient
  51. )
  52. .symbolSize(12)
  53. }
  54. .frame(height: 250)
  55. .chartXAxis(.hidden)
  56. }
  57. }
  58. private func filterGlucoseData(for hours: Int16) -> [BloodGlucose] {
  59. guard hours > 0 else {
  60. return glucose
  61. }
  62. let currentDate = Date()
  63. let startDate = Calendar.current.date(byAdding: .hour, value: -Int(hours), to: currentDate) ?? currentDate
  64. return glucose.filter { $0.dateString >= startDate }
  65. }
  66. private func getCurrentTime() -> String? {
  67. let dateFormatter = DateFormatter()
  68. dateFormatter.dateFormat = "HH:mm"
  69. return dateFormatter.string(from: Date())
  70. }
  71. }
  72. // MARK: BASAL FOR CHART
  73. struct BasalChart: View {
  74. @Binding var tempBasals: [PumpHistoryEvent]
  75. @Binding var screenHours: Int16
  76. var body: some View {
  77. VStack {
  78. let filteredBasal: [PumpHistoryEvent] = filterBasalData(for: screenHours)
  79. Chart(filteredBasal) {
  80. BarMark(
  81. x: .value("Time", $0.timestamp),
  82. y: .value("Value", $0.rate ?? 0)
  83. )
  84. .foregroundStyle(Color.blue.gradient)
  85. .cornerRadius(0)
  86. }
  87. .frame(height: 80)
  88. // .rotationEffect(.degrees(180))
  89. // .chartXAxis(.hidden)
  90. .chartYAxis(.hidden)
  91. .chartPlotStyle { plotArea in
  92. plotArea.background(.blue.gradient.opacity(0.1))
  93. }
  94. }
  95. }
  96. private func filterBasalData(for hours: Int16) -> [PumpHistoryEvent] {
  97. guard hours > 0 else {
  98. return tempBasals
  99. }
  100. let currentDate = Date()
  101. let startDate = Calendar.current.date(byAdding: .hour, value: -Int(hours), to: currentDate) ?? currentDate
  102. return tempBasals.filter { $0.timestamp >= startDate }
  103. }
  104. }
  105. // MARK: COB
  106. struct CarbsChart: View {
  107. @Binding var carbs: [CarbsEntry]
  108. @Binding var screenHours: Int16
  109. var body: some View {
  110. VStack {
  111. let filteredCarbs: [CarbsEntry] = filterCarbData(for: screenHours)
  112. Chart(filteredCarbs) {
  113. PointMark(
  114. x: .value("Time", $0.createdAt),
  115. y: .value("Value", 40)
  116. )
  117. .foregroundStyle(Color.loopYellow.gradient)
  118. }
  119. .frame(height: 80)
  120. .chartYAxis(.hidden)
  121. .chartXAxis(.hidden)
  122. }
  123. }
  124. private func filterCarbData(for hours: Int16) -> [CarbsEntry] {
  125. guard hours > 0 else {
  126. return carbs
  127. }
  128. let currentDate = Date()
  129. let startDate = Calendar.current.date(byAdding: .hour, value: -Int(hours), to: currentDate) ?? currentDate
  130. return carbs.filter { $0.createdAt >= startDate }
  131. }
  132. }
  133. // MARK: LEGEND PANEL FOR CHART
  134. struct Legend: View {
  135. var body: some View {
  136. HStack {
  137. Image(systemName: "line.diagonal")
  138. .rotationEffect(Angle(degrees: 45))
  139. .foregroundColor(.green)
  140. Text("BG")
  141. .foregroundColor(.secondary)
  142. Spacer()
  143. Image(systemName: "line.diagonal")
  144. .rotationEffect(Angle(degrees: 45))
  145. .foregroundColor(.insulin)
  146. Text("IOB")
  147. .foregroundColor(.secondary)
  148. Spacer()
  149. Image(systemName: "line.diagonal")
  150. .rotationEffect(Angle(degrees: 45))
  151. .foregroundColor(.purple)
  152. Text("ZT")
  153. .foregroundColor(.secondary)
  154. Spacer()
  155. Image(systemName: "line.diagonal")
  156. .frame(height: 10)
  157. .rotationEffect(Angle(degrees: 45))
  158. .foregroundColor(.loopYellow)
  159. Text("COB")
  160. .foregroundColor(.secondary)
  161. Spacer()
  162. Image(systemName: "line.diagonal")
  163. .rotationEffect(Angle(degrees: 45))
  164. .foregroundColor(.orange)
  165. Text("UAM")
  166. .foregroundColor(.secondary)
  167. }
  168. .font(.caption2)
  169. .padding(.horizontal, 40)
  170. .padding(.vertical, 1)
  171. }
  172. }