mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-05-16 19:44:14 +00:00
Refresh live quota progress from menubar
This commit is contained in:
parent
ce0e1eb116
commit
33649e0a65
1 changed files with 53 additions and 24 deletions
|
|
@ -6,6 +6,7 @@ private let refreshIntervalSeconds: UInt64 = 30
|
|||
private let nanosPerSecond: UInt64 = 1_000_000_000
|
||||
private let refreshIntervalNanos: UInt64 = refreshIntervalSeconds * nanosPerSecond
|
||||
private let forceRefreshWatchdogSeconds: TimeInterval = 90
|
||||
private let interactiveQuotaRefreshFloorSeconds: TimeInterval = 30
|
||||
private let statusItemWidth: CGFloat = NSStatusItem.variableLength
|
||||
private let popoverWidth: CGFloat = 360
|
||||
private let popoverHeight: CGFloat = 660
|
||||
|
|
@ -262,7 +263,8 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
|
|||
forceRefreshTask = Task {
|
||||
async let main: Void = store.refresh(includeOptimize: false, force: true, showLoading: true)
|
||||
async let today: Void = store.refreshQuietly(period: .today)
|
||||
_ = await (main, today)
|
||||
async let quotas: Bool = refreshLiveQuotaProgressIfDue()
|
||||
_ = await (main, today, quotas)
|
||||
refreshStatusButton()
|
||||
await MainActor.run { [weak self] in
|
||||
guard let self, self.forceRefreshGeneration == generation else { return }
|
||||
|
|
@ -296,6 +298,51 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
|
|||
}
|
||||
|
||||
fileprivate var lastSubscriptionRefreshAt: Date?
|
||||
fileprivate var lastCodexRefreshAt: Date?
|
||||
|
||||
@discardableResult
|
||||
private func refreshLiveQuotaProgressIfDue(force: Bool = false) async -> Bool {
|
||||
let cadence = SubscriptionRefreshCadence.current
|
||||
if !force && cadence == .manual { return false }
|
||||
|
||||
let now = Date()
|
||||
let threshold = force ? 0 : TimeInterval(cadence.rawValue)
|
||||
let shouldRefreshClaude = force || now.timeIntervalSince(lastSubscriptionRefreshAt ?? .distantPast) >= threshold
|
||||
let shouldRefreshCodex = force || now.timeIntervalSince(lastCodexRefreshAt ?? .distantPast) >= threshold
|
||||
guard shouldRefreshClaude || shouldRefreshCodex else { return false }
|
||||
|
||||
switch (shouldRefreshClaude, shouldRefreshCodex) {
|
||||
case (true, true):
|
||||
async let claude = store.refreshSubscriptionReportingSuccess()
|
||||
async let codex = store.refreshCodexReportingSuccess()
|
||||
if await claude { lastSubscriptionRefreshAt = Date() }
|
||||
if await codex { lastCodexRefreshAt = Date() }
|
||||
case (true, false):
|
||||
if await store.refreshSubscriptionReportingSuccess() {
|
||||
lastSubscriptionRefreshAt = Date()
|
||||
}
|
||||
case (false, true):
|
||||
if await store.refreshCodexReportingSuccess() {
|
||||
lastCodexRefreshAt = Date()
|
||||
}
|
||||
case (false, false):
|
||||
break
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private func refreshLiveQuotaProgressForPopoverOpen() {
|
||||
let now = Date()
|
||||
let claudeElapsed = now.timeIntervalSince(lastSubscriptionRefreshAt ?? .distantPast)
|
||||
let codexElapsed = now.timeIntervalSince(lastCodexRefreshAt ?? .distantPast)
|
||||
guard claudeElapsed >= interactiveQuotaRefreshFloorSeconds ||
|
||||
codexElapsed >= interactiveQuotaRefreshFloorSeconds else { return }
|
||||
|
||||
Task { [weak self] in
|
||||
guard let self else { return }
|
||||
_ = await self.refreshLiveQuotaProgressIfDue(force: true)
|
||||
}
|
||||
}
|
||||
|
||||
private func startRefreshLoop() {
|
||||
refreshLoopTask?.cancel()
|
||||
|
|
@ -303,10 +350,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
|
|||
// Provider refreshes only run when the user has explicitly connected.
|
||||
// Each refresh is a no-op until its corresponding bootstrap flag is set.
|
||||
if let self {
|
||||
async let claude = self.store.refreshSubscriptionReportingSuccess()
|
||||
async let codex = self.store.refreshCodexReportingSuccess()
|
||||
if await claude { self.lastSubscriptionRefreshAt = Date() }
|
||||
if await codex { self.lastCodexRefreshAt = Date() }
|
||||
await self.refreshLiveQuotaProgressIfDue(force: true)
|
||||
}
|
||||
while !Task.isCancelled {
|
||||
guard let self else { return }
|
||||
|
|
@ -332,26 +376,12 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
|
|||
// (not last attempt) so an intermittent failure doesn't reset
|
||||
// the timer. Each provider has its own anchor so a Codex 429
|
||||
// doesn't delay a due Claude refresh.
|
||||
let cadence = SubscriptionRefreshCadence.current
|
||||
if cadence != .manual {
|
||||
let claudeElapsed = Date().timeIntervalSince(self.lastSubscriptionRefreshAt ?? .distantPast)
|
||||
if claudeElapsed >= TimeInterval(cadence.rawValue) {
|
||||
let succeeded = await self.store.refreshSubscriptionReportingSuccess()
|
||||
if succeeded { self.lastSubscriptionRefreshAt = Date() }
|
||||
}
|
||||
let codexElapsed = Date().timeIntervalSince(self.lastCodexRefreshAt ?? .distantPast)
|
||||
if codexElapsed >= TimeInterval(cadence.rawValue) {
|
||||
let succeeded = await self.store.refreshCodexReportingSuccess()
|
||||
if succeeded { self.lastCodexRefreshAt = Date() }
|
||||
}
|
||||
}
|
||||
await self.refreshLiveQuotaProgressIfDue()
|
||||
try? await Task.sleep(nanoseconds: refreshIntervalNanos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate var lastCodexRefreshAt: Date?
|
||||
|
||||
@MainActor
|
||||
func refreshSubscriptionNow() {
|
||||
manualRefreshTask?.cancel()
|
||||
|
|
@ -376,8 +406,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
|
|||
// this match reality right now."
|
||||
let needsTodayTotal = self.store.selectedPeriod != .today || self.store.selectedProvider != .all
|
||||
async let payload: Void = self.store.refresh(includeOptimize: false, force: true, showLoading: true)
|
||||
async let claude: Bool = self.store.refreshSubscriptionReportingSuccess()
|
||||
async let codex: Bool = self.store.refreshCodexReportingSuccess()
|
||||
async let quotas: Bool = self.refreshLiveQuotaProgressIfDue(force: true)
|
||||
if needsTodayTotal {
|
||||
await self.store.refreshQuietly(period: .today)
|
||||
}
|
||||
|
|
@ -385,8 +414,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
|
|||
guard self.manualRefreshGeneration == generation, !Task.isCancelled else { return }
|
||||
self.lastRefreshTime = Date()
|
||||
self.refreshStatusButton()
|
||||
if await claude { self.lastSubscriptionRefreshAt = Date() }
|
||||
if await codex { self.lastCodexRefreshAt = Date() }
|
||||
_ = await quotas
|
||||
guard self.manualRefreshGeneration == generation, !Task.isCancelled else { return }
|
||||
self.manualRefreshTask = nil
|
||||
if self.refreshLoopTask == nil {
|
||||
|
|
@ -589,6 +617,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
|
|||
window.collectionBehavior.insert(.canJoinAllSpaces)
|
||||
window.makeKeyAndOrderFront(nil)
|
||||
}
|
||||
refreshLiveQuotaProgressForPopoverOpen()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue