Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions react/src/components/BAIFetchKeyButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ interface BAIAutoRefetchButtonProps
size?: ButtonProps['size'];
onChange: (fetchKey: string) => void;
hidden?: boolean;
pauseWhenHidden?: boolean;
}
const BAIFetchKeyButton: React.FC<BAIAutoRefetchButtonProps> = ({
value,
Expand All @@ -26,6 +27,7 @@ const BAIFetchKeyButton: React.FC<BAIAutoRefetchButtonProps> = ({
size,
hidden,
lastLoadTime: lastLoadTimeProp,
pauseWhenHidden = true,
...buttonProps
}) => {
const { t } = useTranslation();
Expand Down Expand Up @@ -65,6 +67,7 @@ const BAIFetchKeyButton: React.FC<BAIAutoRefetchButtonProps> = ({
},
showLastLoadTime ? 5_000 : null,
lastLoadTime.toISOString(),
pauseWhenHidden,
);

// remember when loading is done to display when the last fetch was done
Expand All @@ -80,6 +83,7 @@ const BAIFetchKeyButton: React.FC<BAIAutoRefetchButtonProps> = ({
},
// only start auto-updating after the previous loading is false(done).
loading ? null : autoUpdateDelay,
pauseWhenHidden,
);

const tooltipTitle = showLastLoadTime ? loadTimeMessage : undefined;
Expand Down
67 changes: 53 additions & 14 deletions react/src/hooks/useIntervalValue.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
}

/**
Expand All @@ -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<T>(
calculator: () => T,
delay: number | null,
triggerKey?: string,
) => {
const [result, setResult] = useState(calculator());
pauseWhenHidden: boolean = true,
): T {
const [result, setResult] = useState<T>(calculator());

useEffect(() => {
if (triggerKey) {
Expand All @@ -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;
};
}