bugger/Sources/Services/NotificationService.swift

106 lines
3.5 KiB
Swift

import AppKit
import Foundation
import UserNotifications
final class NotificationService: NSObject, UNUserNotificationCenterDelegate {
static let shared = NotificationService()
private let center = UNUserNotificationCenter.current()
private var isAuthorized = false
private override init() {
super.init()
center.delegate = self
}
func requestAuthorizationIfNeeded() async {
guard !isAuthorized else { return }
let granted = (try? await center.requestAuthorization(options: [.alert, .sound, .badge])) ?? false
isAuthorized = granted
}
func handleChanges(_ changes: [BugChange]) async {
await requestAuthorizationIfNeeded()
guard isAuthorized else { return }
let significant = changes
.filter { $0.type == .newBug || $0.type == .priorityChanged }
.prefix(3)
for change in significant {
deliver(change)
}
let statusChanges = changes.filter { $0.type == .statusChanged }
if statusChanges.count > 1 {
deliverBatchStatusChange(statusChanges)
} else if let single = statusChanges.first {
deliver(single)
}
}
private func deliver(_ change: BugChange) {
let content = UNMutableNotificationContent()
switch change.type {
case .newBug:
content.title = "New Bug Assigned"
content.body = "[\(change.bug.priority.rawValue)] \(change.bug.title)"
content.sound = .default
case .statusChanged:
content.title = "Bug Status Changed"
content.body = "\(change.bug.title)\(change.bug.status.rawValue)"
case .priorityChanged:
content.title = "Bug Priority Changed"
content.body = "\(change.bug.title)\(change.bug.priority.rawValue)"
case .assigneeChanged:
return
}
content.userInfo = [
"bugId": change.bug.id,
"feishuURL": change.bug.feishuURL.absoluteString
]
let request = UNNotificationRequest(
identifier: "bugger-\(change.bug.id)-\(Date().timeIntervalSince1970)",
content: content,
trigger: nil
)
center.add(request)
}
private func deliverBatchStatusChange(_ changes: [BugChange]) {
let content = UNMutableNotificationContent()
content.title = "\(changes.count) Bugs Updated"
content.body = changes.prefix(3).map { "\($0.bug.title)" }.joined(separator: "\n")
content.sound = .default
let request = UNNotificationRequest(
identifier: "bugger-batch-\(Date().timeIntervalSince1970)",
content: content,
trigger: nil
)
center.add(request)
}
func userNotificationCenter(
_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
) {
completionHandler([.banner, .sound])
}
func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void
) {
if let urlString = response.notification.request.content.userInfo["feishuURL"] as? String,
let url = URL(string: urlString) {
NSWorkspace.shared.open(url)
}
completionHandler()
}
}