frontend/app/lib/client.ts
2025-07-29 17:55:32 +02:00

138 lines
4 KiB
TypeScript

import { QueryClient } from '@tanstack/react-query';
import { persistQueryClient } from '@tanstack/query-persist-client-core';
import { get, set, del } from 'idb-keyval';
import LZString from 'lz-string';
const CACHE_KEY = 'khollise-cache'; // Key for IndexedDB storage
// Check if we're in a browser environment with IndexedDB support
const isIndexedDBAvailable = () => {
return typeof window !== 'undefined' &&
typeof window.indexedDB !== 'undefined' &&
window.indexedDB !== null;
};
// Custom IndexedDB persister with LZ-string compression
const createIDBPersister = () => {
// Return a no-op persister if IndexedDB is not available
if (!isIndexedDBAvailable()) {
console.warn('IndexedDB not available - cache persistence disabled');
return {
persistClient: async () => { },
restoreClient: async () => undefined,
removeClient: async () => { },
};
}
return {
persistClient: async (client: any) => {
try {
// Double-check IndexedDB availability before operation
if (!isIndexedDBAvailable()) {
console.warn('IndexedDB not available during persist operation');
return;
}
// Serialize the client data
const serializedClient = JSON.stringify(client);
// Compress the serialized data
const compressedData = LZString.compress(serializedClient);
// Store compressed data in IndexedDB
await set(CACHE_KEY, compressedData);
} catch (error) {
console.error('Failed to persist client cache:', error);
}
},
restoreClient: async () => {
try {
// Double-check IndexedDB availability before operation
if (!isIndexedDBAvailable()) {
console.warn('IndexedDB not available during restore operation');
return undefined;
}
// Get compressed data from IndexedDB
const compressedData = await get(CACHE_KEY);
if (!compressedData) {
console.log('No cached data found in IndexedDB');
return undefined;
}
// Decompress the data
const decompressedData = LZString.decompress(compressedData);
if (!decompressedData) {
console.warn('Failed to decompress cached data');
return undefined;
}
// Parse and return the client data
const client = JSON.parse(decompressedData);
console.log('Cache restored from IndexedDB');
return client;
} catch (error) {
console.error('Failed to restore client cache:', error);
// Clear corrupted cache if IndexedDB is available
if (isIndexedDBAvailable()) {
try {
await del(CACHE_KEY);
} catch (delError) {
console.error('Failed to clear corrupted cache:', delError);
}
}
return undefined;
}
},
removeClient: async () => {
try {
// Double-check IndexedDB availability before operation
if (!isIndexedDBAvailable()) {
console.warn('IndexedDB not available during remove operation');
return;
}
await del(CACHE_KEY);
console.log('Cache cleared from IndexedDB');
} catch (error) {
console.error('Failed to remove client cache:', error);
}
},
};
};
// Create QueryClient with persistence
const createQueryClient = () => {
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 1000 * 60 * 5, // 5 minutes
gcTime: 1000 * 60 * 60 * 24, // 24 hours (formerly cacheTime)
refetchOnWindowFocus: false,
retry: 2,
},
},
});
// Only set up persistence if IndexedDB is available
if (isIndexedDBAvailable()) {
persistQueryClient({
queryClient,
persister: createIDBPersister(),
maxAge: 1000 * 60 * 60 * 24, // 24 hours
buster: 'v1', // Change this to invalidate cache
})
} else {
console.warn('Cache persistence disabled - IndexedDB not available');
}
return queryClient;
};
const queryClient = createQueryClient();
export default queryClient;