TrioMainWatchView.swift 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  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. @State private var navigateToCarbsInput = false
  13. @State private var navigateToBolusInput = false
  14. @State private var continueToBolus = false
  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 {
  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. .toolbar {
  41. ToolbarItem(placement: .topBarLeading) {
  42. HStack {
  43. Image(systemName: "syringe.fill")
  44. .foregroundStyle(.blue)
  45. Text(state.iob ?? "--")
  46. .foregroundStyle(.white)
  47. }.font(.caption)
  48. }
  49. ToolbarItem(placement: .topBarTrailing) {
  50. HStack {
  51. Text(state.cob ?? "--")
  52. .foregroundStyle(.white)
  53. Image(systemName: "fork.knife")
  54. .foregroundStyle(.orange)
  55. }.font(.caption)
  56. }
  57. ToolbarItemGroup(placement: .bottomBar) {
  58. Button {
  59. showingOverrideSheet = true
  60. } label: {
  61. Image(systemName: "clock.arrow.2.circlepath")
  62. .foregroundStyle(Color.primary, Color.purple)
  63. }
  64. Button {
  65. showingTreatmentMenuSheet.toggle()
  66. } label: {
  67. Image(systemName: "plus")
  68. .foregroundStyle(Color.bgDarkerDarkBlue)
  69. }
  70. .controlSize(.large)
  71. .buttonStyle(WatchOSButtonStyle())
  72. Button {
  73. showingTempTargetSheet = true
  74. } label: {
  75. Image(systemName: "target")
  76. .foregroundStyle(.green.opacity(0.75))
  77. }
  78. }
  79. }
  80. .sheet(isPresented: $showingTreatmentMenuSheet) {
  81. TreatmentMenuView(selectedTreatment: $selectedTreatment)
  82. .onDisappear {
  83. handleTreatmentSelection()
  84. }
  85. }
  86. .sheet(isPresented: $showingOverrideSheet) {
  87. OverridePresetsView(
  88. overridePresets: state.overridePresets,
  89. state: state
  90. )
  91. }
  92. .sheet(isPresented: $showingTempTargetSheet) {
  93. TempTargetPresetsView(
  94. tempTargetPresets: state.tempTargetPresets,
  95. state: state
  96. )
  97. }
  98. .navigationDestination(isPresented: $navigateToCarbsInput) {
  99. CarbsInputView(state: state, continueToBolus: continueToBolus)
  100. }
  101. .navigationDestination(isPresented: $navigateToBolusInput) {
  102. BolusInputView(state: state)
  103. }
  104. }
  105. }
  106. struct WatchOSButtonStyle: ButtonStyle {
  107. var backgroundGradient = LinearGradient(colors: [
  108. Color(red: 0.721, green: 0.341, blue: 1),
  109. Color(red: 0.486, green: 0.545, blue: 0.953),
  110. Color(red: 0.262, green: 0.733, blue: 0.914)
  111. ], startPoint: .topLeading, endPoint: .bottomTrailing)
  112. var foregroundColor: Color = .white
  113. var fontSize: Font = .title2
  114. private var is40mm: Bool {
  115. let size = WKInterfaceDevice.current().screenBounds.size
  116. return size.height < 225 && size.width < 185
  117. }
  118. func makeBody(configuration: Configuration) -> some View {
  119. configuration.label
  120. .font(fontSize)
  121. .fontWeight(is40mm ? .medium : .semibold)
  122. .padding(is40mm ? 6 : 10)
  123. .background(
  124. backgroundGradient.opacity(configuration.isPressed ? 0.8 : 1.0)
  125. )
  126. .clipShape(Circle())
  127. .scaleEffect(configuration.isPressed ? 0.95 : 1.0)
  128. .animation(.easeInOut(duration: 0.1), value: configuration.isPressed)
  129. }
  130. }
  131. private func updateRotation(for trend: String?) {
  132. switch trend {
  133. case "DoubleUp",
  134. "SingleUp":
  135. rotationDegrees = -90
  136. case "FortyFiveUp":
  137. rotationDegrees = -45
  138. case "Flat":
  139. rotationDegrees = 0
  140. case "FortyFiveDown":
  141. rotationDegrees = 45
  142. case "DoubleDown",
  143. "SingleDown":
  144. rotationDegrees = 90
  145. default:
  146. rotationDegrees = 0
  147. }
  148. }
  149. private func handleTreatmentSelection() {
  150. guard let treatment = selectedTreatment else { return }
  151. switch treatment {
  152. case .mealBolusCombo:
  153. navigateToCarbsInput = true
  154. continueToBolus = true
  155. case .meal:
  156. navigateToCarbsInput = true
  157. case .bolus:
  158. navigateToBolusInput = true
  159. }
  160. }
  161. }
  162. extension Binding where Value == Int {
  163. func doubleBinding() -> Binding<Double> {
  164. Binding<Double>(
  165. get: { Double(self.wrappedValue) },
  166. set: { self.wrappedValue = Int($0) }
  167. )
  168. }
  169. }
  170. extension Color {
  171. static let bgDarkBlue = Color("Background_DarkBlue")
  172. static let bgDarkerDarkBlue = Color("Background_DarkerDarkBlue")
  173. }
  174. #Preview {
  175. TrioMainWatchView()
  176. }