BolusInputView.swift 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. import Foundation
  2. import SwiftUI
  3. import WatchKit
  4. // MARK: - Bolus Input View
  5. struct BolusInputView: View {
  6. @Environment(\.dismiss) var dismiss
  7. @State private var bolusAmount = 0.0
  8. @State private var showingConfirmation = false
  9. @State private var confirmationProgress = 0.0
  10. let state: WatchState
  11. var body: some View {
  12. if showingConfirmation {
  13. BolusConfirmationView(
  14. bolusAmount: bolusAmount,
  15. progress: $confirmationProgress,
  16. state: state,
  17. dismiss: dismiss
  18. )
  19. .navigationTitle("Confirm")
  20. .navigationBarBackButtonHidden(true)
  21. .toolbar {
  22. ToolbarItem(placement: .topBarLeading) {
  23. Button {
  24. if state.carbsAmount > 0 {
  25. state.carbsAmount = 0 // reset carbs in state
  26. }
  27. dismiss()
  28. } label: {
  29. Image(systemName: "xmark")
  30. }
  31. .buttonStyle(.bordered)
  32. .clipShape(Circle())
  33. }
  34. ToolbarItem(placement: .topBarTrailing) {
  35. Image(systemName: "digitalcrown.arrow.counterclockwise.fill")
  36. .foregroundStyle(Color.white)
  37. }
  38. }
  39. } else {
  40. VStack {
  41. if state.carbsAmount > 0 {
  42. HStack {
  43. Text("Carbs: \(state.carbsAmount) g").font(.subheadline).padding(.bottom)
  44. Spacer()
  45. }
  46. }
  47. // TODO: handle bolus recommendation
  48. Picker("Bolus", selection: $bolusAmount) {
  49. ForEach(0 ... 100, id: \.self) { number in
  50. Text(String(format: "%.1f U", Double(number) / 10))
  51. .tag(Double(number) / 10)
  52. }
  53. }
  54. Button("Add Bolus") {
  55. showingConfirmation = true
  56. }
  57. .buttonStyle(.bordered)
  58. .tint(.blue)
  59. }
  60. .navigationTitle("Add Insulin")
  61. }
  62. }
  63. }
  64. struct BolusConfirmationView: View {
  65. let bolusAmount: Double
  66. @Binding var progress: Double
  67. let state: WatchState
  68. let dismiss: DismissAction
  69. @FocusState private var isCrownFocused: Bool
  70. var body: some View {
  71. VStack(spacing: 10) {
  72. if state.carbsAmount > 0 {
  73. Text(String(format: "%.1f g", state.carbsAmount))
  74. .bold()
  75. .foregroundStyle(.orange)
  76. }
  77. Text(String(format: "%.1f U", bolusAmount))
  78. .bold()
  79. .foregroundStyle(.blue)
  80. ProgressView(value: progress, total: 1.0)
  81. .tint(progress >= 1.0 ? .green : .blue)
  82. .padding(.horizontal)
  83. Text("\(Int(progress * 100))%")
  84. .font(.caption2)
  85. .foregroundStyle(.secondary)
  86. }
  87. .focusable(true)
  88. .focused($isCrownFocused)
  89. .digitalCrownRotation(
  90. $progress,
  91. from: 0.0,
  92. through: 1.0,
  93. by: 0.05,
  94. sensitivity: .medium,
  95. isContinuous: false,
  96. isHapticFeedbackEnabled: true
  97. )
  98. .onAppear {
  99. isCrownFocused = true
  100. }
  101. .onChange(of: progress) { _, newValue in
  102. if newValue >= 1.0 {
  103. WKInterfaceDevice.current().play(.success)
  104. DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
  105. if state.carbsAmount > 0 {
  106. state.sendCarbsRequest(state.carbsAmount, Date())
  107. state.carbsAmount = 0 // reset carbs in state
  108. }
  109. state.sendBolusRequest(Decimal(bolusAmount))
  110. dismiss()
  111. }
  112. } else if newValue > 0 {
  113. WKInterfaceDevice.current().play(.click)
  114. }
  115. }
  116. }
  117. }
  118. #Preview {
  119. BolusInputView(state: WatchState())
  120. }