Add push notifications

This commit is contained in:
Christoph Hagen
2025-02-18 22:05:19 +01:00
parent de6f474aea
commit 66a40e52a0
9 changed files with 285 additions and 4 deletions

View File

@ -0,0 +1,21 @@
struct NotificationRequest {
let accessToken: String
/// The notificiations in the available languages
let notifications: [String : WebNotification]
init(accessToken: String, notifications: [String : WebNotification]) {
self.accessToken = accessToken
self.notifications = notifications
}
func notification(for language: String) -> WebNotification? {
notifications[language] ?? notifications["en"]
}
}
extension NotificationRequest: Codable {
}

View File

@ -0,0 +1,73 @@
import Foundation
final class NotificationSender: ObservableObject {
@Published
var isTransmittingToRemote = false
@Published
var lastPushWasSuccessful = true
init() {
}
func failedToCreateNotificationDueToMissingSettings() {
guard !isTransmittingToRemote else { return }
DispatchQueue.main.async {
self.lastPushWasSuccessful = false
}
}
func send(url: String, request: NotificationRequest) {
guard !isTransmittingToRemote else { return }
DispatchQueue.main.async {
self.isTransmittingToRemote = true
}
guard let url = URL(string: url) else {
print("Invalid notification url: \(url)")
didFinish(success: false)
return
}
let body: Data
do {
body = try JSONEncoder().encode(request)
} catch {
print("Failed to encode notification: \(error)")
didFinish(success: false)
return
}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = body
Task {
do {
let (_ , result) = try await URLSession.shared.data(for: request)
guard let response = result as? HTTPURLResponse else {
print("Invalid response \(result)")
didFinish(success: false)
return
}
guard response.statusCode == 200 else {
print("Failed to send notification: \(response.statusCode)")
didFinish(success: false)
return
}
didFinish(success: true)
} catch {
print("Failed to send notification request: \(error)")
didFinish(success: false)
return
}
}
}
private func didFinish(success: Bool) {
DispatchQueue.main.async {
self.isTransmittingToRemote = false
self.lastPushWasSuccessful = success
}
}
}

View File

@ -0,0 +1,105 @@
import SwiftUI
import SFSafeSymbols
struct NotificationSheet: View {
@EnvironmentObject
private var content: Content
@EnvironmentObject
private var notifications: NotificationSender
@Environment(\.dismiss)
private var dismiss
@State
private var englishTitle = ""
@State
private var englishMessage = ""
@State
private var germanTitle = ""
@State
private var germanMessage = ""
private var uploadSymbol: SFSymbol {
if notifications.isTransmittingToRemote {
return .squareAndArrowUpBadgeClock
}
if !notifications.lastPushWasSuccessful {
return .squareAndArrowUpTrianglebadgeExclamationmark
}
return .squareAndArrowUp
}
var header: String {
let user = content.settings.general.remoteUserForUpload
let port = content.settings.general.remotePortForUpload
let url = content.settings.general.remotePathForUpload.withHttpPrefixRemoved
let path = content.settings.general.remotePathForUpload
return "\(user)@\(url):\(port)/\(path)"
}
var isIncomplete: Bool {
englishTitle.isEmpty || germanTitle.isEmpty || englishMessage.isEmpty || germanMessage.isEmpty
}
var body: some View {
VStack {
HStack {
Text("Send a notification to subscribers")
.font(.headline)
Spacer()
Button("Close", action: { dismiss() })
}
StringPropertyView(
title: "English title",
text: $englishTitle,
footer: "The title for devices using the english language")
StringPropertyView(
title: "English message",
text: $englishMessage,
footer: "The message for devices using the english language")
StringPropertyView(
title: "German title",
text: $germanTitle,
footer: "The title for devices using the german language")
StringPropertyView(
title: "German message",
text: $germanMessage,
footer: "The message for devices using the german language")
Button("Send", action: send)
.disabled(notifications.isTransmittingToRemote || isIncomplete)
}
.padding()
.frame(minWidth: 300, idealWidth: 400, idealHeight: 500)
}
private func send() {
guard let accessToken = content.settings.general.pushNotificationAccessToken,
let url = content.settings.general.urlForPushNotification else {
return
}
let english = WebNotification(title: englishTitle, body: englishMessage)
let german = WebNotification(title: germanTitle, body: germanMessage)
let notifications: [String : WebNotification] = [
"en": english,
"de": german
]
let request = NotificationRequest.init(
accessToken: accessToken,
notifications: notifications)
self.notifications.send(url: url, request: request)
}
}

View File

@ -0,0 +1,19 @@
struct WebNotification {
/// The title of the notification
/// - Note: Not supported on Safari for iOS
let title: String
/// The main content/message
let body: String
init(title: String, body: String) {
self.title = title
self.body = body
}
}
extension WebNotification: Codable {
}