diff --git a/src/context/globalInfo.tsx b/src/context/globalInfo.tsx index 6f3cf8cd..6919ef0c 100644 --- a/src/context/globalInfo.tsx +++ b/src/context/globalInfo.tsx @@ -1,6 +1,24 @@ import React, { createContext, useContext, useState } from "react"; import { AlertSnackbarProps } from "../components/ui/AlertSnackbar"; import { WhereWhatPair } from "maxun-core"; +import { QueryClient, QueryClientProvider, useQuery, useQueryClient } from '@tanstack/react-query'; +import { getStoredRuns, getStoredRecordings } from "../api/storage"; + +const createDataCacheClient = () => new QueryClient({ + defaultOptions: { + queries: { + staleTime: 30 * 1000, + gcTime: 5 * 60 * 1000, + retry: 2, + retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000), + } + } +}); + +const dataCacheKeys = { + runs: ['cached-runs'] as const, + recordings: ['cached-recordings'] as const, +} as const; interface RobotMeta { name: string; @@ -164,6 +182,65 @@ const globalInfoContext = createContext(globalInfoStore as GlobalInf export const useGlobalInfoStore = () => useContext(globalInfoContext); +export const useCachedRuns = () => { + return useQuery({ + queryKey: dataCacheKeys.runs, + queryFn: async () => { + const runs = await getStoredRuns(); + if (!runs) throw new Error('Failed to fetch runs data'); + return runs.map((run: any, index: number) => ({ id: index, ...run })); + }, + staleTime: 30 * 1000, + gcTime: 5 * 60 * 1000, + retry: 2, + }); +}; + +export const useCacheInvalidation = () => { + const queryClient = useQueryClient(); + + const invalidateRuns = () => { + queryClient.invalidateQueries({ queryKey: dataCacheKeys.runs }); + }; + + const invalidateRecordings = () => { + queryClient.invalidateQueries({ queryKey: dataCacheKeys.recordings }); + }; + + const addOptimisticRun = (newRun: any) => { + queryClient.setQueryData(dataCacheKeys.runs, (oldData: any) => { + if (!oldData) return [{ id: 0, ...newRun }]; + return [{ id: oldData.length, ...newRun }, ...oldData]; + }); + }; + + const invalidateAllCache = () => { + invalidateRuns(); + invalidateRecordings(); + }; + + return { + invalidateRuns, + invalidateRecordings, + addOptimisticRun, + invalidateAllCache + }; +}; + +export const useCachedRecordings = () => { + return useQuery({ + queryKey: dataCacheKeys.recordings, + queryFn: async () => { + const recordings = await getStoredRecordings(); + if (!recordings) throw new Error('Failed to fetch recordings data'); + return recordings; + }, + staleTime: 30 * 1000, + gcTime: 5 * 60 * 1000, + retry: 2, + }); +}; + export const GlobalInfoProvider = ({ children }: { children: JSX.Element }) => { const [browserId, setBrowserId] = useState(globalInfoStore.browserId); const [lastAction, setLastAction] = useState(globalInfoStore.lastAction); @@ -221,9 +298,12 @@ export const GlobalInfoProvider = ({ children }: { children: JSX.Element }) => { } } + const [dataCacheClient] = useState(() => createDataCacheClient()); + return ( - + { currentSnapshot, setCurrentSnapshot, updateDOMMode, - }} - > - {children} - + }} + > + {children} + + ); };