summaryrefslogtreecommitdiff
path: root/DomainDig/LocalNotificationService.swift
blob: 7b43d67e9efb4578e713aa1de37a7ac6ba075287 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import Foundation
import UserNotifications

@MainActor
final class LocalNotificationService {
    static let shared = LocalNotificationService()

    private init() {}

    func configureForegroundPresentation() {
        UNUserNotificationCenter.current().delegate = NotificationCenterDelegate.shared
    }

    func requestAuthorizationIfNeeded() async -> Bool {
        let center = UNUserNotificationCenter.current()
        let settings = await center.notificationSettings()

        switch settings.authorizationStatus {
        case .authorized, .provisional, .ephemeral:
            return true
        case .notDetermined:
            return (try? await center.requestAuthorization(options: [.alert, .badge, .sound])) ?? false
        case .denied:
            return false
        @unknown default:
            return false
        }
    }

    func isAuthorizedForAlerts() async -> Bool {
        let settings = await UNUserNotificationCenter.current().notificationSettings()
        switch settings.authorizationStatus {
        case .authorized, .provisional, .ephemeral:
            return true
        case .denied, .notDetermined:
            return false
        @unknown default:
            return false
        }
    }

    func notifyDomainEvent(domain: String, message: String, severity: ChangeSeverity) async {
        await schedule(
            identifier: "domain-change-\(domain)",
            title: domain,
            body: message,
            interruptionLevel: severity == .high ? .timeSensitive : .active
        )
    }

    func notifyCertificateWarning(domain: String, daysRemaining: Int) async {
        await schedule(
            identifier: "cert-warning-\(domain)",
            title: domain,
            body: "Certificate expires in \(daysRemaining) days",
            interruptionLevel: .timeSensitive
        )
    }

    func notifyMonitoringAlert(
        domain: String,
        message: String,
        severity: MonitoringAlertSeverity
    ) async {
        let interruptionLevel: UNNotificationInterruptionLevel
        switch severity {
        case .critical:
            interruptionLevel = .timeSensitive
        case .warning, .info:
            interruptionLevel = .active
        }

        await schedule(
            identifier: "monitoring-\(domain)-\(UUID().uuidString)",
            title: domain,
            body: message,
            interruptionLevel: interruptionLevel
        )
    }

    func notifySweepComplete(summary: BatchSweepSummary) async {
        let body = "\(summary.changedDomains) changed, \(summary.warningDomains) warnings, \(summary.unchangedDomains) unchanged"
        await schedule(
            identifier: "sweep-complete",
            title: summary.source == .watchlistRefresh ? "Check All Complete" : "Batch Complete",
            body: body,
            interruptionLevel: .active
        )
    }

    private func schedule(
        identifier: String,
        title: String,
        body: String,
        interruptionLevel: UNNotificationInterruptionLevel
    ) async {
        let content = UNMutableNotificationContent()
        content.title = title
        content.body = body
        content.sound = .default
        content.interruptionLevel = interruptionLevel

        let request = UNNotificationRequest(
            identifier: identifier,
            content: content,
            trigger: UNTimeIntervalNotificationTrigger(timeInterval: 0.1, repeats: false)
        )

        try? await UNUserNotificationCenter.current().add(request)
    }
}

private final class NotificationCenterDelegate: NSObject, UNUserNotificationCenterDelegate {
    static let shared = NotificationCenterDelegate()

    func userNotificationCenter(
        _ center: UNUserNotificationCenter,
        willPresent notification: UNNotification
    ) async -> UNNotificationPresentationOptions {
        [.banner, .list, .sound]
    }
}