// // NotificationsPrefsView.swift // Tusker // // Created by Shadowfacts on 4/6/24. // Copyright © 2024 Shadowfacts. All rights reserved. // import SwiftUI import UserNotifications import UserAccounts import PushNotifications struct NotificationsPrefsView: View { @State private var error: NotificationsSetupError? @State private var isSetup = TriStateToggle.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 { HStack { Text("Push Notifications") Spacer() TriStateToggle(titleKey: "Push Notifications Enabled", 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 { if newValue { let success = await startRegistration() if !success { isSetup = .off } } else { let success = await unregister() if !success { isSetup = .on } } } 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)" } } }