summaryrefslogtreecommitdiff
path: root/Shared/DomainDigWidgetData.swift
blob: 5ceae48a1fcea7cf92a2c96b20c1274994a53eeb (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
import Foundation

/// App Group + snapshot shared between the app (writer) and the widget (reader).
///
/// Deliberately self-contained: it references no app-target types (TrackedDomain,
/// PortfolioSnapshot, DomainHealth, …) so it compiles unchanged into the widget
/// extension. The app maps its richer types into these DTOs.
enum DomainDigWidgetStore {
    static let appGroupID = "group.net.cleberg.DomainDig"
    private static let key = "widgetData"

    private static var defaults: UserDefaults? {
        UserDefaults(suiteName: appGroupID)
    }

    static func write(_ data: DomainDigWidgetData) {
        guard let defaults, let encoded = try? JSONEncoder().encode(data) else { return }
        defaults.set(encoded, forKey: key)
    }

    static func read() -> DomainDigWidgetData? {
        guard let defaults,
              let encoded = defaults.data(forKey: key),
              let data = try? JSONDecoder().decode(DomainDigWidgetData.self, from: encoded)
        else { return nil }
        return data
    }
}

struct DomainDigWidgetData: Codable, Sendable {
    var generatedAt: Date
    var totalDomains: Int
    var healthyCount: Int
    var warningCount: Int
    var criticalCount: Int
    var expiringSoonCount: Int
    var unreachableCount: Int
    var domains: [DomainDigWidgetDomain]

    static let placeholder = DomainDigWidgetData(
        generatedAt: .distantPast,
        totalDomains: 3,
        healthyCount: 2,
        warningCount: 1,
        criticalCount: 0,
        expiringSoonCount: 1,
        unreachableCount: 0,
        domains: [
            DomainDigWidgetDomain(domain: "example.com", isPinned: true, status: .healthy, certDaysRemaining: 240, lastChange: nil),
            DomainDigWidgetDomain(domain: "cleberg.net", isPinned: false, status: .warning, certDaysRemaining: 21, lastChange: nil),
            DomainDigWidgetDomain(domain: "example.org", isPinned: false, status: .healthy, certDaysRemaining: 88, lastChange: nil)
        ]
    )

    /// Empty state used before the app has written any data.
    static let empty = DomainDigWidgetData(
        generatedAt: .distantPast,
        totalDomains: 0,
        healthyCount: 0,
        warningCount: 0,
        criticalCount: 0,
        expiringSoonCount: 0,
        unreachableCount: 0,
        domains: []
    )
}

struct DomainDigWidgetDomain: Codable, Sendable, Identifiable {
    var domain: String
    var isPinned: Bool
    var status: DomainDigWidgetStatus
    var certDaysRemaining: Int?
    var lastChange: Date?

    var id: String { domain }
}

enum DomainDigWidgetStatus: String, Codable, Sendable {
    case healthy
    case warning
    case critical
}