krz/hutch
an ios client for sourcehut
clone: git clone https://gitbay.org/krz/hutch.git
v2.3.1: Shared/NeedsAttentionSnapshot.swift · raw
1import Foundation
2#if canImport(WidgetKit)
3import WidgetKit
4#endif
5
6enum HutchAppGroup {
7 static let identifier = "group.net.cleberg.Hutch"
8}
9
10enum NeedsAttentionWidgetConfiguration {
11 static let kind = "NeedsAttentionWidget"
12}
13
14struct NeedsAttentionSnapshot: Codable, Sendable {
15 let unreadInboxThreads: Int?
16 let assignedOpenTickets: Int?
17 let failedBuilds: Int?
18 let updatedAt: Date
19
20 static let unavailable = NeedsAttentionSnapshot(
21 unreadInboxThreads: nil,
22 assignedOpenTickets: nil,
23 failedBuilds: nil,
24 updatedAt: .now
25 )
26
27 var hasAnyData: Bool {
28 unreadInboxThreads != nil || assignedOpenTickets != nil || failedBuilds != nil
29 }
30
31 var allCountsAreZero: Bool {
32 let counts = [unreadInboxThreads, assignedOpenTickets, failedBuilds].compactMap { $0 }
33 return !counts.isEmpty && counts.allSatisfy { $0 == 0 }
34 }
35}
36
37enum NeedsAttentionSnapshotStore {
38 private static let snapshotKey = "needsAttention.snapshot"
39
40 static func load(defaults: UserDefaults? = sharedDefaults()) -> NeedsAttentionSnapshot? {
41 guard let defaults,
42 let data = defaults.data(forKey: snapshotKey) else {
43 return nil
44 }
45
46 return try? JSONDecoder().decode(NeedsAttentionSnapshot.self, from: data)
47 }
48
49 static func save(_ snapshot: NeedsAttentionSnapshot, defaults: UserDefaults? = sharedDefaults()) {
50 guard let defaults,
51 let data = try? JSONEncoder().encode(snapshot) else {
52 return
53 }
54
55 defaults.set(data, forKey: snapshotKey)
56 reloadWidgetTimelines()
57 }
58
59 static func update(
60 unreadInboxThreads: Int? = nil,
61 assignedOpenTickets: Int? = nil,
62 failedBuilds: Int? = nil,
63 defaults: UserDefaults? = sharedDefaults()
64 ) {
65 let existing = load(defaults: defaults)
66 let snapshot = NeedsAttentionSnapshot(
67 unreadInboxThreads: unreadInboxThreads ?? existing?.unreadInboxThreads,
68 assignedOpenTickets: assignedOpenTickets ?? existing?.assignedOpenTickets,
69 failedBuilds: failedBuilds ?? existing?.failedBuilds,
70 updatedAt: .now
71 )
72 save(snapshot, defaults: defaults)
73 }
74
75 static func adjustUnreadInboxThreads(
76 by delta: Int,
77 defaults: UserDefaults? = sharedDefaults()
78 ) {
79 guard let existing = load(defaults: defaults),
80 let unreadInboxThreads = existing.unreadInboxThreads else {
81 return
82 }
83
84 save(
85 NeedsAttentionSnapshot(
86 unreadInboxThreads: max(0, unreadInboxThreads + delta),
87 assignedOpenTickets: existing.assignedOpenTickets,
88 failedBuilds: existing.failedBuilds,
89 updatedAt: .now
90 ),
91 defaults: defaults
92 )
93 }
94
95 static func clear(defaults: UserDefaults? = sharedDefaults()) {
96 defaults?.removeObject(forKey: snapshotKey)
97 reloadWidgetTimelines()
98 }
99
100 private static func sharedDefaults() -> UserDefaults? {
101 UserDefaults(suiteName: HutchAppGroup.identifier)
102 }
103
104 private static func reloadWidgetTimelines() {
105 #if canImport(WidgetKit)
106 WidgetCenter.shared.reloadTimelines(ofKind: NeedsAttentionWidgetConfiguration.kind)
107 #endif
108 }
109}