AlternativeBolusCalcRootView.swift 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749
  1. import Charts
  2. import CoreData
  3. import SwiftUI
  4. import Swinject
  5. extension Bolus {
  6. struct AlternativeBolusCalcRootView: BaseView {
  7. let resolver: Resolver
  8. let waitForSuggestion: Bool
  9. let fetch: Bool
  10. @StateObject var state: StateModel
  11. @State private var showInfo = false
  12. @State private var exceededMaxBolus = false
  13. @State private var keepForNextWiew: Bool = false
  14. @State private var calculatorDetent = PresentationDetent.medium
  15. private enum Config {
  16. static let dividerHeight: CGFloat = 2
  17. static let spacing: CGFloat = 3
  18. }
  19. @Environment(\.colorScheme) var colorScheme
  20. @FetchRequest(
  21. entity: Meals.entity(),
  22. sortDescriptors: [NSSortDescriptor(key: "createdAt", ascending: false)]
  23. ) var meal: FetchedResults<Meals>
  24. private var formatter: NumberFormatter {
  25. let formatter = NumberFormatter()
  26. formatter.numberStyle = .decimal
  27. formatter.maximumFractionDigits = 2
  28. return formatter
  29. }
  30. private var mealFormatter: NumberFormatter {
  31. let formatter = NumberFormatter()
  32. formatter.numberStyle = .decimal
  33. formatter.maximumFractionDigits = 1
  34. return formatter
  35. }
  36. private var gluoseFormatter: NumberFormatter {
  37. let formatter = NumberFormatter()
  38. formatter.numberStyle = .decimal
  39. if state.units == .mmolL {
  40. formatter.maximumFractionDigits = 1
  41. } else { formatter.maximumFractionDigits = 0 }
  42. return formatter
  43. }
  44. private var fractionDigits: Int {
  45. if state.units == .mmolL {
  46. return 1
  47. } else { return 0 }
  48. }
  49. var body: some View {
  50. Form {
  51. Section {
  52. if state.waitForSuggestion {
  53. Text("Please wait")
  54. } else {
  55. predictionChart
  56. }
  57. } header: { Text("Predictions") }
  58. Section {}
  59. if fetch {
  60. Section {
  61. mealEntries
  62. } header: { Text("Meal Summary") }
  63. }
  64. Section {
  65. HStack {
  66. Button(action: {
  67. showInfo.toggle()
  68. }, label: {
  69. Image(systemName: "info.circle")
  70. Text("Calculations")
  71. })
  72. .foregroundStyle(.blue)
  73. .font(.footnote)
  74. .buttonStyle(PlainButtonStyle())
  75. .frame(maxWidth: .infinity, alignment: .leading)
  76. if state.fattyMeals {
  77. Spacer()
  78. Toggle(isOn: $state.useFattyMealCorrectionFactor) {
  79. Text("Fatty Meal")
  80. }
  81. .toggleStyle(CheckboxToggleStyle())
  82. .font(.footnote)
  83. .onChange(of: state.useFattyMealCorrectionFactor) { _ in
  84. state.insulinCalculated = state.calculateInsulin()
  85. if state.useFattyMealCorrectionFactor {
  86. state.useSuperBolus = false
  87. }
  88. }
  89. }
  90. if state.sweetMeals {
  91. Spacer()
  92. Toggle(isOn: $state.useSuperBolus) {
  93. Text("Super Bolus")
  94. }
  95. .toggleStyle(CheckboxToggleStyle())
  96. .font(.footnote)
  97. .onChange(of: state.useSuperBolus) { _ in
  98. state.insulinCalculated = state.calculateInsulin()
  99. if state.useSuperBolus {
  100. state.useFattyMealCorrectionFactor = false
  101. }
  102. }
  103. }
  104. }
  105. if state.waitForSuggestion {
  106. HStack {
  107. Text("Wait please").foregroundColor(.secondary)
  108. Spacer()
  109. ActivityIndicator(isAnimating: .constant(true), style: .medium) // fix iOS 15 bug
  110. }
  111. } else {
  112. HStack {
  113. Text("Recommended Bolus")
  114. Spacer()
  115. Text(
  116. formatter
  117. .string(from: Double(state.insulinCalculated) as NSNumber) ?? ""
  118. )
  119. Text(
  120. NSLocalizedString(" U", comment: "Unit in number of units delivered (keep the space character!)")
  121. ).foregroundColor(.secondary)
  122. }.contentShape(Rectangle())
  123. .onTapGesture { state.amount = state.insulinCalculated }
  124. }
  125. HStack {
  126. Text("Bolus")
  127. Spacer()
  128. DecimalTextField(
  129. "0",
  130. value: $state.amount,
  131. formatter: formatter,
  132. autofocus: false,
  133. cleanInput: true
  134. )
  135. Text(exceededMaxBolus ? "😵" : " U").foregroundColor(.secondary)
  136. }
  137. .onChange(of: state.amount) { newValue in
  138. if newValue > state.maxBolus {
  139. exceededMaxBolus = true
  140. } else {
  141. exceededMaxBolus = false
  142. }
  143. }
  144. } header: { Text("Bolus") }
  145. if state.amount > 0 {
  146. Section {
  147. Button {
  148. keepForNextWiew = true
  149. state.add()
  150. }
  151. label: { Text(exceededMaxBolus ? "Max Bolus exceeded!" : "Enact bolus") }
  152. .frame(maxWidth: .infinity, alignment: .center)
  153. .disabled(disabled)
  154. .listRowBackground(!disabled ? Color(.systemBlue) : Color(.systemGray4))
  155. .tint(.white)
  156. }
  157. }
  158. if state.amount <= 0 {
  159. Section {
  160. Button {
  161. keepForNextWiew = true
  162. state.showModal(for: nil)
  163. }
  164. label: { Text("Continue without bolus") }.frame(maxWidth: .infinity, alignment: .center)
  165. }
  166. }
  167. }
  168. .blur(radius: showInfo ? 3 : 0)
  169. .navigationTitle("Enact Bolus")
  170. .navigationBarTitleDisplayMode(.inline)
  171. .navigationBarItems(
  172. leading: Button {
  173. carbsView()
  174. }
  175. label: {
  176. HStack {
  177. Image(systemName: "chevron.backward")
  178. Text("Meal")
  179. }
  180. },
  181. trailing: Button { state.hideModal() }
  182. label: { Text("Close") }
  183. )
  184. .onAppear {
  185. configureView {
  186. state.waitForSuggestionInitial = waitForSuggestion
  187. state.waitForSuggestion = waitForSuggestion
  188. state.insulinCalculated = state.calculateInsulin()
  189. }
  190. }
  191. .onDisappear {
  192. if fetch, hasFatOrProtein, !keepForNextWiew, state.useCalc {
  193. state.delete(deleteTwice: true, meal: meal)
  194. } else if fetch, !keepForNextWiew, state.useCalc {
  195. state.delete(deleteTwice: false, meal: meal)
  196. }
  197. }
  198. .sheet(isPresented: $showInfo) {
  199. calculationsDetailView
  200. .presentationDetents(
  201. [fetch ? .large : .fraction(0.9), .large],
  202. selection: $calculatorDetent
  203. )
  204. }
  205. }
  206. var predictionChart: some View {
  207. ZStack {
  208. PredictionView(
  209. predictions: $state.predictions, units: $state.units, eventualBG: $state.evBG, target: $state.target,
  210. displayPredictions: $state.displayPredictions
  211. )
  212. }
  213. }
  214. var calcSettingsFirstRow: some View {
  215. GridRow {
  216. Group {
  217. Text("Carb Ratio:")
  218. .foregroundColor(.secondary)
  219. }.gridCellAnchor(.leading)
  220. Group {
  221. Text("ISF:")
  222. .foregroundColor(.secondary)
  223. }.gridCellAnchor(.leading)
  224. VStack {
  225. Text("Target:")
  226. .foregroundColor(.secondary)
  227. }.gridCellAnchor(.leading)
  228. }
  229. }
  230. var calcSettingsSecondRow: some View {
  231. GridRow {
  232. Text(state.carbRatio.formatted() + " " + NSLocalizedString("g/U", comment: " grams per Unit"))
  233. .gridCellAnchor(.leading)
  234. Text(
  235. state.isf.formatted() + " " + state.units
  236. .rawValue + NSLocalizedString("/U", comment: "/Insulin unit")
  237. ).gridCellAnchor(.leading)
  238. let target = state.units == .mmolL ? state.target.asMmolL : state.target
  239. Text(
  240. target
  241. .formatted(.number.grouping(.never).rounded().precision(.fractionLength(fractionDigits))) +
  242. " " + state.units.rawValue
  243. ).gridCellAnchor(.leading)
  244. }
  245. }
  246. var calcGlucoseFirstRow: some View {
  247. GridRow(alignment: .center) {
  248. let currentBG = state.units == .mmolL ? state.currentBG.asMmolL : state.currentBG
  249. let target = state.units == .mmolL ? state.target.asMmolL : state.target
  250. Text("Glucose:").foregroundColor(.secondary)
  251. let firstRow = currentBG
  252. .formatted(.number.grouping(.never).rounded().precision(.fractionLength(fractionDigits)))
  253. + " - " +
  254. target
  255. .formatted(.number.grouping(.never).rounded().precision(.fractionLength(fractionDigits)))
  256. + " = " +
  257. state.targetDifference
  258. .formatted(.number.grouping(.never).rounded().precision(.fractionLength(fractionDigits)))
  259. Text(firstRow).frame(minWidth: 0, alignment: .leading).foregroundColor(.secondary)
  260. .gridColumnAlignment(.leading)
  261. HStack {
  262. Text(
  263. self.insulinRounder(state.targetDifferenceInsulin).formatted()
  264. )
  265. Text("U").foregroundColor(.secondary)
  266. }.fontWeight(.bold)
  267. .gridColumnAlignment(.trailing)
  268. }
  269. }
  270. var calcGlucoseSecondRow: some View {
  271. GridRow(alignment: .center) {
  272. let currentBG = state.units == .mmolL ? state.currentBG.asMmolL : state.currentBG
  273. Text(
  274. currentBG
  275. .formatted(.number.grouping(.never).rounded().precision(.fractionLength(fractionDigits))) +
  276. " " +
  277. state.units.rawValue
  278. )
  279. let secondRow = state.targetDifference
  280. .formatted(
  281. .number.grouping(.never).rounded()
  282. .precision(.fractionLength(fractionDigits))
  283. )
  284. + " / " +
  285. state.isf.formatted()
  286. + " ≈ " +
  287. self.insulinRounder(state.targetDifferenceInsulin).formatted()
  288. Text(secondRow).foregroundColor(.secondary).gridColumnAlignment(.leading)
  289. Color.clear.gridCellUnsizedAxes([.horizontal, .vertical])
  290. }
  291. }
  292. var calcGlucoseFormulaRow: some View {
  293. GridRow(alignment: .top) {
  294. Color.clear.gridCellUnsizedAxes([.horizontal, .vertical])
  295. Text("(Current - Target) / ISF").foregroundColor(.secondary.opacity(colorScheme == .dark ? 0.65 : 0.8))
  296. .gridColumnAlignment(.leading)
  297. .gridCellColumns(2)
  298. }
  299. .font(.caption)
  300. }
  301. var calcIOBRow: some View {
  302. GridRow(alignment: .center) {
  303. HStack {
  304. Text("IOB:").foregroundColor(.secondary)
  305. Text(
  306. self.insulinRounder(state.iob).formatted()
  307. )
  308. }
  309. Text("Subtract IOB").foregroundColor(.secondary.opacity(colorScheme == .dark ? 0.65 : 0.8)).font(.footnote)
  310. let iobFormatted = self.insulinRounder(state.iob).formatted()
  311. HStack {
  312. Text((state.iob != 0 ? "-" : "") + (state.iob >= 0 ? iobFormatted : "(" + iobFormatted + ")"))
  313. Text("U").foregroundColor(.secondary)
  314. }.fontWeight(.bold)
  315. .gridColumnAlignment(.trailing)
  316. }
  317. }
  318. var calcCOBRow: some View {
  319. GridRow(alignment: .center) {
  320. HStack {
  321. Text("COB:").foregroundColor(.secondary)
  322. Text(
  323. state.cob
  324. .formatted(.number.grouping(.never).rounded().precision(.fractionLength(fractionDigits))) +
  325. NSLocalizedString(" g", comment: "grams")
  326. )
  327. }
  328. Text(
  329. state.cob
  330. .formatted(.number.grouping(.never).rounded().precision(.fractionLength(fractionDigits)))
  331. + " / " +
  332. state.carbRatio.formatted()
  333. + " ≈ " +
  334. self.insulinRounder(state.wholeCobInsulin).formatted()
  335. )
  336. .foregroundColor(.secondary)
  337. .gridColumnAlignment(.leading)
  338. HStack {
  339. Text(
  340. self.insulinRounder(state.wholeCobInsulin).formatted()
  341. )
  342. Text("U").foregroundColor(.secondary)
  343. }.fontWeight(.bold)
  344. .gridColumnAlignment(.trailing)
  345. }
  346. }
  347. var calcCOBFormulaRow: some View {
  348. GridRow(alignment: .center) {
  349. Color.clear.gridCellUnsizedAxes([.horizontal, .vertical])
  350. Text("COB / Carb Ratio").foregroundColor(.secondary.opacity(colorScheme == .dark ? 0.65 : 0.8))
  351. .gridColumnAlignment(.leading)
  352. .gridCellColumns(2)
  353. }
  354. .font(.caption)
  355. }
  356. var calcDeltaRow: some View {
  357. GridRow(alignment: .center) {
  358. Text("Delta:").foregroundColor(.secondary)
  359. let deltaBG = state.units == .mmolL ? state.deltaBG.asMmolL : state.deltaBG
  360. Text(
  361. deltaBG
  362. .formatted(
  363. .number.grouping(.never).rounded()
  364. .precision(.fractionLength(fractionDigits))
  365. )
  366. + " / " +
  367. state.isf.formatted()
  368. + " ≈ " +
  369. self.insulinRounder(state.fifteenMinInsulin).formatted()
  370. )
  371. .foregroundColor(.secondary)
  372. .gridColumnAlignment(.leading)
  373. HStack {
  374. Text(
  375. self.insulinRounder(state.fifteenMinInsulin).formatted()
  376. )
  377. Text("U").foregroundColor(.secondary)
  378. }.fontWeight(.bold)
  379. .gridColumnAlignment(.trailing)
  380. }
  381. }
  382. var calcDeltaFormulaRow: some View {
  383. GridRow(alignment: .center) {
  384. let deltaBG = state.units == .mmolL ? state.deltaBG.asMmolL : state.deltaBG
  385. Text(
  386. deltaBG
  387. .formatted(
  388. .number.grouping(.never).rounded()
  389. .precision(.fractionLength(fractionDigits))
  390. ) + " " +
  391. state.units.rawValue
  392. )
  393. Text("15min Delta / ISF").font(.caption).foregroundColor(.secondary.opacity(colorScheme == .dark ? 0.65 : 0.8))
  394. .gridColumnAlignment(.leading)
  395. .gridCellColumns(2).padding(.top, 5)
  396. }
  397. }
  398. var calcFullBolusRow: some View {
  399. GridRow(alignment: .center) {
  400. Text("Full Bolus")
  401. .foregroundColor(.secondary)
  402. Color.clear.gridCellUnsizedAxes([.horizontal, .vertical])
  403. HStack {
  404. Text(self.insulinRounder(state.wholeCalc).formatted())
  405. .foregroundStyle(state.wholeCalc < 0 ? Color.loopRed : Color.primary)
  406. Text("U").foregroundColor(.secondary)
  407. }.gridColumnAlignment(.trailing)
  408. .fontWeight(.bold)
  409. }
  410. }
  411. var calcSuperBolusRow: some View {
  412. GridRow(alignment: .center) {
  413. Text("Super Bolus")
  414. .foregroundColor(.secondary)
  415. Text("Added to Result").foregroundColor(.secondary.opacity(colorScheme == .dark ? 0.65 : 0.8)).font(.footnote)
  416. HStack {
  417. Text("+" + self.insulinRounder(state.superBolusInsulin).formatted())
  418. .foregroundStyle(Color.loopRed)
  419. Text("U").foregroundColor(.secondary)
  420. }.gridColumnAlignment(.trailing)
  421. .fontWeight(.bold)
  422. }
  423. }
  424. var calcResultRow: some View {
  425. GridRow(alignment: .center) {
  426. Text("Result").fontWeight(.bold)
  427. HStack {
  428. Text(state.useSuperBolus ? "(" : "")
  429. .foregroundColor(.loopRed)
  430. + Text(state.fraction.formatted())
  431. + Text(" x ")
  432. .foregroundColor(.secondary)
  433. // if fatty meal is chosen
  434. + Text(state.useFattyMealCorrectionFactor ? state.fattyMealFactor.formatted() : "")
  435. .foregroundColor(.orange)
  436. + Text(state.useFattyMealCorrectionFactor ? " x " : "")
  437. .foregroundColor(.secondary)
  438. // endif fatty meal is chosen
  439. + Text(self.insulinRounder(state.wholeCalc).formatted())
  440. .foregroundColor(state.wholeCalc < 0 ? Color.loopRed : Color.primary)
  441. // if superbolus is chosen
  442. + Text(state.useSuperBolus ? ")" : "")
  443. .foregroundColor(.loopRed)
  444. + Text(state.useSuperBolus ? " + " : "")
  445. .foregroundColor(.secondary)
  446. + Text(state.useSuperBolus ? state.superBolusInsulin.formatted() : "")
  447. .foregroundColor(.loopRed)
  448. // endif superbolus is chosen
  449. + Text(" ≈ ")
  450. .foregroundColor(.secondary)
  451. }
  452. .gridColumnAlignment(.leading)
  453. HStack {
  454. Text(self.insulinRounder(state.insulinCalculated).formatted())
  455. .fontWeight(.bold)
  456. .foregroundColor(.blue)
  457. Text("U").foregroundColor(.secondary)
  458. }
  459. .gridColumnAlignment(.trailing)
  460. .fontWeight(.bold)
  461. }
  462. }
  463. var calcResultFormulaRow: some View {
  464. GridRow(alignment: .bottom) {
  465. if state.useFattyMealCorrectionFactor {
  466. Text("Factor x Fatty Meal Factor x Full Bolus")
  467. .foregroundColor(.secondary.opacity(colorScheme == .dark ? 0.65 : 0.8))
  468. .font(.caption)
  469. .gridCellAnchor(.center)
  470. .gridCellColumns(3)
  471. } else if state.useSuperBolus {
  472. Text("(Factor x Full Bolus) + Super Bolus")
  473. .foregroundColor(.secondary.opacity(colorScheme == .dark ? 0.65 : 0.8))
  474. .font(.caption)
  475. .gridCellAnchor(.center)
  476. .gridCellColumns(3)
  477. } else {
  478. Color.clear.gridCellUnsizedAxes([.horizontal, .vertical])
  479. Text("Factor x Full Bolus")
  480. .foregroundColor(.secondary.opacity(colorScheme == .dark ? 0.65 : 0.8))
  481. .font(.caption)
  482. .padding(.top, 5)
  483. .gridCellAnchor(.leading)
  484. .gridCellColumns(2)
  485. }
  486. }
  487. }
  488. var calculationsDetailView: some View {
  489. NavigationStack {
  490. ScrollView {
  491. Grid(alignment: .topLeading, horizontalSpacing: 3, verticalSpacing: 0) {
  492. GridRow {
  493. Text("Calculations").fontWeight(.bold).gridCellColumns(3).gridCellAnchor(.center).padding(.vertical)
  494. }
  495. calcSettingsFirstRow
  496. calcSettingsSecondRow
  497. DividerCustom()
  498. if fetch {
  499. // meal entries as grid rows
  500. GridRow {
  501. if let carbs = meal.first?.carbs, carbs > 0 {
  502. Text("Carbs").foregroundColor(.secondary)
  503. Color.clear.gridCellUnsizedAxes([.horizontal, .vertical])
  504. HStack {
  505. Text(carbs.formatted())
  506. Text("g").foregroundColor(.secondary)
  507. }.gridCellAnchor(.trailing)
  508. }
  509. }
  510. GridRow {
  511. if let fat = meal.first?.fat, fat > 0 {
  512. Text("Fat").foregroundColor(.secondary)
  513. Color.clear.gridCellUnsizedAxes([.horizontal, .vertical])
  514. HStack {
  515. Text(fat.formatted())
  516. Text("g").foregroundColor(.secondary)
  517. }.gridCellAnchor(.trailing)
  518. }
  519. }
  520. GridRow {
  521. if let protein = meal.first?.protein, protein > 0 {
  522. Text("Protein").foregroundColor(.secondary)
  523. Color.clear.gridCellUnsizedAxes([.horizontal, .vertical])
  524. HStack {
  525. Text(protein.formatted())
  526. Text("g").foregroundColor(.secondary)
  527. }.gridCellAnchor(.trailing)
  528. }
  529. }
  530. GridRow {
  531. if let note = meal.first?.note, note != "" {
  532. Text("Note").foregroundColor(.secondary)
  533. Text(note).foregroundColor(.secondary).gridCellColumns(2).gridCellAnchor(.trailing)
  534. }
  535. }
  536. DividerCustom()
  537. }
  538. GridRow {
  539. Text("Detailed Calculation Steps").gridCellColumns(3).gridCellAnchor(.center)
  540. .padding(.bottom, 10)
  541. }
  542. calcGlucoseFirstRow
  543. calcGlucoseSecondRow.padding(.bottom, 5)
  544. calcGlucoseFormulaRow
  545. DividerCustom()
  546. calcIOBRow
  547. DividerCustom()
  548. calcCOBRow.padding(.bottom, 5)
  549. calcCOBFormulaRow
  550. DividerCustom()
  551. calcDeltaRow
  552. calcDeltaFormulaRow
  553. DividerCustom()
  554. calcFullBolusRow
  555. if state.useSuperBolus {
  556. DividerCustom()
  557. calcSuperBolusRow
  558. }
  559. DividerDouble()
  560. calcResultRow
  561. calcResultFormulaRow
  562. }
  563. Spacer()
  564. Button { showInfo = false }
  565. label: { Text("Got it!").frame(maxWidth: .infinity, alignment: .center) }
  566. .buttonStyle(.bordered)
  567. .padding(.top)
  568. }
  569. .padding([.horizontal, .bottom])
  570. .font(.system(size: 15))
  571. }
  572. }
  573. private func insulinRounder(_ value: Decimal) -> Decimal {
  574. let toRound = NSDecimalNumber(decimal: value).doubleValue
  575. return Decimal(floor(100 * toRound) / 100)
  576. }
  577. private var disabled: Bool {
  578. state.amount <= 0 || state.amount > state.maxBolus
  579. }
  580. var changed: Bool {
  581. ((meal.first?.carbs ?? 0) > 0) || ((meal.first?.fat ?? 0) > 0) || ((meal.first?.protein ?? 0) > 0)
  582. }
  583. var hasFatOrProtein: Bool {
  584. ((meal.first?.fat ?? 0) > 0) || ((meal.first?.protein ?? 0) > 0)
  585. }
  586. func carbsView() {
  587. if fetch {
  588. keepForNextWiew = true
  589. state.backToCarbsView(complexEntry: true, meal, override: false)
  590. } else {
  591. state.backToCarbsView(complexEntry: false, meal, override: true)
  592. }
  593. }
  594. var mealEntries: some View {
  595. VStack {
  596. if let carbs = meal.first?.carbs, carbs > 0 {
  597. HStack {
  598. Text("Carbs").foregroundColor(.secondary)
  599. Spacer()
  600. Text(carbs.formatted())
  601. Text("g").foregroundColor(.secondary)
  602. }
  603. }
  604. if let fat = meal.first?.fat, fat > 0 {
  605. HStack {
  606. Text("Fat").foregroundColor(.secondary)
  607. Spacer()
  608. Text(fat.formatted())
  609. Text("g").foregroundColor(.secondary)
  610. }
  611. }
  612. if let protein = meal.first?.protein, protein > 0 {
  613. HStack {
  614. Text("Protein").foregroundColor(.secondary)
  615. Spacer()
  616. Text(protein.formatted())
  617. Text("g").foregroundColor(.secondary)
  618. }
  619. }
  620. if let note = meal.first?.note, note != "" {
  621. HStack {
  622. Text("Note").foregroundColor(.secondary)
  623. Spacer()
  624. Text(note).foregroundColor(.secondary)
  625. }
  626. }
  627. }
  628. }
  629. }
  630. struct DividerDouble: View {
  631. var body: some View {
  632. VStack(spacing: 2) {
  633. Rectangle()
  634. .frame(height: 1)
  635. .foregroundColor(.gray.opacity(0.65))
  636. Rectangle()
  637. .frame(height: 1)
  638. .foregroundColor(.gray.opacity(0.65))
  639. }
  640. .frame(height: 4)
  641. .padding(.vertical)
  642. }
  643. }
  644. struct DividerCustom: View {
  645. var body: some View {
  646. Rectangle()
  647. .frame(height: 1)
  648. .foregroundColor(.gray.opacity(0.65))
  649. .padding(.vertical)
  650. }
  651. }
  652. }