CurrentGlucoseView.swift 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. import SwiftUI
  2. struct CurrentGlucoseView: View {
  3. @Binding var recentGlucose: BloodGlucose?
  4. @Binding var timerDate: Date
  5. @Binding var delta: Int?
  6. @Binding var units: GlucoseUnits
  7. @Binding var alarm: GlucoseAlarm?
  8. @Binding var lowGlucose: Decimal
  9. @Binding var highGlucose: Decimal
  10. private var glucoseFormatter: NumberFormatter {
  11. let formatter = NumberFormatter()
  12. formatter.numberStyle = .decimal
  13. formatter.maximumFractionDigits = 0
  14. if units == .mmolL {
  15. formatter.minimumFractionDigits = 1
  16. formatter.maximumFractionDigits = 1
  17. }
  18. formatter.roundingMode = .halfUp
  19. return formatter
  20. }
  21. private var deltaFormatter: NumberFormatter {
  22. let formatter = NumberFormatter()
  23. formatter.numberStyle = .decimal
  24. formatter.maximumFractionDigits = 1
  25. formatter.positivePrefix = " +"
  26. formatter.negativePrefix = " -"
  27. return formatter
  28. }
  29. private var timaAgoFormatter: NumberFormatter {
  30. let formatter = NumberFormatter()
  31. formatter.numberStyle = .decimal
  32. formatter.maximumFractionDigits = 0
  33. formatter.negativePrefix = ""
  34. return formatter
  35. }
  36. private var dateFormatter: DateFormatter {
  37. let formatter = DateFormatter()
  38. formatter.timeStyle = .short
  39. return formatter
  40. }
  41. var body: some View {
  42. VStack(alignment: .center) {
  43. HStack {
  44. Text(
  45. (recentGlucose?.glucose ?? 100) == 400 ? "HIGH" : recentGlucose?.glucose
  46. .map {
  47. glucoseFormatter
  48. .string(from: Double(units == .mmolL ? $0.asMmolL : Decimal($0)) as NSNumber)! }
  49. ?? "--"
  50. )
  51. .font(.system(size: 40, weight: .bold))
  52. .foregroundColor(alarm == nil ? colorOfGlucose : .loopRed)
  53. // image
  54. }
  55. HStack {
  56. let minutesAgo = -1 * (recentGlucose?.dateString.timeIntervalSinceNow ?? 0) / 60
  57. let text = timaAgoFormatter.string(for: Double(minutesAgo)) ?? ""
  58. Text(
  59. minutesAgo <= 1 ? "< 1 " + NSLocalizedString("min", comment: "Short form for minutes") : (
  60. text + " " +
  61. NSLocalizedString("min", comment: "Short form for minutes") + " "
  62. )
  63. )
  64. .font(.caption2).foregroundColor(.secondary)
  65. Text(
  66. delta
  67. .map {
  68. deltaFormatter.string(from: Double(units == .mmolL ? $0.asMmolL : Decimal($0)) as NSNumber)!
  69. } ?? "--"
  70. )
  71. .font(.caption2).foregroundColor(.secondary)
  72. }.frame(alignment: .top)
  73. }
  74. .overlay(
  75. TrendShape(color: colorOfGlucose)
  76. )
  77. }
  78. var image: Image {
  79. guard let direction = recentGlucose?.direction else {
  80. return Image(systemName: "arrow.left.and.right")
  81. }
  82. switch direction {
  83. case .doubleUp,
  84. .singleUp,
  85. .tripleUp:
  86. return Image(systemName: "arrow.up")
  87. case .fortyFiveUp:
  88. return Image(systemName: "arrow.up.right")
  89. case .flat:
  90. return Image(systemName: "arrow.forward")
  91. case .fortyFiveDown:
  92. return Image(systemName: "arrow.down.forward")
  93. case .doubleDown,
  94. .singleDown,
  95. .tripleDown:
  96. return Image(systemName: "arrow.down")
  97. case .none,
  98. .notComputable,
  99. .rateOutOfRange:
  100. return Image(systemName: "arrow.left.and.right")
  101. }
  102. }
  103. var colorOfGlucose: Color {
  104. let whichGlucose = recentGlucose?.glucose ?? 0
  105. guard lowGlucose < highGlucose else { return .primary }
  106. switch whichGlucose {
  107. case 0 ..< Int(lowGlucose):
  108. return .loopRed
  109. case Int(lowGlucose) ..< Int(highGlucose):
  110. return .loopGreen
  111. case Int(highGlucose)...:
  112. return .loopYellow
  113. default:
  114. return .loopYellow
  115. }
  116. }
  117. }
  118. struct Triangle: Shape {
  119. func path(in rect: CGRect) -> Path {
  120. var path = Path()
  121. path.move(to: CGPoint(x: rect.midX, y: rect.minY))
  122. path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
  123. path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
  124. path.closeSubpath()
  125. return path
  126. }
  127. }
  128. struct TrendShape: View {
  129. let color: Color
  130. var body: some View {
  131. HStack(alignment: .center, spacing: -4) {
  132. CircleShape(color: color)
  133. TriangleShape(color: color)
  134. }
  135. }
  136. }
  137. struct CircleShape: View {
  138. let color: Color
  139. var body: some View {
  140. Circle()
  141. .stroke(color, lineWidth: 10)
  142. .frame(width: 100, height: 100)
  143. .offset(x: 13)
  144. }
  145. }
  146. struct TriangleShape: View {
  147. let color: Color
  148. var body: some View {
  149. Triangle()
  150. .fill(color)
  151. .frame(width: 30, height: 30)
  152. .rotationEffect(.degrees(90))
  153. .offset(x: 13)
  154. }
  155. }