// // 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? @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) } } private func updateNotificationsEnabled(enabled: Bool) async { if enabled { do { try await enableNotifications() } catch { PushManager.logger.error("Error creating instance subscription: \(String(describing: error))") self.error = .enabling(error) } } else { do { try await disableNotifications() } catch { PushManager.logger.error("Error removing instance subscription: \(String(describing: error))") self.error = .disabling(error) } } } private func enableNotifications() async throws { let subscription = try await PushManager.shared.createSubscription(account: account) let req = Pachyderm.PushSubscription.create( endpoint: pushProxyRegistration.endpoint, publicKey: subscription.secretKey.publicKey.rawRepresentation, authSecret: subscription.authSecret, alerts: .init(subscription.alerts), policy: .init(subscription.policy) ) let mastodonController = await MastodonController.getForAccount(account) do { let (result, _) = try await mastodonController.run(req) PushManager.logger.debug("Push subscription \(result.id) created on \(account.instanceURL)") self.subscription = subscription } catch { // if creation failed, remove the subscription locally as well await PushManager.shared.removeSubscription(account: account) throw error } } private func disableNotifications() async throws { let req = Pachyderm.PushSubscription.delete() let mastodonController = await MastodonController.getForAccount(account) _ = try await mastodonController.run(req) 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 { try! await Task.sleep(nanoseconds: NSEC_PER_SEC) let req = Pachyderm.PushSubscription.update(alerts: .init(alerts), policy: .init(policy)) let mastodonController = await MastodonController.getForAccount(account) do { let (result, _) = try await mastodonController.run(req) PushManager.logger.debug("Push subscription \(result.id) updated on \(account.instanceURL)") subscription?.alerts = alerts subscription?.policy = policy } catch { PushManager.logger.error("Error updating subscription: \(String(describing: error))") self.error = .updating(error) } } } 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)" } } } private extension Pachyderm.PushSubscription.Alerts { init(_ alerts: PushNotifications.PushSubscription.Alerts) { self.init( mention: alerts.contains(.mention), status: alerts.contains(.status), reblog: alerts.contains(.reblog), follow: alerts.contains(.follow), followRequest: alerts.contains(.followRequest), favourite: alerts.contains(.favorite), poll: alerts.contains(.poll), update: alerts.contains(.update) ) } } private extension Pachyderm.PushSubscription.Policy { init(_ policy: PushNotifications.PushSubscription.Policy) { switch policy { case .all: self = .all case .followers: self = .followers case .followed: self = .followed } } } //#Preview { // PushInstanceSettingsView() //}