BolusProgressOverlay.swift 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. import SwiftUI
  2. struct BolusProgressOverlay: View {
  3. let state: WatchState
  4. let onCancelBolus: () -> Void
  5. private let progressGradient = LinearGradient(
  6. colors: [
  7. Color(red: 0.7215686275, green: 0.3411764706, blue: 1), // #B857FF
  8. Color(red: 0.6235294118, green: 0.4235294118, blue: 0.9803921569), // #9F6CFA
  9. Color(red: 0.4862745098, green: 0.5450980392, blue: 0.9529411765), // #7C8BF3
  10. Color(red: 0.3411764706, green: 0.6666666667, blue: 0.9254901961), // #57AAEC
  11. Color(red: 0.262745098, green: 0.7333333333, blue: 0.9137254902) // #43BBE9
  12. ],
  13. startPoint: .leading,
  14. endPoint: .trailing
  15. )
  16. private var isWatchStateDated: Bool {
  17. // If `lastWatchStateUpdate` is nil, treat as "dated"
  18. guard let lastUpdateTimestamp = state.lastWatchStateUpdate else {
  19. return true
  20. }
  21. let now = Date().timeIntervalSince1970
  22. let secondsSinceUpdate = now - lastUpdateTimestamp
  23. // Return true if last update older than 5 min, so 1 loop cycle
  24. return secondsSinceUpdate > 5 * 60
  25. }
  26. private var isSessionUnreachable: Bool {
  27. guard let session = state.session else {
  28. return true // No session at all => unreachable
  29. }
  30. // Return true if not .activated OR not reachable
  31. return session.activationState != .activated
  32. }
  33. var body: some View {
  34. VStack(spacing: 10) {
  35. VStack {
  36. Text("Bolusing")
  37. .font(.footnote)
  38. .foregroundStyle(.secondary)
  39. .padding(.top)
  40. ProgressView(value: state.bolusProgress, total: 1.0)
  41. .tint(progressGradient)
  42. Text(String(
  43. format: String(
  44. localized: "%.2f U of %.2f U",
  45. comment: "Format for showing delivered and active bolus amounts, 'x U of y U' on watch"
  46. ),
  47. state.deliveredAmount,
  48. state.activeBolusAmount
  49. ))
  50. .font(.footnote)
  51. .foregroundStyle(.secondary)
  52. Spacer()
  53. Button(action: {
  54. state.sendCancelBolusRequest()
  55. onCancelBolus()
  56. }) {
  57. Text("Cancel Bolus")
  58. }
  59. .buttonStyle(.bordered)
  60. .padding()
  61. .disabled(isWatchStateDated || isSessionUnreachable)
  62. }
  63. .padding()
  64. .background(Color.black.opacity(0.9))
  65. .cornerRadius(10)
  66. }
  67. .scenePadding()
  68. .onChange(of: state.bolusProgress) { _, newProgress in
  69. if newProgress >= 1.0 {
  70. state.activeBolusAmount = 0 // Reset only when bolus is complete
  71. }
  72. }
  73. .onDisappear {
  74. state.activeBolusAmount = 0 // Triple-check to reset when view disappears
  75. }
  76. }
  77. }