PumpView.swift 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. import CoreData
  2. import SwiftUI
  3. struct PumpView: View {
  4. let reservoir: Decimal?
  5. let name: String
  6. let expiresAtDate: Date?
  7. let timerDate: Date
  8. let timeZone: TimeZone?
  9. let pumpStatusHighlightMessage: String?
  10. let battery: [OpenAPS_Battery]
  11. @Environment(\.colorScheme) var colorScheme
  12. private var batteryFormatter: NumberFormatter {
  13. let formatter = NumberFormatter()
  14. formatter.numberStyle = .percent
  15. return formatter
  16. }
  17. var body: some View {
  18. if let pumpStatusHighlightMessage = pumpStatusHighlightMessage { // display message instead pump info
  19. VStack(alignment: .center) {
  20. Text(pumpStatusHighlightMessage).font(.footnote).fontWeight(.bold)
  21. .multilineTextAlignment(.center).frame(maxWidth: /*@START_MENU_TOKEN@*/ .infinity/*@END_MENU_TOKEN@*/)
  22. }.frame(width: 100)
  23. } else {
  24. VStack(alignment: .leading, spacing: 20) {
  25. if reservoir == nil && battery.isEmpty {
  26. VStack(alignment: .center, spacing: 12) {
  27. HStack {
  28. Image(systemName: "keyboard.onehanded.left")
  29. .font(.body)
  30. .imageScale(.large)
  31. }
  32. HStack {
  33. Text("Add pump")
  34. .font(.caption)
  35. .bold()
  36. }
  37. }
  38. .frame(alignment: .top)
  39. }
  40. if let reservoir = reservoir {
  41. HStack {
  42. Image(systemName: "cross.vial.fill")
  43. .font(.callout)
  44. if reservoir == 0xDEAD_BEEF {
  45. Text("50+ " + NSLocalizedString("U", comment: "Insulin unit"))
  46. .font(.callout)
  47. .fontWeight(.bold)
  48. .fontDesign(.rounded)
  49. } else {
  50. Text(
  51. Formatter.integerFormatter
  52. .string(from: reservoir as NSNumber)! + NSLocalizedString(" U", comment: "Insulin unit")
  53. )
  54. .font(.callout)
  55. .fontWeight(.bold)
  56. .fontDesign(.rounded)
  57. }
  58. }
  59. .padding(.vertical, 5)
  60. .padding(.horizontal, 10)
  61. .foregroundStyle(reservoirColor)
  62. .overlay(
  63. Capsule()
  64. .stroke(reservoirColor.opacity(0.4), lineWidth: 2)
  65. )
  66. if let timeZone = timeZone, timeZone.secondsFromGMT() != TimeZone.current.secondsFromGMT() {
  67. Image(systemName: "clock.badge.exclamationmark.fill")
  68. .font(.callout)
  69. .symbolRenderingMode(.palette)
  70. .foregroundStyle(.red, Color(.warning))
  71. }
  72. }
  73. if (battery.first?.display) != nil, let shouldBatteryDisplay = battery.first?.display, shouldBatteryDisplay {
  74. HStack {
  75. Image(systemName: "battery.100")
  76. .font(.callout)
  77. .foregroundStyle(batteryColor)
  78. Text("\(Int(battery.first?.percent ?? 100)) %")
  79. .font(.callout).fontWeight(.bold).fontDesign(.rounded)
  80. }
  81. }
  82. if let date = expiresAtDate {
  83. HStack {
  84. Image(systemName: "stopwatch.fill")
  85. .font(.callout)
  86. .foregroundStyle(timerColor)
  87. Text(remainingTimeString(time: date.timeIntervalSince(timerDate)))
  88. .font(!(date.timeIntervalSince(timerDate) > 0) ? .subheadline : .callout)
  89. .fontWeight(.bold)
  90. .fontDesign(.rounded)
  91. }
  92. }
  93. }
  94. }
  95. }
  96. private func remainingTimeString(time: TimeInterval) -> String {
  97. guard time > 0 else {
  98. return NSLocalizedString("Replace pod", comment: "View/Header when pod expired")
  99. }
  100. var time = time
  101. let days = Int(time / 1.days.timeInterval)
  102. time -= days.days.timeInterval
  103. let hours = Int(time / 1.hours.timeInterval)
  104. time -= hours.hours.timeInterval
  105. let minutes = Int(time / 1.minutes.timeInterval)
  106. if days >= 1 {
  107. return "\(days)" + NSLocalizedString("d", comment: "abbreviation for days") + " \(hours)" +
  108. NSLocalizedString("h", comment: "abbreviation for hours")
  109. }
  110. if hours >= 1 {
  111. return "\(hours)" + NSLocalizedString("h", comment: "abbreviation for hours")
  112. }
  113. return "\(minutes)" + NSLocalizedString("m", comment: "abbreviation for minutes")
  114. }
  115. private var batteryColor: Color {
  116. guard let battery = battery.first else {
  117. return .gray
  118. }
  119. switch battery.percent {
  120. case ...10:
  121. return Color.loopRed
  122. case ...20:
  123. return Color.orange
  124. default:
  125. return Color.loopGreen
  126. }
  127. }
  128. private var reservoirColor: Color {
  129. guard let reservoir = reservoir else {
  130. return .gray
  131. }
  132. switch reservoir {
  133. case ...10:
  134. return Color.loopRed
  135. case ...30:
  136. return Color.orange
  137. default:
  138. return Color.insulin
  139. }
  140. }
  141. private var timerColor: Color {
  142. guard let expisesAt = expiresAtDate else {
  143. return .gray
  144. }
  145. let time = expisesAt.timeIntervalSince(timerDate)
  146. switch time {
  147. case ...8.hours.timeInterval:
  148. return Color.loopRed
  149. case ...1.days.timeInterval:
  150. return Color.orange
  151. default:
  152. return Color.loopGreen
  153. }
  154. }
  155. }
  156. // #Preview("message") {
  157. // PumpView(
  158. // reservoir: .constant(Decimal(10.0)),
  159. // battery: .constant(nil),
  160. // name: .constant("Pump test"),
  161. // expiresAtDate: .constant(Date().addingTimeInterval(24.hours)),
  162. // timerDate: .constant(Date()),
  163. // pumpStatusHighlightMessage: .constant("⚠️\n Insulin suspended")
  164. // )
  165. // }
  166. //
  167. // #Preview("pump reservoir") {
  168. // PumpView(
  169. // reservoir: .constant(Decimal(40.0)),
  170. // battery: .constant(Battery(percent: 50, voltage: 2.0, string: BatteryState.normal, display: true)),
  171. // name: .constant("Pump test"),
  172. // expiresAtDate: .constant(nil),
  173. // timerDate: .constant(Date().addingTimeInterval(-24.hours)),
  174. // pumpStatusHighlightMessage: .constant(nil)
  175. // )
  176. // }
  177. //
  178. // #Preview("pump expiration") {
  179. // PumpView(
  180. // reservoir: .constant(Decimal(10.0)),
  181. // battery: .constant(Battery(percent: 50, voltage: 2.0, string: BatteryState.normal, display: false)),
  182. // name: .constant("Pump test"),
  183. // expiresAtDate: .constant(Date().addingTimeInterval(2.hours)),
  184. // timerDate: .constant(Date().addingTimeInterval(2.hours)),
  185. // pumpStatusHighlightMessage: .constant(nil)
  186. // )
  187. // }
  188. //
  189. // #Preview("no pump") {
  190. // PumpView(
  191. // reservoir: .constant(nil),
  192. // name: .constant(nil),
  193. // expiresAtDate: .constant(""),
  194. // timerDate: .constant(nil),
  195. // timeZone: .constant(Date()),
  196. // pumpStatusHighlightMessage: .constant(nil)
  197. // )
  198. // }