Skip to content

Commit 05a9f66

Browse files
Clear loading state when superseded loads are abandoned.
Sign-out during an in-flight refresh could leave isLoading stuck on CI when the stale request returned early; also give the sign-out race test more time to observe the loading state before asserting. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 452d8d0 commit 05a9f66

2 files changed

Lines changed: 21 additions & 7 deletions

File tree

Octodot/App/AppState.swift

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,8 @@ final class AppState {
372372
cancelSecurityAlertsRefresh()
373373
cancelSubjectStateResolution()
374374
cancelAllPendingActions()
375+
committedActionsPersistTask?.cancel()
376+
committedActionsPersistTask = nil
375377
activeLoadRequestID = UUID()
376378
tokenDeleter()
377379
apiClient = nil
@@ -396,6 +398,15 @@ final class AppState {
396398
await loadNotifications(policy: .uniform(force: force))
397399
}
398400

401+
private func abandonLoadIfSuperseded(requestID: UUID, authRequestID: UUID) -> Bool {
402+
guard requestID == activeLoadRequestID,
403+
authRequestID == activeAuthRequestID else {
404+
isLoading = false
405+
return true
406+
}
407+
return false
408+
}
409+
399410
private func loadNotifications(policy: RefreshPolicy) async {
400411
guard let client = apiClient else { return }
401412
let authRequestID = activeAuthRequestID
@@ -418,8 +429,9 @@ final class AppState {
418429
maxPages: InboxStore.inboxRecentReadMaxPages
419430
)
420431
} catch {
421-
guard requestID == activeLoadRequestID,
422-
authRequestID == activeAuthRequestID else { return }
432+
if abandonLoadIfSuperseded(requestID: requestID, authRequestID: authRequestID) {
433+
return
434+
}
423435
if Self.isUnauthorized(error) {
424436
signOut()
425437
return
@@ -430,8 +442,9 @@ final class AppState {
430442
} else {
431443
fetchedRecentInbox = []
432444
}
433-
guard requestID == activeLoadRequestID,
434-
authRequestID == activeAuthRequestID else { return }
445+
if abandonLoadIfSuperseded(requestID: requestID, authRequestID: authRequestID) {
446+
return
447+
}
435448
applyLoadedNotifications(
436449
unreadNotifications: fetched,
437450
recentInboxNotifications: fetchedRecentInbox,
@@ -458,8 +471,9 @@ final class AppState {
458471
)
459472
logLastActionSnapshot(context: "after-load")
460473
} catch {
461-
guard requestID == activeLoadRequestID,
462-
authRequestID == activeAuthRequestID else { return }
474+
if abandonLoadIfSuperseded(requestID: requestID, authRequestID: authRequestID) {
475+
return
476+
}
463477
if Self.isUnauthorized(error) {
464478
signOut()
465479
return

OctodotTests/AppStateTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1677,7 +1677,7 @@ struct AppStateTests {
16771677
state.inboxMode = .unread
16781678

16791679
let loadTask = Task { await state.loadNotifications(force: true) }
1680-
await Self.waitUntil(intervalNanoseconds: 1_000_000) {
1680+
await Self.waitUntil(timeoutNanoseconds: 1_000_000_000) {
16811681
await MainActor.run { state.isLoading }
16821682
}
16831683
#expect(state.isLoading)

0 commit comments

Comments
 (0)