mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-05-17 03:56:45 +00:00
Fix menubar tab refresh recovery
This commit is contained in:
parent
403efd4727
commit
00c55873a4
3 changed files with 38 additions and 16 deletions
|
|
@ -100,8 +100,12 @@ final class AppStore {
|
|||
staleInteractivePayloadAgeSeconds != nil
|
||||
}
|
||||
|
||||
var hasMissingInteractivePayloadWithoutAttempt: Bool {
|
||||
cache[currentKey] == nil && !isCurrentKeyLoading && !hasAttemptedCurrentKeyLoad
|
||||
}
|
||||
|
||||
var shouldResetInteractiveRefreshPipeline: Bool {
|
||||
hasStaleLoading || hasStaleInteractivePayload
|
||||
hasStaleLoading || hasStaleInteractivePayload || hasMissingInteractivePayloadWithoutAttempt
|
||||
}
|
||||
|
||||
var staleInteractivePayloadAgeSeconds: Int? {
|
||||
|
|
@ -149,16 +153,7 @@ final class AppStore {
|
|||
/// all-provider data in parallel so tab strip costs stay in sync with the hero.
|
||||
func switchTo(period: Period) {
|
||||
selectedPeriod = period
|
||||
switchTask?.cancel()
|
||||
switchTask = Task {
|
||||
if selectedProvider == .all {
|
||||
await refresh(includeOptimize: false, force: true)
|
||||
} else {
|
||||
async let main: Void = refresh(includeOptimize: false, force: true)
|
||||
async let all: Void = refreshQuietly(period: period)
|
||||
_ = await (main, all)
|
||||
}
|
||||
}
|
||||
startInteractiveSelectionRefresh()
|
||||
}
|
||||
|
||||
/// Switch to a provider filter. Cancels any in-flight switch so rapid tab tapping only
|
||||
|
|
@ -166,13 +161,21 @@ final class AppStore {
|
|||
/// in parallel so the tab strip costs stay in sync with the hero.
|
||||
func switchTo(provider: ProviderFilter) {
|
||||
selectedProvider = provider
|
||||
startInteractiveSelectionRefresh()
|
||||
}
|
||||
|
||||
private func startInteractiveSelectionRefresh() {
|
||||
switchTask?.cancel()
|
||||
resetLoadingState()
|
||||
let period = selectedPeriod
|
||||
let provider = selectedProvider
|
||||
lastErrorByKey[PayloadCacheKey(period: period, provider: provider)] = nil
|
||||
switchTask = Task {
|
||||
if provider == .all {
|
||||
await refresh(includeOptimize: false, force: true)
|
||||
await refresh(includeOptimize: false, force: true, showLoading: true)
|
||||
} else {
|
||||
async let main: Void = refresh(includeOptimize: false, force: true)
|
||||
async let all: Void = refreshQuietly(period: selectedPeriod)
|
||||
async let main: Void = refresh(includeOptimize: false, force: true, showLoading: true)
|
||||
async let all: Void = refreshQuietly(period: period)
|
||||
_ = await (main, all)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -259,10 +259,18 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
|
|||
|
||||
@discardableResult
|
||||
private func clearStaleForceRefreshIfNeeded(now: Date = Date()) -> Bool {
|
||||
if let started = forceRefreshStartedAt, forceRefreshTask != nil {
|
||||
if forceRefreshTask != nil {
|
||||
guard let started = forceRefreshStartedAt else {
|
||||
NSLog("CodeBurn: force refresh task had no start timestamp - clearing")
|
||||
forceRefreshTask?.cancel()
|
||||
forceRefreshTask = nil
|
||||
forceRefreshGeneration &+= 1
|
||||
store.resetLoadingState()
|
||||
return true
|
||||
}
|
||||
let elapsed = now.timeIntervalSince(started)
|
||||
guard elapsed > forceRefreshWatchdogSeconds else { return false }
|
||||
NSLog("CodeBurn: force refresh stuck for %ds — cancelling and restarting", Int(elapsed))
|
||||
NSLog("CodeBurn: force refresh stuck for %ds - cancelling and restarting", Int(elapsed))
|
||||
forceRefreshTask?.cancel()
|
||||
forceRefreshTask = nil
|
||||
forceRefreshStartedAt = nil
|
||||
|
|
|
|||
|
|
@ -60,4 +60,15 @@ struct AppStoreRefreshRecoveryTests {
|
|||
#expect(!store.hasStaleInteractivePayload)
|
||||
#expect(!store.shouldResetInteractiveRefreshPipeline)
|
||||
}
|
||||
|
||||
@Test("missing unattempted payload triggers hard recovery")
|
||||
func missingUnattemptedPayloadTriggersHardRecovery() {
|
||||
let store = AppStore()
|
||||
|
||||
#expect(!store.hasCachedData)
|
||||
#expect(!store.hasAttemptedCurrentKeyLoad)
|
||||
#expect(store.needsInteractivePayloadRefresh)
|
||||
#expect(store.hasMissingInteractivePayloadWithoutAttempt)
|
||||
#expect(store.shouldResetInteractiveRefreshPipeline)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue