AlternativeBolusCalcRootView.swift 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. import SwiftUI
  2. import Swinject
  3. extension Bolus {
  4. // alternative bolus calc
  5. struct AlternativeBolusCalcRootView: BaseView {
  6. let resolver: Resolver
  7. let waitForSuggestion: Bool
  8. @ObservedObject var state: StateModel
  9. @State private var showInfo = false
  10. @State var insulinCalculated: Decimal = 0
  11. @Environment(\.colorScheme) var colorScheme
  12. private var formatter: NumberFormatter {
  13. let formatter = NumberFormatter()
  14. formatter.numberStyle = .decimal
  15. formatter.maximumFractionDigits = 2
  16. return formatter
  17. }
  18. private var fractionDigits: Int {
  19. if state.units == .mmolL {
  20. return 1
  21. } else { return 0 }
  22. }
  23. var body: some View {
  24. Form {
  25. Section {
  26. HStack {
  27. Text("Glucose")
  28. DecimalTextField(
  29. "0",
  30. value: Binding(
  31. get: {
  32. if state.units == .mmolL {
  33. return state.currentBG * 0.0555
  34. } else {
  35. return state.currentBG
  36. }
  37. },
  38. set: { newValue in
  39. if state.units == .mmolL {
  40. state.currentBG = newValue * 0.0555
  41. } else {
  42. state.currentBG = newValue
  43. }
  44. }
  45. ),
  46. formatter: formatter,
  47. autofocus: false,
  48. cleanInput: true
  49. )
  50. .onChange(of: state.currentBG) { newValue in
  51. if newValue > 500 {
  52. state.currentBG = 500 // ensure that user can not input more than 500 mg/dL
  53. }
  54. insulinCalculated = state.calculateInsulin()
  55. }
  56. Text(state.units.rawValue)
  57. .foregroundColor(.secondary)
  58. }
  59. .contentShape(Rectangle())
  60. HStack {
  61. Button(action: {
  62. showInfo.toggle()
  63. insulinCalculated = state.calculateInsulin()
  64. }, label: {
  65. Image(systemName: "info.circle")
  66. Text("Calculations")
  67. })
  68. .foregroundStyle(.blue)
  69. .font(.footnote)
  70. .buttonStyle(PlainButtonStyle())
  71. .frame(maxWidth: .infinity, alignment: .leading)
  72. if state.fattyMeals {
  73. Spacer()
  74. Toggle(isOn: $state.useFattyMealCorrectionFactor) {
  75. Text("Fatty Meal")
  76. }
  77. .toggleStyle(CheckboxToggleStyle())
  78. .font(.footnote)
  79. .onChange(of: state.useFattyMealCorrectionFactor) { _ in
  80. insulinCalculated = state.calculateInsulin()
  81. }
  82. }
  83. }
  84. }
  85. header: { Text("Values") }
  86. Section {
  87. HStack {
  88. Text("Recommended Bolus")
  89. Spacer()
  90. Text(
  91. formatter
  92. .string(from: Double(insulinCalculated) as NSNumber)!
  93. )
  94. let unit = NSLocalizedString(
  95. " U",
  96. comment: "Unit in number of units delivered (keep the space character!)"
  97. )
  98. Text(unit).foregroundColor(.secondary)
  99. }.contentShape(Rectangle())
  100. .onTapGesture {
  101. state.amount = insulinCalculated
  102. }
  103. if !state.waitForSuggestion {
  104. HStack {
  105. Text("Bolus")
  106. Spacer()
  107. DecimalTextField(
  108. "0",
  109. value: $state.amount,
  110. formatter: formatter,
  111. autofocus: false,
  112. cleanInput: true
  113. )
  114. Text(!(state.amount > state.maxBolus) ? "U" : "😵").foregroundColor(.secondary)
  115. }
  116. }
  117. }
  118. header: { Text("Bolus") }
  119. Section {
  120. Button(action: {
  121. state.add()
  122. }) {
  123. Text(!(state.amount > state.maxBolus) ? "Enact bolus" : "Max Bolus exceeded!")
  124. .frame(maxWidth: .infinity, alignment: .center)
  125. }
  126. .disabled(
  127. state.amount <= 0 || state.amount > state.maxBolus
  128. )
  129. }
  130. .onAppear {
  131. configureView {
  132. state.waitForSuggestionInitial = waitForSuggestion
  133. state.waitForSuggestion = waitForSuggestion
  134. }
  135. }
  136. .navigationTitle("Enact Bolus")
  137. .navigationBarTitleDisplayMode(.inline)
  138. .navigationBarItems(leading: Button("Close", action: state.hideModal))
  139. }
  140. .blur(radius: showInfo ? 3 : 0)
  141. .popup(isPresented: showInfo) {
  142. bolusInfoAlternativeCalculator
  143. }
  144. }
  145. // calculation showed in popup
  146. var bolusInfoAlternativeCalculator: some View {
  147. let unit = NSLocalizedString(
  148. " U",
  149. comment: "Unit in number of units delivered (keep the space character!)"
  150. )
  151. return VStack {
  152. VStack {
  153. VStack {
  154. HStack {
  155. Text("Calculations")
  156. .font(.title3)
  157. .fontWeight(.semibold)
  158. Spacer()
  159. }
  160. .padding(.vertical, 10)
  161. HStack {
  162. Text("Carb Ratio")
  163. .foregroundColor(.secondary)
  164. Spacer()
  165. Text(state.carbRatio.formatted())
  166. Text(NSLocalizedString(" g/U", comment: " grams per Unit"))
  167. .foregroundColor(.secondary)
  168. }
  169. HStack {
  170. Text("ISF")
  171. .foregroundColor(.secondary)
  172. Spacer()
  173. let isf = state.isf
  174. Text(isf.formatted())
  175. Text(state.units.rawValue + NSLocalizedString("/U", comment: "/Insulin unit"))
  176. .foregroundColor(.secondary)
  177. }
  178. HStack {
  179. Text("Target Glucose")
  180. .foregroundColor(.secondary)
  181. Spacer()
  182. let target = state.units == .mmolL ? state.target.asMmolL : state.target
  183. Text(target.formatted(.number.grouping(.never).rounded().precision(.fractionLength(fractionDigits))))
  184. Text(state.units.rawValue)
  185. .foregroundColor(.secondary)
  186. }
  187. HStack {
  188. Text("Basal")
  189. .foregroundColor(.secondary)
  190. Spacer()
  191. let basal = state.basal
  192. Text(basal.formatted())
  193. Text(NSLocalizedString(" U/h", comment: " Units per hour"))
  194. .foregroundColor(.secondary)
  195. }
  196. HStack {
  197. Text("Fraction")
  198. .foregroundColor(.secondary)
  199. Spacer()
  200. let fraction = state.fraction
  201. Text(fraction.formatted())
  202. }
  203. if state.useFattyMealCorrectionFactor {
  204. HStack {
  205. Text("Fatty Meal Factor")
  206. .foregroundColor(.orange)
  207. Spacer()
  208. let fraction = state.fattyMealFactor
  209. Text(fraction.formatted())
  210. .foregroundColor(.orange)
  211. }
  212. }
  213. }
  214. .padding()
  215. VStack {
  216. HStack {
  217. Text("Glucose")
  218. .foregroundColor(.secondary)
  219. Spacer()
  220. let glucose = state.units == .mmolL ? state.currentBG.asMmolL : state.currentBG
  221. Text(glucose.formatted(.number.grouping(.never).rounded().precision(.fractionLength(fractionDigits))))
  222. Text(state.units.rawValue)
  223. .foregroundColor(.secondary)
  224. Spacer()
  225. Image(systemName: "arrow.right")
  226. Spacer()
  227. let targetDifferenceInsulin = state.targetDifferenceInsulin
  228. // rounding
  229. let targetDifferenceInsulinAsDouble = NSDecimalNumber(decimal: targetDifferenceInsulin).doubleValue
  230. let roundedTargetDifferenceInsulin = Decimal(round(100 * targetDifferenceInsulinAsDouble) / 100)
  231. Text(roundedTargetDifferenceInsulin.formatted())
  232. Text(unit)
  233. .foregroundColor(.secondary)
  234. }
  235. HStack {
  236. Text("IOB")
  237. .foregroundColor(.secondary)
  238. Spacer()
  239. let iob = state.iob
  240. // rounding
  241. let iobAsDouble = NSDecimalNumber(decimal: iob).doubleValue
  242. let roundedIob = Decimal(round(100 * iobAsDouble) / 100)
  243. Text(roundedIob.formatted())
  244. Text(unit)
  245. .foregroundColor(.secondary)
  246. Spacer()
  247. Image(systemName: "arrow.right")
  248. Spacer()
  249. let iobCalc = state.iobInsulinReduction
  250. // rounding
  251. let iobCalcAsDouble = NSDecimalNumber(decimal: iobCalc).doubleValue
  252. let roundedIobCalc = Decimal(round(100 * iobCalcAsDouble) / 100)
  253. Text(roundedIobCalc.formatted())
  254. Text(unit).foregroundColor(.secondary)
  255. }
  256. HStack {
  257. Text("Trend")
  258. .foregroundColor(.secondary)
  259. Spacer()
  260. let trend = state.units == .mmolL ? state.deltaBG.asMmolL : state.deltaBG
  261. Text(trend.formatted(.number.grouping(.never).rounded().precision(.fractionLength(fractionDigits))))
  262. Text(state.units.rawValue).foregroundColor(.secondary)
  263. Spacer()
  264. Image(systemName: "arrow.right")
  265. Spacer()
  266. let trendInsulin = state.fifteenMinInsulin
  267. // rounding
  268. let trendInsulinAsDouble = NSDecimalNumber(decimal: trendInsulin).doubleValue
  269. let roundedTrendInsulin = Decimal(round(100 * trendInsulinAsDouble) / 100)
  270. Text(roundedTrendInsulin.formatted())
  271. Text(unit)
  272. .foregroundColor(.secondary)
  273. }
  274. HStack {
  275. Text("COB")
  276. .foregroundColor(.secondary)
  277. Spacer()
  278. let cob = state.cob
  279. Text(cob.formatted())
  280. let unitGrams = NSLocalizedString(" g", comment: "grams")
  281. Text(unitGrams).foregroundColor(.secondary)
  282. Spacer()
  283. Image(systemName: "arrow.right")
  284. Spacer()
  285. let insulinCob = state.wholeCobInsulin
  286. // rounding
  287. let insulinCobAsDouble = NSDecimalNumber(decimal: insulinCob).doubleValue
  288. let roundedInsulinCob = Decimal(round(100 * insulinCobAsDouble) / 100)
  289. Text(roundedInsulinCob.formatted())
  290. Text(unit)
  291. .foregroundColor(.secondary)
  292. }
  293. }
  294. .padding()
  295. Divider()
  296. .fontWeight(.bold)
  297. HStack {
  298. Text("Full Bolus")
  299. .foregroundColor(.secondary)
  300. Spacer()
  301. let insulin = state.roundedWholeCalc
  302. Text(insulin.formatted()).foregroundStyle(state.roundedWholeCalc < 0 ? Color.loopRed : Color.primary)
  303. Text(unit)
  304. .foregroundColor(.secondary)
  305. }
  306. .padding()
  307. Divider()
  308. .fontWeight(.bold)
  309. HStack {
  310. Text("Result")
  311. .fontWeight(.bold)
  312. Spacer()
  313. let fraction = state.fraction
  314. Text(fraction.formatted())
  315. Text(" x ")
  316. .foregroundColor(.secondary)
  317. // if fatty meal is chosen
  318. if state.useFattyMealCorrectionFactor {
  319. let fattyMealFactor = state.fattyMealFactor
  320. Text(fattyMealFactor.formatted())
  321. .foregroundColor(.orange)
  322. Text(" x ")
  323. .foregroundColor(.secondary)
  324. }
  325. let insulin = state.roundedWholeCalc
  326. Text(insulin.formatted()).foregroundStyle(state.roundedWholeCalc < 0 ? Color.loopRed : Color.primary)
  327. Text(unit)
  328. .foregroundColor(.secondary)
  329. Text(" = ")
  330. .foregroundColor(.secondary)
  331. let result = state.insulinCalculated
  332. // rounding
  333. let resultAsDouble = NSDecimalNumber(decimal: result).doubleValue
  334. let roundedResult = Decimal(round(100 * resultAsDouble) / 100)
  335. Text(roundedResult.formatted())
  336. .fontWeight(.bold)
  337. .font(.system(size: 16))
  338. .foregroundColor(.blue)
  339. Text(unit)
  340. .foregroundColor(.secondary)
  341. }
  342. .padding()
  343. }
  344. .padding(.top, 10)
  345. .padding(.bottom, 15)
  346. // Hide button
  347. VStack {
  348. Button { showInfo = false }
  349. label: {
  350. Text("OK")
  351. }
  352. .frame(maxWidth: .infinity, alignment: .center)
  353. .font(.system(size: 16))
  354. .fontWeight(.semibold)
  355. .foregroundColor(.blue)
  356. }
  357. .padding(.bottom, 20)
  358. }
  359. .font(.footnote)
  360. .background(
  361. RoundedRectangle(cornerRadius: 10, style: .continuous)
  362. .fill(Color(colorScheme == .dark ? UIColor.systemGray4 : UIColor.systemGray4).opacity(0.9))
  363. )
  364. }
  365. }
  366. }