// // NotificationsPrefsView.swift // Tusker // // Created by Shadowfacts on 4/6/24. // Copyright © 2024 Shadowfacts. All rights reserved. // import SwiftUI import UserNotifications import UserAccounts import PushNotifications import TuskerComponents struct NotificationsPrefsView: View { @State private var error: NotificationsSetupError? @State private var isSetup = AsyncToggle.Mode.off @State private var pushProxyRegistration: PushProxyRegistration? @ObservedObject private var userAccounts = UserAccountsManager.shared var body: some View { List { enableSection if isSetup == .on, let pushProxyRegistration { accountsSection(pushProxyRegistration: pushProxyRegistration) } } .listStyle(.insetGrouped) .appGroupedListBackground(container: PreferencesNavigationController.self) .navigationTitle("Notifications") } private var enableSection: some View { Section { AsyncToggle("Push Notifications", mode: $isSetup, onChange: isSetupChanged(newValue:)) } .appGroupedListRowBackground() .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 ? .on : .off if !UIApplication.shared.isRegisteredForRemoteNotifications { _ = await registerForRemoteNotifications() } } } private func accountsSection(pushProxyRegistration: PushProxyRegistration) -> some View { Section { ForEach(userAccounts.accounts) { account in PushInstanceSettingsView(account: account, pushProxyRegistration: pushProxyRegistration) } } .appGroupedListRowBackground() } private func isSetupChanged(newValue: Bool) async -> Bool { if newValue { return await startRegistration() } else { return await unregister() } } 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)" } } }