diff --git a/libs/shared/providers/src/index.ts b/libs/shared/providers/src/index.ts index e880451bd..12f603e31 100644 --- a/libs/shared/providers/src/index.ts +++ b/libs/shared/providers/src/index.ts @@ -11,6 +11,7 @@ export * from './notifications'; export * from './prices'; export * from './rebaseBanner'; export * from './redeemer'; +export * from './refresher'; export * from './settings'; export * from './slippage'; export * from './swapper'; diff --git a/libs/shared/providers/src/refresher/hooks.ts b/libs/shared/providers/src/refresher/hooks.ts new file mode 100644 index 000000000..e4ecafb3e --- /dev/null +++ b/libs/shared/providers/src/refresher/hooks.ts @@ -0,0 +1,81 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { useCallback, useState } from 'react'; + +import { useIntervalEffect } from '@react-hookz/web'; +import { useQueryClient } from '@tanstack/react-query'; + +import type { QueryFunction, QueryKey } from '@tanstack/react-query'; + +export type RefreshStatus = + | 'idle' + | 'polling' + | 'processed' + | 'timeout' + | 'error'; + +export type UseRefresherProps = { + queryKey: QueryKey; + queryFn: QueryFunction; + isResultProcessed: (prev: QueryResult, next: QueryResult) => boolean; + maxRetries?: number; + interval?: number; +}; + +export const useRefresher = ({ + queryKey, + queryFn, + isResultProcessed, + maxRetries = 10, + interval = 2000, +}: UseRefresherProps) => { + const queryClient = useQueryClient(); + const prev = queryClient.getQueryData(queryKey); + const [retries, setRetries] = useState(0); + const [status, setStatus] = useState('idle'); + const [pollInterval, setPollInterval] = useState( + undefined, + ); + + const startRefresh = useCallback(() => { + setPollInterval(interval); + setStatus('polling'); + }, [interval]); + + const stopRefresh = useCallback(() => { + setPollInterval(undefined); + setStatus('idle'); + }, []); + + useIntervalEffect(() => { + (async () => { + const next = await queryClient.fetchQuery({ + queryKey, + queryFn, + staleTime: 0, + }); + + try { + if (!prev || !next) { + setPollInterval(undefined); + setStatus('error'); + } else if (retries > maxRetries) { + setPollInterval(undefined); + setStatus('timeout'); + } else if (isResultProcessed(prev, next)) { + setPollInterval(undefined); + setStatus('processed'); + } + } catch { + setPollInterval(undefined); + setStatus('error'); + } + setRetries((prev) => prev + 1); + })(); + }, pollInterval); + + return { + status, + startRefresh, + stopRefresh, + }; +}; diff --git a/libs/shared/providers/src/refresher/index.ts b/libs/shared/providers/src/refresher/index.ts new file mode 100644 index 000000000..4cc90d02b --- /dev/null +++ b/libs/shared/providers/src/refresher/index.ts @@ -0,0 +1 @@ +export * from './hooks';