feat(06-05): global session-expiry interstitial via QueryCache/MutationCache onError
- Add sessionExpired boolean + setSessionExpired action to Zustand calendarStore - main.tsx: construct QueryClient with QueryCache+MutationCache onError (v5 pattern, not defaultOptions.onError) - onGlobalError: checks instanceof SessionExpiredError, calls setSessionExpired(true) via store.getState() - CalendarShell: read sessionExpired from store; render AuthSplash(state=redirecting, 'Session expired', 'Signing you back in…') - CalendarShell: useEffect fires clearLoginRedirect + maybeRedirectToLogin after 1.5s when sessionExpired (one-shot guard re-armed) - Context7 /tanstack/query confirmed v5 QueryCache/MutationCache constructor + onError signature
This commit is contained in:
@@ -50,6 +50,12 @@ export interface CalendarStore {
|
||||
/** UID of the most recently enqueued write. SyncStateToast polls sync-status for this. */
|
||||
lastSyncedUid: string | null
|
||||
|
||||
// ── Session-expiry flag (Plan 06-05 / D-11) ─────────────────────────────
|
||||
// Set to true by the global QueryCache/MutationCache onError handler in main.tsx
|
||||
// when a SessionExpiredError is detected from any query or mutation.
|
||||
// Drives the "Session expired / Signing you back in…" interstitial in CalendarShell.
|
||||
sessionExpired: boolean
|
||||
|
||||
setSelectedView: (view: string) => void
|
||||
setSelectedDate: (date: string) => void
|
||||
setOpenEventId: (id: string | null) => void
|
||||
@@ -77,6 +83,14 @@ export interface CalendarStore {
|
||||
* Pass null to dismiss the toast.
|
||||
*/
|
||||
setLastSyncedUid: (uid: string | null) => void
|
||||
|
||||
/**
|
||||
* Arm the session-expiry interstitial (D-11).
|
||||
* Called imperatively from the global QueryCache/MutationCache onError handler
|
||||
* (outside React, via getState().setSessionExpired) when a SessionExpiredError
|
||||
* is caught from any query or mutation.
|
||||
*/
|
||||
setSessionExpired: (expired: boolean) => void
|
||||
}
|
||||
|
||||
// ── Helpers ────────────────────────────────────────────────────────────────
|
||||
@@ -141,6 +155,9 @@ export const useCalendarStore = create<CalendarStore>((set) => ({
|
||||
deleteDialogUid: null,
|
||||
lastSyncedUid: null,
|
||||
|
||||
// Session-expiry flag — false by default; set by global QueryCache/MutationCache handler
|
||||
sessionExpired: false,
|
||||
|
||||
setSelectedView: (view: string) => {
|
||||
set({ selectedView: view })
|
||||
// Persist to localStorage keyed by breakpoint group
|
||||
@@ -166,4 +183,6 @@ export const useCalendarStore = create<CalendarStore>((set) => ({
|
||||
set({ deleteDialogOpen: open, deleteDialogUid: uid }),
|
||||
|
||||
setLastSyncedUid: (uid: string | null) => set({ lastSyncedUid: uid }),
|
||||
|
||||
setSessionExpired: (expired: boolean) => set({ sessionExpired: expired }),
|
||||
}))
|
||||
|
||||
Reference in New Issue
Block a user