import Foundation /// Persisted "what we've already notified about", so the background poller /// only fires a notification when something actually changes: /// - a PR that wasn't there before → "new PR" /// - a PR whose CI aggregate status changed → "CI status changed" struct NotificationState: Codable { /// PR ids (repo.fullName#number) we've already announced as new. var announcedNewPRs: Set = [] /// PR id → last notified CI status string ("success"/"failure"/"running"/etc). var ciStatusByPR: [String: String] = [:] private static let url: URL = { let dir = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0] return dir.appendingPathComponent("notification-state.json") }() func save() { guard let data = try? JSONEncoder().encode(self) else { return } try? data.write(to: Self.url) } static func load() -> NotificationState { guard let data = try? Data(contentsOf: url), let state = try? JSONDecoder().decode(NotificationState.self, from: data) else { return NotificationState() } return state } }