// // NotificationsPrefsView.swift // Tusker // // Created by Shadowfacts on 4/6/24. // Copyright © 2024 Shadowfacts. All rights reserved. // import SwiftUI import UserNotifications struct NotificationsPrefsView: View { @State private var error: NotificationsSetupError? @State private var isSetup = false @State private var working = false @State private var pushProxyRegistration: PushProxyRegistration? var body: some View { List { enableSection } .listStyle(.insetGrouped) .appGroupedListBackground(container: PreferencesNavigationController.self) .navigationTitle("Notifications") } private var enableSection: some View { Section { HStack { Text("Push Notifications") Spacer() if working { ProgressView() } else { Toggle("Push Notifications Enabled", isOn: Binding(get: { isSetup }, set: { newValue in isSetup = newValue isSetupChanged(newValue: newValue) })) .labelsHidden() } } } .alertWithData("An Error Occurred", data: $error) { error in Button("OK") {} } message: { error in Text(error.localizedDescription) } .task { @MainActor in pushProxyRegistration = PushManager.shared.pushProxyRegistration isSetup = pushProxyRegistration != nil if !UIApplication.shared.isRegisteredForRemoteNotifications { _ = await registerForRemoteNotifications() } } } private func isSetupChanged(newValue: Bool) { working = true Task { defer { working = false } let success = if newValue { await startRegistration() } else { await unregister() } if !success { isSetup = !newValue } } } private func startRegistration() async -> Bool { let authorized: Bool do { authorized = try await UNUserNotificationCenter.current().requestAuthorization(options: [.alert]) } catch { self.error = .requestingAuthorization(error) return false } guard authorized else { return false } return await registerForRemoteNotifications() } private func registerForRemoteNotifications() async -> Bool { do { pushProxyRegistration = try await PushManager.shared.register(transactionID: 0) return true } catch { self.error = .registering(error) return false } } private func unregister() async -> Bool { do { try await PushManager.shared.unregister() pushProxyRegistration = nil return true } catch { self.error = .unregistering(error) return false } } } private enum NotificationsSetupError: LocalizedError { case requestingAuthorization(any Error) case registering(any Error) case unregistering(any Error) var errorDescription: String? { switch self { case .requestingAuthorization(let error): "Notifications authorization request failed: \(error.localizedDescription)" case .registering(let error): "Registration failed: \(error.localizedDescription)" case .unregistering(let error): "Deactivation failed: \(error.localizedDescription)" } } }