TrioMainWatchView.swift 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. import Charts
  2. import SwiftUI
  3. struct TrioMainWatchView: View {
  4. @State private var state = WatchState()
  5. // misc
  6. @State private var currentPage: Int = 0
  7. @State private var rotationDegrees: Double = 0.0
  8. @State private var showingTempTargetSheet = false
  9. // view visbility
  10. @State private var showingTreatmentMenuSheet: Bool = false
  11. @State private var showingOverrideSheet: Bool = false
  12. // navigation flag for meal bolus combo
  13. @State private var continueToBolus = false
  14. @State private var navigationPath: [NavigationDestinations] = []
  15. // treatments
  16. @State private var selectedTreatment: TreatmentOption?
  17. private var trioBackgroundColor = LinearGradient(
  18. gradient: Gradient(colors: [Color.bgDarkBlue, Color.bgDarkerDarkBlue]),
  19. startPoint: .top,
  20. endPoint: .bottom
  21. )
  22. var body: some View {
  23. NavigationStack(path: $navigationPath) {
  24. TabView(selection: $currentPage) {
  25. // Page 1: Current glucose trend in "BG bobble"
  26. GlucoseTrendView(state: state, rotationDegrees: rotationDegrees)
  27. .tag(0)
  28. // Page 2: Glucose chart
  29. GlucoseChartView(glucoseValues: state.glucoseValues)
  30. .tag(1)
  31. }
  32. .background(trioBackgroundColor)
  33. .tabViewStyle(.verticalPage)
  34. .digitalCrownRotation($currentPage.doubleBinding(), from: 0, through: 1, by: 1)
  35. .onChange(of: state.trend) { _, newTrend in
  36. withAnimation {
  37. updateRotation(for: newTrend)
  38. }
  39. }
  40. .onAppear {
  41. state.confirmationProgress = 0 // reset auth progress
  42. }
  43. .toolbar {
  44. ToolbarItem(placement: .topBarLeading) {
  45. HStack {
  46. Image(systemName: "syringe.fill")
  47. .foregroundStyle(.blue)
  48. Text(state.iob ?? "--")
  49. .foregroundStyle(.white)
  50. }.font(.caption)
  51. }
  52. ToolbarItem(placement: .topBarTrailing) {
  53. HStack {
  54. Text(state.cob ?? "--")
  55. .foregroundStyle(.white)
  56. Image(systemName: "fork.knife")
  57. .foregroundStyle(.orange)
  58. }.font(.caption)
  59. }
  60. ToolbarItemGroup(placement: .bottomBar) {
  61. Button {
  62. showingOverrideSheet = true
  63. } label: {
  64. Image(systemName: "clock.arrow.2.circlepath")
  65. .foregroundStyle(Color.primary, Color.purple)
  66. }
  67. Button {
  68. showingTreatmentMenuSheet = true
  69. } label: {
  70. Image(systemName: "plus")
  71. .foregroundStyle(Color.bgDarkerDarkBlue)
  72. }
  73. .controlSize(.large)
  74. .buttonStyle(WatchOSButtonStyle())
  75. Button {
  76. showingTempTargetSheet = true
  77. } label: {
  78. Image(systemName: "target")
  79. .foregroundStyle(.green.opacity(0.75))
  80. }
  81. }
  82. }
  83. .fullScreenCover(isPresented: $showingTreatmentMenuSheet) {
  84. TreatmentMenuView(selectedTreatment: $selectedTreatment) {
  85. handleTreatmentSelection()
  86. }
  87. .onAppear {
  88. continueToBolus = false
  89. }
  90. }
  91. .sheet(isPresented: $showingOverrideSheet) {
  92. OverridePresetsView(
  93. overridePresets: state.overridePresets,
  94. state: state
  95. )
  96. }
  97. .sheet(isPresented: $showingTempTargetSheet) {
  98. TempTargetPresetsView(
  99. tempTargetPresets: state.tempTargetPresets,
  100. state: state
  101. )
  102. }
  103. .navigationDestination(for: NavigationDestinations.self) { destination in
  104. switch destination {
  105. case .acknowledgmentPending:
  106. AcknowledgementPendingView(
  107. navigationPath: $navigationPath,
  108. state: state
  109. )
  110. case .carbInput:
  111. CarbsInputView(
  112. navigationPath: $navigationPath,
  113. state: state,
  114. continueToBolus: selectedTreatment == .mealBolusCombo
  115. )
  116. case .bolusInput:
  117. BolusInputView(
  118. navigationPath: $navigationPath,
  119. state: state
  120. )
  121. case .bolusConfirm:
  122. BolusConfirmationView(
  123. navigationPath: $navigationPath,
  124. state: state,
  125. bolusAmount: $state.bolusAmount,
  126. confirmationProgress: $state.confirmationProgress
  127. )
  128. }
  129. }
  130. }
  131. .blur(radius: state.showBolusProgressOverlay ? 3 : 0)
  132. .overlay {
  133. if state.showBolusProgressOverlay {
  134. BolusProgressOverlay(state: state)
  135. .transition(.opacity)
  136. }
  137. }
  138. }
  139. private func updateRotation(for trend: String?) {
  140. switch trend {
  141. case "DoubleUp",
  142. "SingleUp":
  143. rotationDegrees = -90
  144. case "FortyFiveUp":
  145. rotationDegrees = -45
  146. case "Flat":
  147. rotationDegrees = 0
  148. case "FortyFiveDown":
  149. rotationDegrees = 45
  150. case "DoubleDown",
  151. "SingleDown":
  152. rotationDegrees = 90
  153. default:
  154. rotationDegrees = 0
  155. }
  156. }
  157. private func handleTreatmentSelection() {
  158. showingTreatmentMenuSheet = false // Dismiss the sheet
  159. guard let treatment = selectedTreatment else { return }
  160. switch treatment {
  161. case .meal:
  162. navigationPath.append(NavigationDestinations.carbInput)
  163. case .bolus:
  164. navigationPath.append(NavigationDestinations.bolusInput)
  165. case .mealBolusCombo:
  166. navigationPath.append(NavigationDestinations.carbInput)
  167. }
  168. }
  169. }
  170. #Preview {
  171. TrioMainWatchView()
  172. }