diff --git a/react/src/components/BAIFetchKeyButton.tsx b/react/src/components/BAIFetchKeyButton.tsx index e22d837e55..fb831f7af0 100644 --- a/react/src/components/BAIFetchKeyButton.tsx +++ b/react/src/components/BAIFetchKeyButton.tsx @@ -16,6 +16,7 @@ interface BAIAutoRefetchButtonProps size?: ButtonProps['size']; onChange: (fetchKey: string) => void; hidden?: boolean; + pauseWhenHidden?: boolean; } const BAIFetchKeyButton: React.FC = ({ value, @@ -26,6 +27,7 @@ const BAIFetchKeyButton: React.FC = ({ size, hidden, lastLoadTime: lastLoadTimeProp, + pauseWhenHidden = true, ...buttonProps }) => { const { t } = useTranslation(); @@ -65,6 +67,7 @@ const BAIFetchKeyButton: React.FC = ({ }, showLastLoadTime ? 5_000 : null, lastLoadTime.toISOString(), + pauseWhenHidden, ); // remember when loading is done to display when the last fetch was done @@ -80,6 +83,7 @@ const BAIFetchKeyButton: React.FC = ({ }, // only start auto-updating after the previous loading is false(done). loading ? null : autoUpdateDelay, + pauseWhenHidden, ); const tooltipTitle = showLastLoadTime ? loadTimeMessage : undefined; diff --git a/react/src/hooks/useIntervalValue.tsx b/react/src/hooks/useIntervalValue.tsx index e669d83b6d..58c7c32fb9 100644 --- a/react/src/hooks/useIntervalValue.tsx +++ b/react/src/hooks/useIntervalValue.tsx @@ -5,24 +5,57 @@ import { useEffect, useRef, useState } from 'react'; * * @param callback The function to be executed at the specified interval. * @param delay The delay (in milliseconds) between each execution of the callback function. If `null`, the interval is cleared(pause). + * @param pauseWhenHidden Whether to pause the interval when the page becomes hidden. Defaults to true. */ -export function useInterval(callback: () => void, delay: number | null) { - const savedCallback = useRef<() => any>(null); +export function useInterval( + callback: () => void, + delay: number | null, + pauseWhenHidden: boolean = true, +) { + const savedCallback = useRef<(() => void) | null>(null); + const [isPageVisible, setIsPageVisible] = useState(() => + typeof document === 'undefined' ? true : !document.hidden, + ); useEffect(() => { savedCallback.current = callback; }); + // Handle page visibility changes + useEffect(() => { + if (!pauseWhenHidden || typeof window === 'undefined') return; + + const handleVisibilityChange = () => { + const visible = !document.hidden; + setIsPageVisible((prev) => { + if (delay !== null && !prev && visible) { + // Execute callback immediately when page becomes visible again + savedCallback.current?.(); + } + return visible; + }); + }; + + document.addEventListener('visibilitychange', handleVisibilityChange); + + return () => { + document.removeEventListener('visibilitychange', handleVisibilityChange); + }; + }, [pauseWhenHidden, delay]); + useEffect(() => { function tick() { savedCallback.current?.(); } if (delay !== null) { - let id = setInterval(tick, delay); - return () => clearInterval(id); + // Only check visibility if pauseWhenHidden is enabled + if (!pauseWhenHidden || isPageVisible) { + let id = setInterval(tick, delay); + return () => clearInterval(id); + } } - }, [delay]); + }, [delay, pauseWhenHidden, isPageVisible]); } /** @@ -31,14 +64,16 @@ export function useInterval(callback: () => void, delay: number | null) { * @param calculator - A function that calculates the value. * @param delay - The delay in milliseconds between updates. * @param triggerKey - An optional key that triggers an immediate update when changed. + * @param pauseWhenHidden - Whether to pause the interval when the page becomes hidden. Defaults to true. * @returns The updated value. */ -export const useIntervalValue = ( - calculator: () => any, +export function useIntervalValue( + calculator: () => T, delay: number | null, triggerKey?: string, -) => { - const [result, setResult] = useState(calculator()); + pauseWhenHidden: boolean = true, +): T { + const [result, setResult] = useState(calculator()); useEffect(() => { if (triggerKey) { @@ -47,10 +82,14 @@ export const useIntervalValue = ( // eslint-disable-next-line react-hooks/exhaustive-deps }, [triggerKey]); - useInterval(() => { - const newResult = calculator(); - if (newResult !== result) setResult(newResult); - }, delay); + useInterval( + () => { + const newResult = calculator(); + if (newResult !== result) setResult(newResult); + }, + delay, + pauseWhenHidden, + ); return result; -}; +}