TrioMainWatchView.swift 6.4 KB

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