Tusker/Tusker/Screens/Preferences/Notifications/PushInstanceSettingsView.swift

142 lines
5.3 KiB
Swift

//
// PushInstanceSettingsView.swift
// Tusker
//
// Created by Shadowfacts on 4/7/24.
// Copyright © 2024 Shadowfacts. All rights reserved.
//
import SwiftUI
import UserAccounts
import Pachyderm
import PushNotifications
import TuskerComponents
struct PushInstanceSettingsView: View {
let account: UserAccountInfo
let pushProxyRegistration: PushProxyRegistration
@State private var mode: AsyncToggle.Mode
@State private var error: Error?
@State private var subscription: PushNotifications.PushSubscription?
@State private var showReLoginRequiredAlert = false
@MainActor
init(account: UserAccountInfo, pushProxyRegistration: PushProxyRegistration) {
self.account = account
self.pushProxyRegistration = pushProxyRegistration
let subscription = PushManager.shared.pushSubscription(account: account)
self.subscription = subscription
self.mode = subscription == nil ? .off : .on
}
var body: some View {
VStack(alignment: .prefsAvatar) {
HStack {
PrefsAccountView(account: account)
Spacer()
AsyncToggle("\(account.instanceURL.host!) notifications enabled", labelHidden: true, mode: $mode, onChange: updateNotificationsEnabled(enabled:))
.labelsHidden()
}
PushSubscriptionView(account: account, subscription: subscription, updateSubscription: updateSubscription)
}
.alertWithData("An Error Occurred", data: $error) { data in
Button("OK") {}
} message: { data in
Text(data.localizedDescription)
}
.alert("Re-Login Required", isPresented: $showReLoginRequiredAlert) {
Button("Cancel", role: .cancel) {}
Button("Login") {
NotificationCenter.default.post(name: .reLogInRequired, object: account)
}
} message: {
Text("You must grant permission on \(account.instanceURL.host!) to turn on push notifications.")
}
}
private func updateNotificationsEnabled(enabled: Bool) async -> Bool {
if enabled {
do {
return try await enableNotifications()
} catch {
PushManager.logger.error("Error creating instance subscription: \(String(describing: error))")
self.error = .enabling(error)
return false
}
} else {
do {
try await disableNotifications()
} catch {
PushManager.logger.error("Error removing instance subscription: \(String(describing: error))")
self.error = .disabling(error)
return false
}
}
return true
}
private func enableNotifications() async throws -> Bool {
guard account.scopes?.contains(Scope.push.rawValue) == true else {
showReLoginRequiredAlert = true
return false
}
let subscription = try await PushManager.shared.createSubscription(account: account)
let mastodonController = await MastodonController.getForAccount(account)
do {
let result = try await mastodonController.createPushSubscription(subscription: subscription)
PushManager.logger.debug("Push subscription \(result.id) created on \(account.instanceURL)")
self.subscription = subscription
return true
} catch {
// if creation failed, remove the subscription locally as well
await PushManager.shared.removeSubscription(account: account)
throw error
}
}
private func disableNotifications() async throws {
let mastodonController = await MastodonController.getForAccount(account)
try await mastodonController.deletePushSubscription()
await PushManager.shared.removeSubscription(account: account)
subscription = nil
PushManager.logger.debug("Push subscription removed on \(account.instanceURL)")
}
private func updateSubscription(alerts: PushNotifications.PushSubscription.Alerts, policy: PushNotifications.PushSubscription.Policy) async -> Bool {
let mastodonController = await MastodonController.getForAccount(account)
do {
let result = try await mastodonController.updatePushSubscription(alerts: alerts, policy: policy)
PushManager.logger.debug("Push subscription \(result.id) updated on \(account.instanceURL)")
subscription?.alerts = alerts
subscription?.policy = policy
return true
} catch {
PushManager.logger.error("Error updating subscription: \(String(describing: error))")
self.error = .updating(error)
return false
}
}
}
private enum Error: LocalizedError {
case enabling(any Swift.Error)
case disabling(any Swift.Error)
case updating(any Swift.Error)
var errorDescription: String? {
switch self {
case .enabling(let error):
"Enabling push: \(error.localizedDescription)"
case .disabling(let error):
"Disabling push: \(error.localizedDescription)"
case .updating(let error):
"Updating settings: \(error.localizedDescription)"
}
}
}
//#Preview {
// PushInstanceSettingsView()
//}