141 lines
4.9 KiB
Swift
141 lines
4.9 KiB
Swift
//
|
|
// 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.proxyRegistration
|
|
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, .providesAppNotificationSettings])
|
|
} 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()
|
|
return true
|
|
} catch {
|
|
self.error = .registering(error)
|
|
return false
|
|
}
|
|
}
|
|
|
|
private func unregister() async -> Bool {
|
|
do {
|
|
try await PushManager.shared.unregister()
|
|
pushProxyRegistration = nil
|
|
for subscription in PushManager.shared.subscriptions {
|
|
if let account = UserAccountsManager.shared.getAccount(id: subscription.accountID) {
|
|
let mastodonController = MastodonController.getForAccount(account)
|
|
do {
|
|
try await mastodonController.deletePushSubscription()
|
|
PushManager.shared.removeSubscription(account: account)
|
|
PushManager.logger.debug("Push subscription removed on \(account.instanceURL)")
|
|
// this is a bit of a hack. the PushInstanceSettingsViews need to know to update
|
|
// their @State variables after we remove the subscription
|
|
NotificationCenter.default.post(name: .pushSubscriptionRemoved, object: account.id)
|
|
} catch {
|
|
PushManager.logger.error("Erroring removing push subscription: \(String(describing: error))")
|
|
}
|
|
}
|
|
}
|
|
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)"
|
|
}
|
|
}
|
|
}
|
|
|
|
extension Notification.Name {
|
|
static let pushSubscriptionRemoved = Notification.Name("Tusker.pushSubscriptionRemoved")
|
|
}
|