PumpView.swift 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. import CoreData
  2. import SwiftUI
  3. struct PumpView: View {
  4. @Binding var reservoir: Decimal?
  5. @Binding var name: String
  6. @Binding var expiresAtDate: Date?
  7. @Binding var timerDate: Date
  8. <<<<<<< HEAD
  9. @Binding var timeZone: TimeZone?
  10. @Binding var pumpStatusHighlightMessage: String?
  11. var battery: [OpenAPS_Battery]
  12. @Environment(\.colorScheme) var colorScheme
  13. =======
  14. @Binding var pumpStatusHighlightMessage: String?
  15. >>>>>>> 9672da256c317a314acc76d6e4f6e82cc174d133
  16. private var reservoirFormatter: NumberFormatter {
  17. let formatter = NumberFormatter()
  18. formatter.numberStyle = .decimal
  19. formatter.maximumFractionDigits = 0
  20. return formatter
  21. }
  22. private var batteryFormatter: NumberFormatter {
  23. let formatter = NumberFormatter()
  24. formatter.numberStyle = .percent
  25. return formatter
  26. }
  27. <<<<<<< HEAD
  28. private var numberFormatter: NumberFormatter {
  29. let formatter = NumberFormatter()
  30. formatter.numberStyle = .decimal
  31. formatter.maximumFractionDigits = 2
  32. return formatter
  33. }
  34. private var dateFormatter: DateFormatter {
  35. let dateFormatter = DateFormatter()
  36. dateFormatter.timeStyle = .short
  37. return dateFormatter
  38. }
  39. var body: some View {
  40. if let pumpStatusHighlightMessage = pumpStatusHighlightMessage { // display message instead pump info
  41. VStack(alignment: .center) {
  42. Text(pumpStatusHighlightMessage).font(.footnote).fontWeight(.bold)
  43. .multilineTextAlignment(.center).frame(maxWidth: /*@START_MENU_TOKEN@*/ .infinity/*@END_MENU_TOKEN@*/)
  44. }.frame(width: 100)
  45. } else {
  46. VStack(alignment: .leading, spacing: 20) {
  47. if reservoir == nil && battery.isEmpty {
  48. VStack(alignment: .center, spacing: 12) {
  49. HStack {
  50. Image(systemName: "keyboard.onehanded.left")
  51. .font(.body)
  52. .imageScale(.large)
  53. }
  54. HStack {
  55. Text("Add pump")
  56. .font(.caption)
  57. .bold()
  58. }
  59. }
  60. .frame(alignment: .top)
  61. }
  62. if let reservoir = reservoir {
  63. HStack {
  64. Image(systemName: "cross.vial.fill")
  65. .font(.system(size: 16))
  66. .foregroundColor(reservoirColor)
  67. if reservoir == 0xDEAD_BEEF {
  68. Text("50+ " + NSLocalizedString("U", comment: "Insulin unit"))
  69. .font(.system(size: 15, design: .rounded))
  70. } else {
  71. Text(
  72. reservoirFormatter
  73. .string(from: reservoir as NSNumber)! + NSLocalizedString(" U", comment: "Insulin unit")
  74. )
  75. .font(.system(size: 16, design: .rounded))
  76. }
  77. }
  78. if let timeZone = timeZone, timeZone.secondsFromGMT() != TimeZone.current.secondsFromGMT() {
  79. Image(systemName: "clock.badge.exclamationmark.fill")
  80. .font(.system(size: 16))
  81. .symbolRenderingMode(.palette)
  82. .foregroundStyle(.red, Color(.warning))
  83. }
  84. }
  85. if (battery.first?.display) != nil, expiresAtDate == nil {
  86. HStack {
  87. Image(systemName: "battery.100")
  88. .font(.system(size: 16))
  89. .foregroundColor(batteryColor)
  90. Text("\(Int(battery.first?.percent ?? 100)) %").font(.system(size: 16, design: .rounded))
  91. }
  92. =======
  93. var body: some View {
  94. if let pumpStatusHighlightMessage = pumpStatusHighlightMessage { // display message instead pump info
  95. VStack(alignment: .center) {
  96. Text(pumpStatusHighlightMessage).font(.footnote).fontWeight(.bold)
  97. .multilineTextAlignment(.center).frame(maxWidth: /*@START_MENU_TOKEN@*/ .infinity/*@END_MENU_TOKEN@*/)
  98. }.frame(width: 100)
  99. } else {
  100. VStack(alignment: .leading, spacing: 12) {
  101. if reservoir == nil && battery == nil {
  102. VStack(alignment: .center, spacing: 12) {
  103. HStack { // no cgm defined so display a generic CGM
  104. Image(systemName: "keyboard.onehanded.left").font(.body).imageScale(.large)
  105. }
  106. HStack {
  107. Text("Add pump").font(.caption).bold()
  108. }
  109. }.frame(alignment: .top)
  110. }
  111. if let reservoir = reservoir {
  112. HStack {
  113. Image(systemName: "drop.fill")
  114. .resizable()
  115. .aspectRatio(contentMode: .fit)
  116. .frame(maxHeight: 10)
  117. .foregroundColor(reservoirColor)
  118. if reservoir == 0xDEAD_BEEF {
  119. Text("50+ " + NSLocalizedString("U", comment: "Insulin unit")).font(.footnote)
  120. .fontWeight(.bold)
  121. } else {
  122. Text(
  123. reservoirFormatter
  124. .string(from: reservoir as NSNumber)! +
  125. NSLocalizedString(" U", comment: "Insulin unit")
  126. )
  127. .font(.footnote).fontWeight(.bold)
  128. }
  129. }.frame(alignment: .top)
  130. }
  131. if let battery = battery, battery.display ?? false, expiresAtDate == nil {
  132. HStack {
  133. Image(systemName: "battery.100")
  134. .resizable()
  135. .aspectRatio(contentMode: .fit)
  136. .frame(maxHeight: 10)
  137. .foregroundColor(batteryColor)
  138. Text("\(Int(battery.percent ?? 100)) %").font(.footnote)
  139. .fontWeight(.bold)
  140. }.frame(alignment: .bottom)
  141. >>>>>>> 9672da256c317a314acc76d6e4f6e82cc174d133
  142. }
  143. if let date = expiresAtDate {
  144. HStack {
  145. Image(systemName: "stopwatch.fill")
  146. <<<<<<< HEAD
  147. .font(.system(size: 16))
  148. .foregroundColor(timerColor)
  149. Text(remainingTimeString(time: date.timeIntervalSince(timerDate)))
  150. .font(.system(size: 16, design: .rounded))
  151. }
  152. =======
  153. .resizable()
  154. .aspectRatio(contentMode: .fit)
  155. .frame(maxHeight: 10)
  156. .foregroundColor(timerColor)
  157. Text(remainingTimeString(time: date.timeIntervalSince(timerDate))).font(.footnote)
  158. .fontWeight(.bold)
  159. }.frame(alignment: .bottom)
  160. >>>>>>> 9672da256c317a314acc76d6e4f6e82cc174d133
  161. }
  162. }
  163. }
  164. }
  165. private func remainingTimeString(time: TimeInterval) -> String {
  166. guard time > 0 else {
  167. return NSLocalizedString("Replace pod", comment: "View/Header when pod expired")
  168. }
  169. var time = time
  170. let days = Int(time / 1.days.timeInterval)
  171. time -= days.days.timeInterval
  172. let hours = Int(time / 1.hours.timeInterval)
  173. time -= hours.hours.timeInterval
  174. let minutes = Int(time / 1.minutes.timeInterval)
  175. if days >= 1 {
  176. return "\(days)" + NSLocalizedString("d", comment: "abbreviation for days") + " \(hours)" +
  177. NSLocalizedString("h", comment: "abbreviation for hours")
  178. }
  179. if hours >= 1 {
  180. return "\(hours)" + NSLocalizedString("h", comment: "abbreviation for hours")
  181. }
  182. return "\(minutes)" + NSLocalizedString("m", comment: "abbreviation for minutes")
  183. }
  184. private var batteryColor: Color {
  185. guard let battery = battery.first else {
  186. return .gray
  187. }
  188. switch battery.percent {
  189. case ...10:
  190. return .red
  191. case ...20:
  192. return .yellow
  193. default:
  194. return .green
  195. }
  196. }
  197. private var reservoirColor: Color {
  198. guard let reservoir = reservoir else {
  199. return .gray
  200. }
  201. switch reservoir {
  202. case ...10:
  203. return .red
  204. case ...30:
  205. return .yellow
  206. default:
  207. return .blue
  208. }
  209. }
  210. private var timerColor: Color {
  211. guard let expisesAt = expiresAtDate else {
  212. return .gray
  213. }
  214. let time = expisesAt.timeIntervalSince(timerDate)
  215. switch time {
  216. case ...8.hours.timeInterval:
  217. return .red
  218. case ...1.days.timeInterval:
  219. return .yellow
  220. default:
  221. return .green
  222. }
  223. }
  224. }
  225. <<<<<<< HEAD
  226. struct Hairline: View {
  227. let color: Color
  228. var body: some View {
  229. Rectangle()
  230. .fill(color)
  231. .frame(width: UIScreen.main.bounds.width / 1.3, height: 1)
  232. .opacity(0.5)
  233. }
  234. =======
  235. #Preview("message") {
  236. PumpView(
  237. reservoir: .constant(Decimal(10.0)),
  238. battery: .constant(nil),
  239. name: .constant("Pump test"),
  240. expiresAtDate: .constant(Date().addingTimeInterval(24.hours)),
  241. timerDate: .constant(Date()),
  242. pumpStatusHighlightMessage: .constant("⚠️\n Insulin suspended")
  243. )
  244. }
  245. #Preview("pump reservoir") {
  246. PumpView(
  247. reservoir: .constant(Decimal(40.0)),
  248. battery: .constant(Battery(percent: 50, voltage: 2.0, string: BatteryState.normal, display: true)),
  249. name: .constant("Pump test"),
  250. expiresAtDate: .constant(nil),
  251. timerDate: .constant(Date().addingTimeInterval(-24.hours)),
  252. pumpStatusHighlightMessage: .constant(nil)
  253. )
  254. }
  255. #Preview("pump expiration") {
  256. PumpView(
  257. reservoir: .constant(Decimal(10.0)),
  258. battery: .constant(Battery(percent: 50, voltage: 2.0, string: BatteryState.normal, display: false)),
  259. name: .constant("Pump test"),
  260. expiresAtDate: .constant(Date().addingTimeInterval(2.hours)),
  261. timerDate: .constant(Date().addingTimeInterval(2.hours)),
  262. pumpStatusHighlightMessage: .constant(nil)
  263. )
  264. }
  265. #Preview("no pump") {
  266. PumpView(
  267. reservoir: .constant(nil),
  268. battery: .constant(nil),
  269. name: .constant(""),
  270. expiresAtDate: .constant(nil),
  271. timerDate: .constant(Date()),
  272. pumpStatusHighlightMessage: .constant(nil)
  273. )
  274. >>>>>>> 9672da256c317a314acc76d6e4f6e82cc174d133
  275. }