OverrideView.swift 9.3 KB

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