forked from shadowfacts/Tusker
Fix push notifications on Pleroma/Akkoma and older Mastodon versions
This commit is contained in:
parent
475b9911b1
commit
05cfecb797
|
@ -184,6 +184,31 @@ public final class InstanceFeatures: ObservableObject {
|
|||
hasMastodonVersion(4, 2, 0) || instanceType.isMastodon(.hometown(nil))
|
||||
}
|
||||
|
||||
public var pushNotificationTypeStatus: Bool {
|
||||
hasMastodonVersion(3, 3, 0)
|
||||
}
|
||||
|
||||
public var pushNotificationTypeFollowRequest: Bool {
|
||||
hasMastodonVersion(3, 1, 0)
|
||||
}
|
||||
|
||||
public var pushNotificationTypeUpdate: Bool {
|
||||
hasMastodonVersion(3, 5, 0)
|
||||
}
|
||||
|
||||
public var pushNotificationPolicy: Bool {
|
||||
hasMastodonVersion(3, 5, 0)
|
||||
}
|
||||
|
||||
public var pushNotificationPolicyMissingFromResponse: Bool {
|
||||
switch instanceType {
|
||||
case .mastodon(_, let version):
|
||||
return version >= Version(3, 5, 0) && version < Version(4, 1, 0)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
public init() {
|
||||
}
|
||||
|
||||
|
|
|
@ -9,11 +9,11 @@
|
|||
import Foundation
|
||||
|
||||
public struct PushSubscription: Decodable, Sendable {
|
||||
public let id: String
|
||||
public let endpoint: URL
|
||||
public let serverKey: String
|
||||
public let alerts: Alerts
|
||||
public let policy: Policy
|
||||
public var id: String
|
||||
public var endpoint: URL
|
||||
public var serverKey: String
|
||||
public var alerts: Alerts
|
||||
public var policy: Policy
|
||||
|
||||
public init(from decoder: any Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
@ -27,7 +27,8 @@ public struct PushSubscription: Decodable, Sendable {
|
|||
self.endpoint = try container.decode(URL.self, forKey: .endpoint)
|
||||
self.serverKey = try container.decode(String.self, forKey: .serverKey)
|
||||
self.alerts = try container.decode(PushSubscription.Alerts.self, forKey: .alerts)
|
||||
self.policy = try container.decode(PushSubscription.Policy.self, forKey: .policy)
|
||||
// added in mastodon 4.1.0
|
||||
self.policy = try container.decodeIfPresent(PushSubscription.Policy.self, forKey: .policy) ?? .all
|
||||
}
|
||||
|
||||
public static func create(endpoint: URL, publicKey: Data, authSecret: Data, alerts: Alerts, policy: Policy) -> Request<PushSubscription> {
|
||||
|
@ -96,6 +97,21 @@ extension PushSubscription {
|
|||
self.update = update
|
||||
}
|
||||
|
||||
public init(from decoder: any Decoder) throws {
|
||||
let container: KeyedDecodingContainer<PushSubscription.Alerts.CodingKeys> = try decoder.container(keyedBy: PushSubscription.Alerts.CodingKeys.self)
|
||||
self.mention = try container.decode(Bool.self, forKey: PushSubscription.Alerts.CodingKeys.mention)
|
||||
// status added in mastodon 3.3.0
|
||||
self.status = try container.decodeIfPresent(Bool.self, forKey: PushSubscription.Alerts.CodingKeys.status) ?? false
|
||||
self.reblog = try container.decode(Bool.self, forKey: PushSubscription.Alerts.CodingKeys.reblog)
|
||||
self.follow = try container.decode(Bool.self, forKey: PushSubscription.Alerts.CodingKeys.follow)
|
||||
// follow_request added in 3.1.0
|
||||
self.followRequest = try container.decodeIfPresent(Bool.self, forKey: PushSubscription.Alerts.CodingKeys.followRequest) ?? false
|
||||
self.favourite = try container.decode(Bool.self, forKey: PushSubscription.Alerts.CodingKeys.favourite)
|
||||
self.poll = try container.decode(Bool.self, forKey: PushSubscription.Alerts.CodingKeys.poll)
|
||||
// update added in mastodon 3.5.0
|
||||
self.update = try container.decodeIfPresent(Bool.self, forKey: PushSubscription.Alerts.CodingKeys.update) ?? false
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case mention
|
||||
case status
|
||||
|
|
|
@ -33,7 +33,13 @@ extension MastodonController {
|
|||
|
||||
func updatePushSubscription(alerts: PushNotifications.PushSubscription.Alerts, policy: PushNotifications.PushSubscription.Policy) async throws -> Pachyderm.PushSubscription {
|
||||
let req = Pachyderm.PushSubscription.update(alerts: .init(alerts), policy: .init(policy))
|
||||
return try await run(req).0
|
||||
var result = try await run(req).0
|
||||
if instanceFeatures.pushNotificationPolicyMissingFromResponse {
|
||||
// see https://github.com/mastodon/mastodon/issues/23145
|
||||
// so just assume if the request was successful that it worked
|
||||
result.policy = .init(policy)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func deletePushSubscription() async throws {
|
||||
|
|
|
@ -13,7 +13,6 @@ import PushNotifications
|
|||
import TuskerComponents
|
||||
|
||||
struct NotificationsPrefsView: View {
|
||||
@State private var error: NotificationsSetupError?
|
||||
@ObservedObject private var userAccounts = UserAccountsManager.shared
|
||||
|
||||
var body: some View {
|
||||
|
@ -48,14 +47,3 @@ struct NotificationsPrefsView: View {
|
|||
.navigationTitle("Notifications")
|
||||
}
|
||||
}
|
||||
|
||||
private enum NotificationsSetupError: LocalizedError {
|
||||
case requestingAuthorization(any Error)
|
||||
|
||||
var errorDescription: String? {
|
||||
switch self {
|
||||
case .requestingAuthorization(let error):
|
||||
"Notifications authorization request failed: \(error.localizedDescription)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import TuskerComponents
|
|||
|
||||
struct PushInstanceSettingsView: View {
|
||||
let account: UserAccountInfo
|
||||
let mastodonController: MastodonController
|
||||
@State private var mode: AsyncToggle.Mode
|
||||
@State private var error: Error?
|
||||
@State private var subscription: PushNotifications.PushSubscription?
|
||||
|
@ -22,6 +23,7 @@ struct PushInstanceSettingsView: View {
|
|||
@MainActor
|
||||
init(account: UserAccountInfo) {
|
||||
self.account = account
|
||||
self.mastodonController = .getForAccount(account)
|
||||
let subscription = PushManager.shared.pushSubscription(account: account)
|
||||
self.subscription = subscription
|
||||
self.mode = subscription == nil ? .off : .on
|
||||
|
@ -35,7 +37,7 @@ struct PushInstanceSettingsView: View {
|
|||
AsyncToggle("\(account.instanceURL.host!) notifications enabled", labelHidden: true, mode: $mode, onChange: updateNotificationsEnabled(enabled:))
|
||||
.labelsHidden()
|
||||
}
|
||||
PushSubscriptionView(account: account, subscription: subscription, updateSubscription: updateSubscription)
|
||||
PushSubscriptionView(account: account, mastodonController: mastodonController, subscription: subscription, updateSubscription: updateSubscription)
|
||||
}
|
||||
.alertWithData("An Error Occurred", data: $error) { data in
|
||||
Button("OK") {}
|
||||
|
|
|
@ -13,12 +13,13 @@ import TuskerComponents
|
|||
|
||||
struct PushSubscriptionView: View {
|
||||
let account: UserAccountInfo
|
||||
let mastodonController: MastodonController
|
||||
let subscription: PushSubscription?
|
||||
let updateSubscription: (PushSubscription.Alerts, PushSubscription.Policy) async -> Bool
|
||||
|
||||
var body: some View {
|
||||
if let subscription {
|
||||
PushSubscriptionSettingsView(account: account, subscription: subscription, updateSubscription: updateSubscription)
|
||||
PushSubscriptionSettingsView(account: account, mastodonController: mastodonController, subscription: subscription, updateSubscription: updateSubscription)
|
||||
} else {
|
||||
Text("No notifications")
|
||||
.font(.callout)
|
||||
|
@ -29,20 +30,16 @@ struct PushSubscriptionView: View {
|
|||
|
||||
private struct PushSubscriptionSettingsView: View {
|
||||
let account: UserAccountInfo
|
||||
let mastodonController: MastodonController
|
||||
let subscription: PushSubscription
|
||||
let updateSubscription: (PushSubscription.Alerts, PushSubscription.Policy) async -> Bool
|
||||
@State private var isLoading: [PushSubscription.Alerts: Bool] = [:]
|
||||
|
||||
init(account: UserAccountInfo, subscription: PushSubscription, updateSubscription: @escaping (PushSubscription.Alerts, PushSubscription.Policy) async -> Bool) {
|
||||
self.account = account
|
||||
self.subscription = subscription
|
||||
self.updateSubscription = updateSubscription
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
alertsToggles
|
||||
|
||||
if mastodonController.instanceFeatures.pushNotificationPolicy {
|
||||
AsyncPicker("From", alignment: .trailing, value: .constant(subscription.policy)) { newPolicy in
|
||||
await updateSubscription(subscription.alerts, newPolicy)
|
||||
} content: {
|
||||
|
@ -52,6 +49,7 @@ private struct PushSubscriptionSettingsView: View {
|
|||
}
|
||||
.pickerStyle(.menu)
|
||||
}
|
||||
}
|
||||
// this is the default value of the alignment guide, but this modifier is loading bearing
|
||||
.alignmentGuide(.prefsAvatar, computeValue: { dimension in
|
||||
dimension[.leading]
|
||||
|
@ -63,18 +61,35 @@ private struct PushSubscriptionSettingsView: View {
|
|||
private var alertsToggles: some View {
|
||||
GroupBox("Get notifications for") {
|
||||
VStack {
|
||||
toggle("All", alert: [.mention, .favorite, .reblog, .follow, .followRequest, .poll, .update])
|
||||
toggle("All", alert: allSupportedAlertTypes)
|
||||
toggle("Mentions", alert: .mention)
|
||||
toggle("Favorites", alert: .favorite)
|
||||
toggle("Reblogs", alert: .reblog)
|
||||
if mastodonController.instanceFeatures.pushNotificationTypeFollowRequest {
|
||||
toggle("Follows", alert: [.follow, .followRequest])
|
||||
} else {
|
||||
toggle("Follows", alert: .follow)
|
||||
}
|
||||
toggle("Polls finishing", alert: .poll)
|
||||
if mastodonController.instanceFeatures.pushNotificationTypeUpdate {
|
||||
toggle("Edits", alert: .update)
|
||||
}
|
||||
// status notifications not supported until we can enable/disable them in the app
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var allSupportedAlertTypes: PushSubscription.Alerts {
|
||||
var alerts: PushSubscription.Alerts = [.mention, .favorite, .reblog, .follow, .poll]
|
||||
if mastodonController.instanceFeatures.pushNotificationTypeFollowRequest {
|
||||
alerts.insert(.followRequest)
|
||||
}
|
||||
if mastodonController.instanceFeatures.pushNotificationTypeUpdate {
|
||||
alerts.insert(.update)
|
||||
}
|
||||
return alerts
|
||||
}
|
||||
|
||||
private func toggle(_ titleKey: LocalizedStringKey, alert: PushSubscription.Alerts) -> some View {
|
||||
let binding: Binding<AsyncToggle.Mode> = Binding {
|
||||
isLoading[alert] == true ? .loading : subscription.alerts.contains(alert) ? .on : .off
|
||||
|
|
Loading…
Reference in New Issue