Files
docqube_frontend/src/context/CacheContext.tsx
T
2026-09-08 10:50:13 +05:30

118 lines
3.5 KiB
TypeScript

import React, { createContext, useContext, useState, useCallback, useMemo, useRef, ReactNode } from 'react';
import { DRIVE_FAVORITE_CHANGED_EVENT } from '@/services/drive';
interface CacheContextType {
get: (key: string) => any;
set: (key: string, value: any, ttl?: number) => void;
invalidate: (key: string) => void;
invalidatePattern: (pattern: string) => void;
clear: () => void;
}
interface CacheEntry {
value: any;
expiry: number;
}
const CacheContext = createContext<CacheContextType | undefined>(undefined);
export const CacheProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
const [cache, setCache] = useState<Map<string, CacheEntry>>(new Map());
const cacheRef = useRef(cache);
cacheRef.current = cache;
const get = useCallback((key: string) => {
const entry = cacheRef.current.get(key);
if (!entry) return null;
if (Date.now() > entry.expiry) {
setCache(prev => {
const newCache = new Map(prev);
newCache.delete(key);
return newCache;
});
return null;
}
return entry.value;
}, []);
const set = useCallback((key: string, value: any, ttl: number = Infinity) => {
setCache(prev => {
const newCache = new Map(prev);
newCache.set(key, {
value,
expiry: ttl === Infinity ? Infinity : Date.now() + ttl
});
return newCache;
});
}, []);
const invalidate = useCallback((key: string) => {
setCache(prev => {
const newCache = new Map(prev);
newCache.delete(key);
return newCache;
});
}, []);
const invalidatePattern = useCallback((pattern: string) => {
setCache(prev => {
const newCache = new Map(prev);
const regex = new RegExp(pattern.replace('*', '.*'));
for (const key of newCache.keys()) {
if (regex.test(key)) {
newCache.delete(key);
}
}
return newCache;
});
}, []);
const clear = useCallback(() => {
setCache(new Map());
}, []);
React.useEffect(() => {
const handleLogout = () => {
clear();
};
window.addEventListener('auth:logout', handleLogout);
return () => window.removeEventListener('auth:logout', handleLogout);
}, [clear]);
React.useEffect(() => {
const handleFavoriteChanged = () => {
invalidatePattern('mydocs:*');
invalidate('favorites:starred');
invalidate('dashboard:main');
invalidate('recent:combined');
invalidate('shared:with_me');
};
window.addEventListener(DRIVE_FAVORITE_CHANGED_EVENT, handleFavoriteChanged);
return () => window.removeEventListener(DRIVE_FAVORITE_CHANGED_EVENT, handleFavoriteChanged);
}, [invalidate, invalidatePattern]);
const value = useMemo(() => ({ get, set, invalidate, invalidatePattern, clear }), [get, set, invalidate, invalidatePattern, clear]);
return (
<CacheContext.Provider value={value}>
{children}
</CacheContext.Provider>
);
};
export const useCache = (): CacheContextType => {
const context = useContext(CacheContext);
if (!context) {
throw new Error('useCache must be used within a CacheProvider');
}
return context;
};