CarbsInputView.swift 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. import Foundation
  2. import SwiftUI
  3. // MARK: - Carbs Input View
  4. struct CarbsInputView: View {
  5. @Binding var navigationPath: NavigationPath
  6. @State private var carbsAmount: Double = 0.0 // Needs to be Double due to .digitalCrownRotation() stride
  7. @FocusState private var isCrownFocused: Bool // Manage crown focus
  8. let state: WatchState
  9. let continueToBolus: Bool
  10. private var effectiveCarbsLimit: Double {
  11. // Extract current COB from string and convert to Double
  12. let currentCOB = Double(state.cob?.replacingOccurrences(of: " g", with: "") ?? "0") ?? 0
  13. // Calculate available COB
  14. let availableCOB = max(0, Double(truncating: state.maxCOB as NSNumber) - currentCOB)
  15. return min(
  16. Double(truncating: state.maxCarbs as NSNumber),
  17. availableCOB
  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. let buttonLabel = continueToBolus ? "Proceed" : "Log Carbs"
  27. // TODO: introduce meal setting fpu enablement to conditional handle FPU
  28. VStack {
  29. Spacer()
  30. HStack {
  31. // "-" Button
  32. Button(action: {
  33. if carbsAmount > 0 {
  34. let currentAmount = carbsAmount
  35. carbsAmount < 5 ? carbsAmount = 0 : (carbsAmount -= 5)
  36. }
  37. }) {
  38. Image(systemName: "minus.circle.fill")
  39. .font(.title3)
  40. .foregroundColor(.orange)
  41. }
  42. .buttonStyle(.borderless)
  43. .disabled(carbsAmount < 1)
  44. Spacer()
  45. // Display the current carb amount
  46. Text(String(format: "%.0f g", carbsAmount))
  47. .fontWeight(.bold)
  48. .font(.system(.title2, design: .rounded))
  49. .foregroundColor(carbsAmount > 0.0 && carbsAmount >= effectiveCarbsLimit ? .loopRed : .primary)
  50. .focusable(true)
  51. .focused($isCrownFocused)
  52. .digitalCrownRotation(
  53. $carbsAmount,
  54. from: 0,
  55. through: effectiveCarbsLimit,
  56. by: 1,
  57. sensitivity: .medium,
  58. isContinuous: false,
  59. isHapticFeedbackEnabled: true
  60. )
  61. Spacer()
  62. // "+" Button
  63. Button(action: {
  64. carbsAmount += 5
  65. }) {
  66. Image(systemName: "plus.circle.fill")
  67. .font(.title3)
  68. .foregroundColor(.orange)
  69. }
  70. .buttonStyle(.borderless)
  71. .disabled(carbsAmount >= effectiveCarbsLimit)
  72. }.padding(.horizontal)
  73. Text("Carbohydrates")
  74. .font(.subheadline)
  75. .foregroundColor(.secondary)
  76. .padding(.bottom)
  77. Spacer()
  78. if carbsAmount > 0.0 && carbsAmount >= effectiveCarbsLimit {
  79. Text("Carbs Limit Reached!")
  80. .font(.footnote)
  81. .foregroundColor(.loopRed)
  82. }
  83. Button(buttonLabel) {
  84. if continueToBolus {
  85. state.carbsAmount = Int(min(carbsAmount, effectiveCarbsLimit))
  86. navigationPath.append(NavigationDestinations.bolusInput)
  87. } else {
  88. state.sendCarbsRequest(Int(min(carbsAmount, effectiveCarbsLimit)))
  89. navigationPath.append(NavigationDestinations.acknowledgmentPending)
  90. }
  91. }
  92. .buttonStyle(.bordered)
  93. .tint(.orange)
  94. .disabled(!(carbsAmount > 0.0) || carbsAmount >= effectiveCarbsLimit)
  95. }
  96. .background(trioBackgroundColor)
  97. .toolbar {
  98. ToolbarItem(placement: .topBarTrailing) {
  99. Image(systemName: "fork.knife")
  100. .resizable()
  101. .aspectRatio(contentMode: .fit)
  102. .frame(width: 14, height: 14)
  103. .padding()
  104. .background(Color.orange)
  105. .foregroundStyle(.white)
  106. .clipShape(Circle())
  107. }
  108. }
  109. }
  110. }