admin/lib/queryClient.ts
alisaza e1eaf5eff5 feat: initial ghabilee-admin backoffice app
Extract admin dashboard from ghabilee-frontend2 into a dedicated Next.js
app for backoffice.ghabilee.ir (no SEO indexing / Clarity).
2026-09-05 13:12:59 +03:30

102 lines
2.8 KiB
TypeScript

import type { PersistQueryClientProviderProps } from '@tanstack/react-query-persist-client'
import { QueryClient } from '@tanstack/react-query'
import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persister'
const DEFAULT_STALE_TIME_MS = 30_000
const DEFAULT_GC_TIME_MS = 5 * 60_000
export const makeQueryClient = () =>
new QueryClient({
defaultOptions: {
queries: {
staleTime: DEFAULT_STALE_TIME_MS,
gcTime: DEFAULT_GC_TIME_MS,
refetchOnWindowFocus: true,
refetchOnReconnect: true,
retry: 1,
},
},
})
let browserQueryClient: QueryClient | undefined
/** Browser singleton so layout remounts share one cache. */
export const getQueryClient = () => {
if (typeof window === 'undefined') {
return makeQueryClient()
}
browserQueryClient ??= makeQueryClient()
return browserQueryClient
}
const PERSIST_STORAGE_KEY = 'ghabilee-admin-query-cache-v1'
const LEGACY_PERSIST_STORAGE_KEY = 'ghabilee-query-cache-v2'
const PERSIST_MAX_AGE_MS = 24 * 60 * 60_000
/** Inert stand-in for `window.localStorage` during SSR. */
const noopStorage: Storage = {
length: 0,
clear() {
/* SSR inert storage */
},
getItem: () => null,
key: () => null,
removeItem() {
/* SSR inert storage */
},
setItem() {
/* SSR inert storage */
},
}
const removeStorageKey = (key: string) => {
if (typeof window === 'undefined') return
try {
window.localStorage.removeItem(key)
} catch {
// Persistence is optional; blocked storage must not break the app.
}
}
let didRemoveLegacyPersistedCache = false
const removeLegacyPersistedCache = () => {
if (didRemoveLegacyPersistedCache || typeof window === 'undefined') return
removeStorageKey(LEGACY_PERSIST_STORAGE_KEY)
removeStorageKey('ghabilee-query-cache')
didRemoveLegacyPersistedCache = true
}
/** Remove persisted query state synchronously before ending a user session. */
export const clearPersistedQueryCache = () => {
removeStorageKey(PERSIST_STORAGE_KEY)
removeStorageKey(LEGACY_PERSIST_STORAGE_KEY)
removeStorageKey('ghabilee-query-cache')
}
/**
* Admin does not persist discovery/home-feed caches. Provider shape stays
* stable for hydration; dehydrate always returns false.
*/
export const getQueryPersistOptions = (): PersistQueryClientProviderProps['persistOptions'] => {
removeLegacyPersistedCache()
return {
// eslint-disable-next-line @typescript-eslint/no-deprecated -- sync persister intentional for localStorage
persister: createSyncStoragePersister({
storage: typeof window === 'undefined' ? noopStorage : window.localStorage,
key: PERSIST_STORAGE_KEY,
}),
maxAge: PERSIST_MAX_AGE_MS,
buster: process.env.NEXT_PUBLIC_APP_VERSION ?? '',
dehydrateOptions: {
shouldDehydrateQuery: () => false,
},
}
}