DeactivatePodViewModel.swift 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. //
  2. // DeactivatePodViewModel.swift
  3. // OmniKit
  4. //
  5. // Created by Pete Schwamb on 3/9/20.
  6. // Copyright © 2021 LoopKit Authors. All rights reserved.
  7. //
  8. import Foundation
  9. import LoopKitUI
  10. import OmniKit
  11. public protocol PodDeactivater {
  12. func deactivatePod(completion: @escaping (OmnipodPumpManagerError?) -> Void)
  13. func forgetPod(completion: @escaping () -> Void)
  14. }
  15. extension OmnipodPumpManager: PodDeactivater {}
  16. class DeactivatePodViewModel: ObservableObject, Identifiable {
  17. enum DeactivatePodViewModelState {
  18. case active
  19. case deactivating
  20. case resultError(DeactivationError)
  21. case finished
  22. var actionButtonAccessibilityLabel: String {
  23. switch self {
  24. case .active:
  25. return LocalizedString("Deactivate Pod", comment: "Deactivate pod action button accessibility label while ready to deactivate")
  26. case .deactivating:
  27. return LocalizedString("Deactivating.", comment: "Deactivate pod action button accessibility label while deactivating")
  28. case .resultError(let error):
  29. return String(format: "%@ %@", error.errorDescription ?? "", error.recoverySuggestion ?? "")
  30. case .finished:
  31. return LocalizedString("Pod deactivated successfully. Continue.", comment: "Deactivate pod action button accessibility label when deactivation complete")
  32. }
  33. }
  34. var actionButtonDescription: String {
  35. switch self {
  36. case .active:
  37. return LocalizedString("Slide to Deactivate Pod", comment: "Action button description for deactivate while pod still active")
  38. case .resultError:
  39. return LocalizedString("Retry", comment: "Action button description for deactivate after failed attempt")
  40. case .deactivating:
  41. return LocalizedString("Deactivating...", comment: "Action button description while deactivating")
  42. case .finished:
  43. return LocalizedString("Continue", comment: "Action button description when deactivated")
  44. }
  45. }
  46. var actionButtonStyle: ActionButton.ButtonType {
  47. switch self {
  48. case .active:
  49. return .destructive
  50. default:
  51. return .primary
  52. }
  53. }
  54. var progressState: ProgressIndicatorState {
  55. switch self {
  56. case .active, .resultError:
  57. return .hidden
  58. case .deactivating:
  59. return .indeterminantProgress
  60. case .finished:
  61. return .completed
  62. }
  63. }
  64. var showProgressDetail: Bool {
  65. switch self {
  66. case .active:
  67. return false
  68. default:
  69. return true
  70. }
  71. }
  72. var isProcessing: Bool {
  73. switch self {
  74. case .deactivating:
  75. return true
  76. default:
  77. return false
  78. }
  79. }
  80. var isFinished: Bool {
  81. if case .finished = self {
  82. return true
  83. }
  84. return false
  85. }
  86. }
  87. @Published var state: DeactivatePodViewModelState = .active
  88. public var stateNeedsDeliberateUserAcceptance : Bool {
  89. switch state {
  90. case .active:
  91. true
  92. default:
  93. false
  94. }
  95. }
  96. var error: DeactivationError? {
  97. if case .resultError(let error) = self.state {
  98. return error
  99. }
  100. return nil
  101. }
  102. var didFinish: (() -> Void)?
  103. var didCancel: (() -> Void)?
  104. var podDeactivator: PodDeactivater
  105. var podAttachedToBody: Bool
  106. var instructionText: String
  107. init(podDeactivator: PodDeactivater, podAttachedToBody: Bool, fault: DetailedStatus?) {
  108. var text: String = ""
  109. if let faultEventCode = fault?.faultEventCode {
  110. let notificationString = faultEventCode.notificationTitle
  111. switch faultEventCode.faultType {
  112. case .exceededMaximumPodLife80Hrs, .reservoirEmpty, .occluded:
  113. // Just prepend a simple sentence with the notification string for these faults.
  114. // Other occluded related 0x6? faults will be treated as a general pod error as per the PDM.
  115. text = String(format: "%@. ", notificationString)
  116. default:
  117. // Display the fault code in decimal and hex, the fault description and the pdmRef string for other errors.
  118. text = String(format: "⚠️ %1$@ (0x%2$02X)\n%3$@\n", notificationString, faultEventCode.rawValue, faultEventCode.faultDescription)
  119. if let pdmRef = fault?.pdmRef {
  120. text += LocalizedString("Ref: ", comment: "PDM Ref string line") + pdmRef + "\n\n"
  121. }
  122. }
  123. }
  124. if podAttachedToBody {
  125. text += LocalizedString("Please deactivate the pod. When deactivation is complete, you may remove it and pair a new pod.", comment: "Instructions for deactivate pod when pod is on body")
  126. } else {
  127. text += LocalizedString("Please deactivate the pod. When deactivation is complete, you may pair a new pod.", comment: "Instructions for deactivate pod when pod not on body")
  128. }
  129. self.podDeactivator = podDeactivator
  130. self.podAttachedToBody = podAttachedToBody
  131. self.instructionText = text
  132. }
  133. public func continueButtonTapped() {
  134. if case .finished = state {
  135. didFinish?()
  136. } else {
  137. self.state = .deactivating
  138. podDeactivator.deactivatePod { (error) in
  139. DispatchQueue.main.async {
  140. if let error = error {
  141. self.state = .resultError(DeactivationError.OmnipodPumpManagerError(error))
  142. } else {
  143. self.discardPod(navigateOnCompletion: false)
  144. }
  145. }
  146. }
  147. }
  148. }
  149. public func discardPod(navigateOnCompletion: Bool = true) {
  150. podDeactivator.forgetPod {
  151. DispatchQueue.main.async {
  152. if navigateOnCompletion {
  153. self.didFinish?()
  154. } else {
  155. self.state = .finished
  156. }
  157. }
  158. }
  159. }
  160. }
  161. enum DeactivationError : LocalizedError {
  162. case OmnipodPumpManagerError(OmnipodPumpManagerError)
  163. var recoverySuggestion: String? {
  164. switch self {
  165. case .OmnipodPumpManagerError:
  166. return LocalizedString("There was a problem communicating with the pod. If this problem persists, tap Discard Pod. You can then activate a new Pod.", comment: "Format string for recovery suggestion during deactivate pod.")
  167. }
  168. }
  169. var errorDescription: String? {
  170. switch self {
  171. case .OmnipodPumpManagerError(let error):
  172. return error.errorDescription
  173. }
  174. }
  175. }