Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
43 changes: 12 additions & 31 deletions src/components/QueryExecutionStatus/QueryExecutionStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {HOUR_IN_SECONDS, SECOND_IN_MS} from '../../utils/constants';
import {useTypedSelector} from '../../utils/hooks';
import {isAxiosError} from '../../utils/response';

import {useElapsedDuration} from './useElapsedDuration';

import './QueryExecutionStatus.scss';

const b = cn('kv-query-execution-status');
Expand All @@ -29,38 +31,17 @@ export const QueryExecutionStatus = ({className, error, loading}: QueryExecution
let textColor: TextProps['color'];
const {startTime, endTime} = useTypedSelector(selectQueryDuration);

const [queryDuration, setQueryDuration] = React.useState<number>(
startTime ? (endTime || Date.now()) - startTime : 0,
);

const isCancelled = isQueryCancelledError(error);

const setDuration = React.useCallback(() => {
if (startTime) {
const actualEndTime = endTime || Date.now();
setQueryDuration(actualEndTime - startTime);
const durationMs = useElapsedDuration({startTime, endTime, loading});
const formattedDuration = React.useMemo(() => {
if (durationMs < 10 * SECOND_IN_MS) {
return duration(durationMs).format('mm:ss.SSS');
}
}, [endTime, startTime]);
return durationMs > HOUR_IN_SECONDS * SECOND_IN_MS
? duration(durationMs).format('hh:mm:ss')
: duration(durationMs).format('mm:ss');
}, [durationMs]);

React.useEffect(() => {
let timerId: ReturnType<typeof setInterval> | undefined;
setDuration();

if (loading) {
timerId = setInterval(setDuration, SECOND_IN_MS);
} else {
clearInterval(timerId);
}
return () => {
clearInterval(timerId);
};
}, [loading, setDuration]);

const formattedQueryDuration = React.useMemo(() => {
return queryDuration > HOUR_IN_SECONDS * SECOND_IN_MS
? duration(queryDuration).format('hh:mm:ss')
: duration(queryDuration).format('mm:ss');
}, [queryDuration]);
const isCancelled = isQueryCancelledError(error);

if (loading) {
theme = 'info';
Expand Down Expand Up @@ -96,7 +77,7 @@ export const QueryExecutionStatus = ({className, error, loading}: QueryExecution
size="m"
className={b(null, className)}
icon={icon}
value={formattedQueryDuration}
value={formattedDuration}
>
<Text color={textColor}>{label}</Text>
</Label>
Expand Down
63 changes: 63 additions & 0 deletions src/components/QueryExecutionStatus/useElapsedDuration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React from 'react';

import {SECOND_IN_MS} from '../../utils/constants';

const FAST_REFRESH_MS = 100;
const FAST_REFRESH_JITTER_MS = 20;
const TEN_SECONDS_IN_MS = 10 * SECOND_IN_MS;

interface UseElapsedDurationParams {
startTime?: number;
endTime?: number;
loading?: boolean;
}

export function useElapsedDuration({
startTime,
endTime,
loading,
}: UseElapsedDurationParams): number {
const [durationMs, setDurationMs] = React.useState<number>(
startTime ? (endTime || Date.now()) - startTime : 0,
);

const setDuration = React.useCallback(() => {
if (startTime) {
const actualEndTime = endTime || Date.now();
setDurationMs(actualEndTime - startTime);
}
}, [endTime, startTime]);

const timerRef = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
const cancelledRef = React.useRef<boolean>(false);

const tick = React.useCallback(() => {
if (cancelledRef.current) {
return;
}
setDuration();

if (!loading || !startTime || cancelledRef.current) {
return;
}

const actualEndTime = endTime || Date.now();
const elapsedMs = actualEndTime - startTime;
const jitter = Math.floor((Math.random() * 2 - 1) * FAST_REFRESH_JITTER_MS);
const nextDelay = elapsedMs < TEN_SECONDS_IN_MS ? FAST_REFRESH_MS + jitter : SECOND_IN_MS;
timerRef.current = setTimeout(tick, nextDelay);
}, [setDuration, loading, startTime, endTime]);

React.useEffect(() => {
cancelledRef.current = false;
tick();
return () => {
cancelledRef.current = true;
if (timerRef.current) {
clearTimeout(timerRef.current);
}
};
}, [tick]);

return durationMs;
}
Loading