BolusInputView.swift 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. import Foundation
  2. import SwiftUI
  3. import WatchKit
  4. // MARK: - Bolus Input View
  5. struct BolusInputView: View {
  6. @ObservedObject var navigationState: NavigationState
  7. @State private var bolusAmount = 0.0
  8. let state: WatchState
  9. @FocusState private var isCrownFocused: Bool
  10. private var effectiveBolusLimit: Double {
  11. // Extract current IOB from string and convert to Double
  12. let currentIOB = Double(state.iob?.replacingOccurrences(of: " U", with: "") ?? "0") ?? 0
  13. // Calculate available IOB
  14. let availableIOB = max(0, Double(truncating: state.maxIOB as NSNumber) - currentIOB)
  15. return min(
  16. Double(truncating: state.maxBolus as NSNumber),
  17. availableIOB
  18. )
  19. }
  20. var trioBackgroundColor = LinearGradient(
  21. gradient: Gradient(colors: [Color.bgDarkBlue, Color.bgDarkerDarkBlue]),
  22. startPoint: .top,
  23. endPoint: .bottom
  24. )
  25. var body: some View {
  26. VStack {
  27. if state.carbsAmount > 0 {
  28. HStack {
  29. Text("Carbs:").bold().font(.subheadline).padding(.leading)
  30. Text("\(state.carbsAmount) g").font(.subheadline).foregroundStyle(Color.orange)
  31. Spacer()
  32. }
  33. }
  34. Spacer()
  35. HStack {
  36. // "-" Button
  37. Button(action: {
  38. if bolusAmount > 0 { bolusAmount -= 1 }
  39. }) {
  40. Image(systemName: "minus.circle.fill")
  41. .font(.title3)
  42. .foregroundColor(.blue)
  43. }
  44. .buttonStyle(.borderless)
  45. .disabled(bolusAmount < 1)
  46. Spacer()
  47. // Display the current carb amount
  48. Text(String(format: "%.2f U", bolusAmount))
  49. .fontWeight(.bold)
  50. .font(.system(.title2, design: .rounded))
  51. .foregroundColor(.primary)
  52. .focusable(true)
  53. .focused($isCrownFocused)
  54. .digitalCrownRotation(
  55. $bolusAmount,
  56. from: 0,
  57. through: effectiveBolusLimit,
  58. by: 1, // TODO: use pump increment here
  59. sensitivity: .medium,
  60. isContinuous: false,
  61. isHapticFeedbackEnabled: true
  62. )
  63. Spacer()
  64. // "+" Button
  65. Button(action: {
  66. bolusAmount += 0.5
  67. }) {
  68. Image(systemName: "plus.circle.fill")
  69. .font(.title3)
  70. .foregroundColor(.blue)
  71. }
  72. .buttonStyle(.borderless)
  73. .disabled(bolusAmount >= effectiveBolusLimit)
  74. }.padding(.horizontal)
  75. Text("Insulin")
  76. .font(.subheadline)
  77. .foregroundColor(.secondary)
  78. .padding(.bottom)
  79. Spacer()
  80. Button("Log Bolus") {
  81. state.bolusAmount = min(bolusAmount, effectiveBolusLimit)
  82. navigationState.path.append(NavigationDestinations.bolusConfirm)
  83. }
  84. .buttonStyle(.bordered)
  85. .tint(.blue)
  86. .disabled(!(bolusAmount > 0.0) || bolusAmount >= effectiveBolusLimit)
  87. }
  88. .background(trioBackgroundColor)
  89. .toolbar {
  90. ToolbarItem(placement: .topBarTrailing) {
  91. Image(systemName: "syringe.fill")
  92. .resizable()
  93. .aspectRatio(contentMode: .fit)
  94. .frame(width: 14, height: 14)
  95. .padding()
  96. .background(Color.blue)
  97. .foregroundStyle(.white)
  98. .clipShape(Circle())
  99. }
  100. }
  101. .blur(radius: state.bolusProgress > 0 && state.bolusProgress < 1.0 && !state.isBolusCanceled ? 3 : 0)
  102. .overlay {
  103. if state.bolusProgress > 0 && state.bolusProgress < 1.0 && !state.isBolusCanceled {
  104. BolusProgressOverlay(state: state)
  105. .transition(.opacity)
  106. }
  107. }
  108. }
  109. }
  110. #Preview {
  111. BolusInputView(navigationState: NavigationState(), state: WatchState())
  112. }