BolusConfirmationView.swift 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. import Combine
  2. import SwiftUI
  3. struct BolusConfirmationView: View {
  4. @EnvironmentObject var state: WatchStateModel
  5. @State var crownProgress: CGFloat = 100.0
  6. @State var progress: CGFloat = 0
  7. private let elementSize: CGFloat = 30
  8. @State var progressReturn: AnyCancellable?
  9. @State var done = false
  10. var body: some View {
  11. VStack {
  12. GeometryReader { geo in
  13. ZStack {
  14. RoundedRectangle(cornerRadius: elementSize / 2, style: .circular)
  15. .fill(.secondary)
  16. .frame(width: elementSize, height: geo.size.height)
  17. .opacity(0.2)
  18. Image(systemName: "arrow.right")
  19. .resizable()
  20. .frame(width: elementSize / 2, height: elementSize / 2)
  21. .foregroundColor(.primary)
  22. .position(x: geo.size.width - elementSize / 4, y: elementSize / 2)
  23. .transition(.opacity)
  24. if done {
  25. Image(systemName: "checkmark.circle.fill")
  26. .resizable()
  27. .foregroundColor(.loopGreen)
  28. .frame(width: elementSize, height: elementSize)
  29. .position(
  30. x: geo.size.width / 2,
  31. y: elementSize / 2 + ((geo.size.height - elementSize) * progress / 100)
  32. )
  33. } else {
  34. Image(systemName: "arrow.down.circle.fill")
  35. .resizable()
  36. .foregroundColor(.insulin)
  37. .frame(width: elementSize, height: elementSize)
  38. .position(
  39. x: geo.size.width / 2,
  40. y: elementSize / 2 + ((geo.size.height - elementSize) * progress / 100)
  41. )
  42. }
  43. }
  44. .frame(maxWidth: .infinity, maxHeight: .infinity)
  45. }
  46. .padding()
  47. Button {
  48. WKInterfaceDevice.current().play(.click)
  49. state.pendingBolus = nil
  50. state.isConfirmationBolusViewActive = false
  51. }
  52. label: {
  53. Text("Cancel")
  54. }
  55. }
  56. .focusable(true)
  57. .digitalCrownRotation(
  58. $crownProgress,
  59. from: 0.0,
  60. through: 100.0,
  61. by: 1,
  62. sensitivity: .high,
  63. isContinuous: false,
  64. isHapticFeedbackEnabled: true
  65. )
  66. .onChange(of: crownProgress) { _ in
  67. guard !done else { return }
  68. progressReturn?.cancel()
  69. progress = min(max(0, 100 - crownProgress), 100)
  70. if progress >= 100 {
  71. success()
  72. } else {
  73. progressReturn = Just(())
  74. .delay(for: 0.1, scheduler: RunLoop.main)
  75. .sink { _ in
  76. crownProgress = 100
  77. withAnimation {
  78. progress = 0
  79. }
  80. }
  81. }
  82. }
  83. }
  84. private func success() {
  85. WKInterfaceDevice.current().play(.success)
  86. withAnimation {
  87. done = true
  88. }
  89. DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
  90. state.enactBolus()
  91. }
  92. }
  93. }
  94. struct BolusConfirmationView_Previews: PreviewProvider {
  95. static var previews: some View {
  96. BolusConfirmationView().environmentObject(WatchStateModel())
  97. }
  98. }