TrioMainWatchView.swift 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. import Charts
  2. import SwiftUI
  3. struct TrioMainWatchView: View {
  4. @State private var state = WatchState()
  5. @State private var showingCarbsSheet = false
  6. @State private var showingBolusSheet = false
  7. @State private var currentPage: Double = 0
  8. @State private var rotationDegrees: Double = 0.0
  9. var body: some View {
  10. TabView(selection: $currentPage) {
  11. // Page 1: Current glucose and action buttons
  12. ScrollView {
  13. VStack(spacing: 10) {
  14. // IOB, COB, lastLoopTime Display
  15. VStack(alignment: .leading) {
  16. HStack {
  17. HStack {
  18. Image(systemName: "syringe.fill")
  19. .foregroundStyle(.blue)
  20. Text(state.iob ?? "--")
  21. }
  22. Spacer()
  23. HStack {
  24. Image(systemName: "fork.knife")
  25. .foregroundStyle(.orange)
  26. Text(state.cob ?? "--")
  27. }
  28. Spacer()
  29. // TODO: set loop colors conditionally, not hard coded
  30. HStack {
  31. Image(systemName: "circle")
  32. .foregroundStyle(.green)
  33. Text(state.lastLoopTime ?? "--")
  34. .padding(.trailing)
  35. }
  36. }
  37. }
  38. // Main Glucose Display
  39. ZStack {
  40. TrendShape(rotationDegrees: rotationDegrees)
  41. .animation(.spring(response: 0.5, dampingFraction: 0.6), value: rotationDegrees)
  42. VStack(alignment: .center) {
  43. Text(state.currentGlucose)
  44. .fontWeight(.semibold)
  45. .font(.system(.title, design: .rounded))
  46. if let delta = state.delta {
  47. Text(delta)
  48. .fontWeight(.semibold)
  49. .font(.system(.caption, design: .rounded))
  50. .foregroundStyle(.secondary)
  51. }
  52. }
  53. }.padding(.top)
  54. }
  55. .scenePadding()
  56. }
  57. .tag(0.0)
  58. .onChange(of: state.trend) { _, newTrend in
  59. withAnimation {
  60. updateRotation(for: newTrend)
  61. }
  62. }
  63. .toolbar {
  64. ToolbarItemGroup(placement: .bottomBar) {
  65. Button {
  66. // Perform an action here.
  67. } label: {
  68. Image(systemName: "clock.arrow.2.circlepath").foregroundStyle(Color.primary, Color.purple)
  69. }
  70. Button {
  71. // Perform an action here.
  72. } label: {
  73. Image(systemName: "plus")
  74. .foregroundStyle(Color.black)
  75. }
  76. .controlSize(.large)
  77. .buttonStyle(WatchOSButtonStyle(backgroundGradient: LinearGradient(colors: [
  78. Color(red: 0.7215686275, green: 0.3411764706, blue: 1), // #B857FF
  79. Color(red: 0.6235294118, green: 0.4235294118, blue: 0.9803921569), // #9F6CFA
  80. Color(red: 0.4862745098, green: 0.5450980392, blue: 0.9529411765), // #7C8BF3
  81. Color(red: 0.3411764706, green: 0.6666666667, blue: 0.9254901961), // #57AAEC
  82. Color(red: 0.262745098, green: 0.7333333333, blue: 0.9137254902), // #43BBE9
  83. Color(
  84. red: 0.7215686275,
  85. green: 0.3411764706,
  86. blue: 1
  87. ) // #B857FF (repeated for seamless transition)
  88. ], startPoint: .topLeading, endPoint: .bottomTrailing)))
  89. Button {
  90. // Perform an action here.
  91. } label: {
  92. Image(systemName: "target").foregroundStyle(.green.opacity(0.75))
  93. }
  94. }
  95. }
  96. // Page 2: Glucose chart
  97. GlucoseChartView(glucoseValues: state.glucoseValues)
  98. .tag(1.0)
  99. }
  100. .tabViewStyle(.verticalPage)
  101. .navigationBarHidden(true)
  102. .digitalCrownRotation($currentPage, from: 0, through: 1, by: 1)
  103. .sheet(isPresented: $showingCarbsSheet) {
  104. CarbsInputView(state: state)
  105. }
  106. .sheet(isPresented: $showingBolusSheet) {
  107. BolusInputView(state: state)
  108. }
  109. }
  110. struct WatchOSButtonStyle: ButtonStyle {
  111. var backgroundGradient: LinearGradient
  112. var foregroundColor: Color = .white
  113. var fontSize: Font = .title2
  114. func makeBody(configuration: Configuration) -> some View {
  115. configuration.label
  116. .font(fontSize)
  117. .fontWeight(.semibold)
  118. .foregroundColor(foregroundColor)
  119. .padding()
  120. .background(
  121. backgroundGradient // Custom background color
  122. .opacity(configuration.isPressed ? 0.8 : 1.0) // Simulates the press effect
  123. )
  124. .clipShape(Circle())
  125. .scaleEffect(configuration.isPressed ? 0.95 : 1.0) // Adds subtle scaling for press
  126. .animation(.easeInOut(duration: 0.1), value: configuration.isPressed) // Smooth animation
  127. }
  128. }
  129. private func updateRotation(for trend: String?) {
  130. switch trend {
  131. case "DoubleUp",
  132. "SingleUp":
  133. rotationDegrees = -90
  134. case "FortyFiveUp":
  135. rotationDegrees = -45
  136. case "Flat":
  137. rotationDegrees = 0
  138. case "FortyFiveDown":
  139. rotationDegrees = 45
  140. case "DoubleDown",
  141. "SingleDown":
  142. rotationDegrees = 90
  143. default:
  144. rotationDegrees = 0
  145. }
  146. }
  147. }
  148. #Preview {
  149. TrioMainWatchView()
  150. }