OverrideView.swift 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. // LoopFollow
  2. // OverrideView.swift
  3. import HealthKit
  4. import SwiftUI
  5. struct OverrideView: View {
  6. @Environment(\.presentationMode) private var presentationMode
  7. private let pushNotificationManager = PushNotificationManager()
  8. @ObservedObject var device = Storage.shared.device
  9. @ObservedObject var overrideNote = Observable.shared.override
  10. @State private var showAlert: Bool = false
  11. @State private var alertType: AlertType? = nil
  12. @State private var alertMessage: String? = nil
  13. @State private var isLoading: Bool = false
  14. @State private var statusMessage: String? = nil
  15. @State private var selectedOverride: ProfileManager.TrioOverride? = nil
  16. @State private var showConfirmation: Bool = false
  17. @FocusState private var noteFieldIsFocused: Bool
  18. private var profileManager = ProfileManager.shared
  19. enum AlertType {
  20. case confirmActivation
  21. case confirmCancellation
  22. case statusSuccess
  23. case statusFailure
  24. case validation
  25. }
  26. var body: some View {
  27. NavigationView {
  28. VStack {
  29. if device.value != "Trio" {
  30. ErrorMessageView(
  31. message: String(localized: "Remote commands are currently only available for Trio.")
  32. )
  33. } else {
  34. Form {
  35. if let activeNote = overrideNote.value {
  36. Section(header: Text("Active Override")) {
  37. HStack {
  38. Text("Override")
  39. Spacer()
  40. Text(activeNote)
  41. .foregroundColor(.secondary)
  42. }
  43. Button {
  44. alertType = .confirmCancellation
  45. showAlert = true
  46. } label: {
  47. HStack {
  48. Text("Cancel Override")
  49. Spacer()
  50. Image(systemName: "xmark.app")
  51. .font(.title)
  52. }
  53. }
  54. .tint(.red)
  55. }
  56. }
  57. Section(header: Text("Available Overrides")) {
  58. if profileManager.trioOverrides.isEmpty {
  59. Text("No overrides available.")
  60. .foregroundColor(.secondary)
  61. } else {
  62. ForEach(profileManager.trioOverrides, id: \.name) { override in
  63. Button(action: {
  64. selectedOverride = override
  65. alertType = .confirmActivation
  66. showAlert = true
  67. }) {
  68. HStack {
  69. VStack(alignment: .leading) {
  70. Text(override.name)
  71. .font(.headline)
  72. if let duration = override.duration {
  73. Text("Duration: \(Int(duration)) minutes")
  74. .font(.subheadline)
  75. .foregroundColor(.secondary)
  76. }
  77. if let percentage = override.percentage {
  78. Text("Percentage: \(Int(percentage))%")
  79. .font(.subheadline)
  80. .foregroundColor(.secondary)
  81. }
  82. if let target = override.target {
  83. Text("Target: \(Localizer.formatQuantity(target)) \(Localizer.getPreferredUnit().localizedShortUnitString)")
  84. .font(.subheadline)
  85. .foregroundColor(.secondary)
  86. }
  87. }
  88. Spacer()
  89. Image(systemName: "arrow.right.circle")
  90. .foregroundColor(.blue)
  91. }
  92. }
  93. }
  94. }
  95. }
  96. }
  97. if isLoading {
  98. ProgressView("Please wait...")
  99. .padding()
  100. }
  101. }
  102. }
  103. .navigationTitle("Overrides")
  104. .navigationBarTitleDisplayMode(.inline)
  105. .alert(isPresented: $showAlert) {
  106. switch alertType {
  107. case .confirmActivation:
  108. return Alert(
  109. title: Text("Activate Override"),
  110. message: Text("Do you want to activate the override '\(selectedOverride?.name ?? "")'?"),
  111. primaryButton: .default(Text("Confirm"), action: {
  112. if let override = selectedOverride {
  113. activateOverride(override)
  114. }
  115. }),
  116. secondaryButton: .cancel()
  117. )
  118. case .confirmCancellation:
  119. return Alert(
  120. title: Text("Cancel Override"),
  121. message: Text("Are you sure you want to cancel the active override?"),
  122. primaryButton: .default(Text("Confirm"), action: {
  123. cancelOverride()
  124. }),
  125. secondaryButton: .cancel()
  126. )
  127. case .statusSuccess:
  128. return Alert(
  129. title: Text("Success"),
  130. message: Text(statusMessage ?? ""),
  131. dismissButton: .default(Text("OK"), action: {
  132. presentationMode.wrappedValue.dismiss()
  133. })
  134. )
  135. case .statusFailure:
  136. return Alert(
  137. title: Text("Error"),
  138. message: Text(statusMessage ?? String(localized: "An error occurred.")),
  139. dismissButton: .default(Text("OK"))
  140. )
  141. case .validation:
  142. return Alert(
  143. title: Text("Validation Error"),
  144. message: Text(alertMessage ?? String(localized: "Invalid input.")),
  145. dismissButton: .default(Text("OK"))
  146. )
  147. case .none:
  148. return Alert(title: Text("Unknown Alert"))
  149. }
  150. }
  151. }
  152. }
  153. // MARK: - Functions
  154. private func activateOverride(_ override: ProfileManager.TrioOverride) {
  155. isLoading = true
  156. pushNotificationManager.sendOverridePushNotification(override: override) { success, errorMessage in
  157. DispatchQueue.main.async {
  158. self.isLoading = false
  159. if success {
  160. self.statusMessage = String(localized: "Override command sent successfully.")
  161. self.alertType = .statusSuccess
  162. LogManager.shared.log(category: .apns, message: "sendOverridePushNotification succeeded for override: \(override.name)")
  163. } else {
  164. self.statusMessage = errorMessage ?? String(localized: "Failed to send override command.")
  165. self.alertType = .statusFailure
  166. LogManager.shared.log(category: .apns, message: "sendOverridePushNotification failed for override: \(override.name). Error: \(errorMessage ?? "unknown error")")
  167. }
  168. self.showAlert = true
  169. }
  170. }
  171. }
  172. private func cancelOverride() {
  173. isLoading = true
  174. pushNotificationManager.sendCancelOverridePushNotification { success, errorMessage in
  175. DispatchQueue.main.async {
  176. self.isLoading = false
  177. if success {
  178. self.statusMessage = String(localized: "Cancel override command sent successfully.")
  179. self.alertType = .statusSuccess
  180. LogManager.shared.log(category: .apns, message: "sendCancelOverridePushNotification succeeded")
  181. } else {
  182. self.statusMessage = errorMessage ?? String(localized: "Failed to send cancel override command.")
  183. self.alertType = .statusFailure
  184. LogManager.shared.log(category: .apns, message: "sendCancelOverridePushNotification failed. Error: \(errorMessage ?? "unknown error")")
  185. }
  186. self.showAlert = true
  187. }
  188. }
  189. }
  190. }