Tusker/Tusker/Screens/Preferences/Notifications/PushSubscriptionView.swift

151 lines
5.3 KiB
Swift

//
// PushSubscriptionView.swift
// Tusker
//
// Created by Shadowfacts on 4/7/24.
// Copyright © 2024 Shadowfacts. All rights reserved.
//
import SwiftUI
import UserAccounts
import PushNotifications
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, mastodonController: mastodonController, subscription: subscription, updateSubscription: updateSubscription)
} else {
Text("No notifications")
.font(.callout)
.foregroundStyle(.secondary)
}
}
}
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] = [:]
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: {
ForEach(PushSubscription.Policy.allCases) {
Text($0.displayName).tag($0)
}
}
.pickerStyle(.menu)
}
}
// this is the default value of the alignment guide, but this modifier is loading bearing
.alignmentGuide(.prefsAvatar, computeValue: { dimension in
dimension[.leading]
})
// otherwise the flexible view makes the containing stack extend under the edge of the list row
.padding(.leading, 38)
}
private var alertsToggles: some View {
GroupBox("Get notifications for") {
VStack {
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)
}
if mastodonController.instanceFeatures.emojiReactionNotifications {
toggle("Reactions", alert: .emojiReaction)
}
// status notifications not supported until we can enable/disable them in the app
}
}
.groupBoxStyle(AppBackgroundGroupBoxStyle())
}
private var allSupportedAlertTypes: PushSubscription.Alerts {
var all: PushSubscription.Alerts = [.mention, .favorite, .reblog, .follow, .poll]
if mastodonController.instanceFeatures.pushNotificationTypeFollowRequest {
all.insert(.followRequest)
}
if mastodonController.instanceFeatures.pushNotificationTypeUpdate {
all.insert(.update)
}
if mastodonController.instanceFeatures.emojiReactionNotifications {
all.insert(.emojiReaction)
}
return all
}
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
} set: { newValue in
isLoading[alert] = newValue == .loading
}
return AsyncToggle(titleKey, mode: binding) {
return await updateAlert(alert, isOn: $0)
}
}
private func updateAlert(_ alert: PushSubscription.Alerts, isOn: Bool) async -> Bool {
var newAlerts = subscription.alerts
if isOn {
newAlerts.insert(alert)
} else {
newAlerts.remove(alert)
}
return await updateSubscription(newAlerts, subscription.policy)
}
}
private extension PushSubscription.Policy {
var displayName: String {
switch self {
case .all:
"Anyone"
case .followed:
"Accounts you follow"
case .followers:
"Your followers"
}
}
}
private struct AppBackgroundGroupBoxStyle: GroupBoxStyle {
func makeBody(configuration: Configuration) -> some View {
VStack(alignment: .leading, spacing: 16) {
configuration.label
.font(.headline)
configuration.content
}
.padding()
.background(Color.appGroupedBackground, in: RoundedRectangle(cornerRadius: 8))
}
}
//#Preview {
// PushSubscriptionView()
//}