mirror of
https://github.com/carey314/mio-plugin-worker.git
synced 2026-08-10 07:04:32 +00:00
78 lines
3.2 KiB
Swift
78 lines
3.2 KiB
Swift
|
|
//
|
||
|
|
// NotificationCenter.swift
|
||
|
|
// 摸鱼侠 plugin v0.1
|
||
|
|
//
|
||
|
|
// Thin wrapper around UNUserNotificationCenter. Plugin bundles inherit
|
||
|
|
// the host app's bundle identifier for notification authorization, so
|
||
|
|
// the host (Mio Island) needs to have notifications allowed in System
|
||
|
|
// Settings. We request once on activate; if it's denied we fall back
|
||
|
|
// to the in-panel red-dot pulse pattern.
|
||
|
|
//
|
||
|
|
|
||
|
|
import Foundation
|
||
|
|
@preconcurrency import UserNotifications
|
||
|
|
import AppKit
|
||
|
|
|
||
|
|
@MainActor
|
||
|
|
final class WorkerNotificationCenter {
|
||
|
|
static let shared = WorkerNotificationCenter()
|
||
|
|
|
||
|
|
private(set) var isAuthorized: Bool = false
|
||
|
|
private var didRequest: Bool = false
|
||
|
|
|
||
|
|
private init() {}
|
||
|
|
|
||
|
|
/// Asks the user once. Subsequent calls are no-ops.
|
||
|
|
func requestAuthorizationIfNeeded() {
|
||
|
|
guard !didRequest else { return }
|
||
|
|
didRequest = true
|
||
|
|
let center = UNUserNotificationCenter.current()
|
||
|
|
center.getNotificationSettings { settings in
|
||
|
|
let status = settings.authorizationStatus
|
||
|
|
switch status {
|
||
|
|
case .authorized, .provisional, .ephemeral:
|
||
|
|
Task { @MainActor in WorkerNotificationCenter.shared.isAuthorized = true }
|
||
|
|
case .denied:
|
||
|
|
Task { @MainActor in WorkerNotificationCenter.shared.isAuthorized = false }
|
||
|
|
WorkerDebugLog.write("notifications denied — falling back to in-panel dot")
|
||
|
|
case .notDetermined:
|
||
|
|
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound]) { granted, error in
|
||
|
|
if let error = error {
|
||
|
|
WorkerDebugLog.write("notif auth error: \(error)")
|
||
|
|
}
|
||
|
|
Task { @MainActor in WorkerNotificationCenter.shared.isAuthorized = granted }
|
||
|
|
WorkerDebugLog.write("notif auth granted=\(granted)")
|
||
|
|
}
|
||
|
|
@unknown default:
|
||
|
|
Task { @MainActor in WorkerNotificationCenter.shared.isAuthorized = false }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Fire-and-forget local notification. Returns true if scheduled
|
||
|
|
/// (best effort — auth status may flip between scheduling and firing).
|
||
|
|
@discardableResult
|
||
|
|
func notify(title: String, body: String, identifier: String = UUID().uuidString) -> Bool {
|
||
|
|
let content = UNMutableNotificationContent()
|
||
|
|
content.title = title
|
||
|
|
content.body = body
|
||
|
|
content.sound = .default
|
||
|
|
|
||
|
|
// Trigger immediately. nil means deliver right away. We use a
|
||
|
|
// 0.1s threshold trigger because some macOS builds are flaky
|
||
|
|
// with truly-instant local notifications from plugin bundles.
|
||
|
|
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 0.1, repeats: false)
|
||
|
|
let req = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)
|
||
|
|
UNUserNotificationCenter.current().add(req) { err in
|
||
|
|
if let err = err {
|
||
|
|
WorkerDebugLog.write("notif schedule error: \(err)")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Always bounce the dock as a backup signal — works even when
|
||
|
|
// notifications are denied.
|
||
|
|
NSApp.requestUserAttention(.criticalRequest)
|
||
|
|
return isAuthorized
|
||
|
|
}
|
||
|
|
}
|