CarbsInputView.swift 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. import Foundation
  2. import SwiftUI
  3. // MARK: - Carbs Input View
  4. struct CarbsInputView: View {
  5. @Binding var navigationPath: [NavigationDestinations]
  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 { carbsAmount -= 1 }
  34. }) {
  35. Image(systemName: "minus.circle.fill")
  36. .font(.title3)
  37. .foregroundColor(.orange)
  38. }
  39. .buttonStyle(.borderless)
  40. .disabled(carbsAmount < 1)
  41. Spacer()
  42. // Display the current carb amount
  43. Text(String(format: "%.0f g", carbsAmount))
  44. .fontWeight(.bold)
  45. .font(.system(.title2, design: .rounded))
  46. .foregroundColor(carbsAmount > 0.0 && carbsAmount >= effectiveCarbsLimit ? .red : .primary)
  47. .focusable(true)
  48. .focused($isCrownFocused)
  49. .digitalCrownRotation(
  50. $carbsAmount,
  51. from: 0,
  52. through: effectiveCarbsLimit,
  53. by: 1,
  54. sensitivity: .medium,
  55. isContinuous: false,
  56. isHapticFeedbackEnabled: true
  57. )
  58. Spacer()
  59. // "+" Button
  60. Button(action: {
  61. carbsAmount += 1
  62. }) {
  63. Image(systemName: "plus.circle.fill")
  64. .font(.title3)
  65. .foregroundColor(.orange)
  66. }
  67. .buttonStyle(.borderless)
  68. .disabled(carbsAmount >= effectiveCarbsLimit)
  69. }.padding(.horizontal)
  70. Text("Carbohydrates")
  71. .font(.subheadline)
  72. .foregroundColor(.secondary)
  73. .padding(.bottom)
  74. Spacer()
  75. if carbsAmount > 0.0 && carbsAmount >= effectiveCarbsLimit {
  76. Text("Carbs Limit Reached!")
  77. .font(.footnote)
  78. .foregroundColor(.red)
  79. }
  80. Button(buttonLabel) {
  81. if continueToBolus {
  82. state.carbsAmount = Int(min(carbsAmount, effectiveCarbsLimit))
  83. navigationPath.append(NavigationDestinations.bolusInput)
  84. } else {
  85. state.sendCarbsRequest(Int(min(carbsAmount, effectiveCarbsLimit)))
  86. navigationPath.append(NavigationDestinations.acknowledgmentPending)
  87. }
  88. }
  89. .buttonStyle(.bordered)
  90. .tint(.orange)
  91. .disabled(!(carbsAmount > 0.0) || carbsAmount >= effectiveCarbsLimit)
  92. }
  93. .background(trioBackgroundColor)
  94. .toolbar {
  95. ToolbarItem(placement: .topBarTrailing) {
  96. Image(systemName: "fork.knife")
  97. .resizable()
  98. .aspectRatio(contentMode: .fit)
  99. .frame(width: 14, height: 14)
  100. .padding()
  101. .background(Color.orange)
  102. .foregroundStyle(.white)
  103. .clipShape(Circle())
  104. }
  105. }
  106. }
  107. }