mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-07-18 05:24:36 +00:00
A launchd-spawned process gets separate TCC attribution from the LaunchServices app, so the out-of-process refresher prompted for "access data from other apps" on every run regardless of the GUI's grant. Remove it entirely: the in-app loop (timer survives sleep, popover-open recovery) is the source of truth and now writes menubar-status.json on each successful refresh. On upgrade, any leftover com.codeburn.refresh LaunchAgent is unloaded and deleted.
43 lines
1.8 KiB
Swift
43 lines
1.8 KiB
Swift
import Foundation
|
|
|
|
/// On-disk badge backstop. The app writes `menubar-status.json` on each successful
|
|
/// refresh and reads it back as a badge fallback after a restart, before the live
|
|
/// loop has produced a payload. Shares the `MenubarPayload` model with the live
|
|
/// path — no separate data model.
|
|
struct MenubarStatusCache {
|
|
let statusPath: String
|
|
|
|
/// Default location under `~/.cache/codeburn/`.
|
|
static func standard() -> MenubarStatusCache {
|
|
let home = FileManager.default.homeDirectoryForCurrentUser.path
|
|
return MenubarStatusCache(statusPath: "\(home)/.cache/codeburn/menubar-status.json")
|
|
}
|
|
|
|
struct BadgeRead {
|
|
let payload: MenubarPayload
|
|
let ageSeconds: TimeInterval
|
|
}
|
|
|
|
/// Decodes the status file and returns it with its age (from the file's
|
|
/// mtime). Returns nil when the file is missing, corrupt, unreadable, or
|
|
/// older than `maxAgeSeconds` — every failure mode silently falls back to
|
|
/// the in-memory payload, never crashes.
|
|
func readBadgePayload(maxAgeSeconds: TimeInterval) -> BadgeRead? {
|
|
guard let attrs = try? FileManager.default.attributesOfItem(atPath: statusPath),
|
|
let mtime = attrs[.modificationDate] as? Date else {
|
|
return nil
|
|
}
|
|
let age = Date().timeIntervalSince(mtime)
|
|
guard age >= 0, age <= maxAgeSeconds else { return nil }
|
|
guard let data = try? SafeFile.read(from: statusPath),
|
|
let payload = try? JSONDecoder().decode(MenubarPayload.self, from: data) else {
|
|
return nil
|
|
}
|
|
return BadgeRead(payload: payload, ageSeconds: age)
|
|
}
|
|
|
|
func writeStatus(_ payload: MenubarPayload) throws {
|
|
let data = try JSONEncoder().encode(payload)
|
|
try SafeFile.write(data, to: statusPath, mode: 0o600)
|
|
}
|
|
}
|