[Perf] Introduce onFullyIdle to respect heavy animations

This commit is contained in:
Alexander Zinchuk 2024-09-06 15:42:25 +02:00
parent 4399c4fa8d
commit cfe7446d93
3 changed files with 35 additions and 16 deletions

View File

@ -28,7 +28,7 @@ import {
compact, pick, pickTruthy, unique, compact, pick, pickTruthy, unique,
} from '../util/iteratees'; } from '../util/iteratees';
import { encryptSession } from '../util/passcode'; import { encryptSession } from '../util/passcode';
import { onBeforeUnload, onIdle, throttle } from '../util/schedulers'; import { onBeforeUnload, throttle } from '../util/schedulers';
import { hasStoredSession } from '../util/sessions'; import { hasStoredSession } from '../util/sessions';
import { isUserId } from './helpers'; import { isUserId } from './helpers';
import { addActionHandler, getGlobal } from './index'; import { addActionHandler, getGlobal } from './index';
@ -44,11 +44,11 @@ import {
} from './selectors'; } from './selectors';
import { getIsMobile } from '../hooks/useAppLayout'; import { getIsMobile } from '../hooks/useAppLayout';
import { isHeavyAnimating } from '../hooks/useHeavyAnimationCheck'; import { isHeavyAnimating, onFullyIdle } from '../hooks/useHeavyAnimationCheck';
const UPDATE_THROTTLE = 5000; const UPDATE_THROTTLE = 5000;
const updateCacheThrottled = throttle(() => onIdle(() => updateCache()), UPDATE_THROTTLE, false); const updateCacheThrottled = throttle(() => onFullyIdle(() => updateCache()), UPDATE_THROTTLE, false);
const updateCacheForced = () => updateCache(true); const updateCacheForced = () => updateCache(true);
let isCaching = false; let isCaching = false;

View File

@ -2,7 +2,10 @@ import {
useCallback, useEffect, useMemo, useRef, useCallback, useEffect, useMemo, useRef,
} from '../lib/teact/teact'; } from '../lib/teact/teact';
import { requestMeasure } from '../lib/fasterdom/fasterdom';
import { createCallbackManager } from '../util/callbacks'; import { createCallbackManager } from '../util/callbacks';
import { onIdle } from '../util/schedulers';
import { createSignal } from '../util/signals';
import useLastCallback from './useLastCallback'; import useLastCallback from './useLastCallback';
// Make sure to end even if end callback was not called (which was some hardly-reproducible bug) // Make sure to end even if end callback was not called (which was some hardly-reproducible bug)
@ -12,13 +15,16 @@ const startCallbacks = createCallbackManager();
const endCallbacks = createCallbackManager(); const endCallbacks = createCallbackManager();
let timeout: number | undefined; let timeout: number | undefined;
let isAnimating = false;
const useHeavyAnimationCheck = ( const [getIsAnimating, setIsAnimating] = createSignal(false);
export const getIsHeavyAnimating = getIsAnimating;
export default function useHeavyAnimationCheck(
onStart?: AnyToVoidFunction, onStart?: AnyToVoidFunction,
onEnd?: AnyToVoidFunction, onEnd?: AnyToVoidFunction,
isDisabled = false, isDisabled = false,
) => { ) {
const lastOnStart = useLastCallback(onStart); const lastOnStart = useLastCallback(onStart);
const lastOnEnd = useLastCallback(onEnd); const lastOnEnd = useLastCallback(onEnd);
@ -27,7 +33,7 @@ const useHeavyAnimationCheck = (
return undefined; return undefined;
} }
if (isAnimating) { if (getIsAnimating()) {
lastOnStart(); lastOnStart();
} }
@ -39,8 +45,9 @@ const useHeavyAnimationCheck = (
startCallbacks.removeCallback(lastOnStart); startCallbacks.removeCallback(lastOnStart);
}; };
}, [isDisabled, lastOnEnd, lastOnStart]); }, [isDisabled, lastOnEnd, lastOnStart]);
}; }
// TODO → `onFullyIdle`?
export function useThrottleForHeavyAnimation<T extends AnyToVoidFunction>(afterHeavyAnimation: T, deps: unknown[]) { export function useThrottleForHeavyAnimation<T extends AnyToVoidFunction>(afterHeavyAnimation: T, deps: unknown[]) {
// eslint-disable-next-line react-hooks-static-deps/exhaustive-deps // eslint-disable-next-line react-hooks-static-deps/exhaustive-deps
const fnMemo = useCallback(afterHeavyAnimation, deps); const fnMemo = useCallback(afterHeavyAnimation, deps);
@ -50,7 +57,7 @@ export function useThrottleForHeavyAnimation<T extends AnyToVoidFunction>(afterH
return useMemo(() => { return useMemo(() => {
return (...args: Parameters<T>) => { return (...args: Parameters<T>) => {
if (!isScheduledRef.current) { if (!isScheduledRef.current) {
if (!isAnimating) { if (!getIsAnimating()) {
fnMemo(...args); fnMemo(...args);
return; return;
} }
@ -68,12 +75,12 @@ export function useThrottleForHeavyAnimation<T extends AnyToVoidFunction>(afterH
} }
export function isHeavyAnimating() { export function isHeavyAnimating() {
return isAnimating; return getIsAnimating();
} }
export function dispatchHeavyAnimationEvent(duration = AUTO_END_TIMEOUT) { export function dispatchHeavyAnimationEvent(duration = AUTO_END_TIMEOUT) {
if (!isAnimating) { if (!getIsAnimating()) {
isAnimating = true; setIsAnimating(true);
startCallbacks.runCallbacks(); startCallbacks.runCallbacks();
} }
@ -89,7 +96,7 @@ export function dispatchHeavyAnimationEvent(duration = AUTO_END_TIMEOUT) {
timeout = undefined; timeout = undefined;
} }
isAnimating = false; setIsAnimating(false);
endCallbacks.runCallbacks(); endCallbacks.runCallbacks();
} }
@ -98,4 +105,14 @@ export function dispatchHeavyAnimationEvent(duration = AUTO_END_TIMEOUT) {
return onEnd; return onEnd;
} }
export default useHeavyAnimationCheck; export function onFullyIdle(cb: NoneToVoidFunction, idleTimeout?: number) {
onIdle(() => {
if (getIsAnimating()) {
requestMeasure(() => {
onFullyIdle(cb, idleTimeout);
});
} else {
cb();
}
}, idleTimeout);
}

View File

@ -21,7 +21,9 @@ import {
import arePropsShallowEqual from './arePropsShallowEqual'; import arePropsShallowEqual from './arePropsShallowEqual';
import { createCallbackManager } from './callbacks'; import { createCallbackManager } from './callbacks';
import { areSortedArraysEqual, unique } from './iteratees'; import { areSortedArraysEqual, unique } from './iteratees';
import { onIdle, throttle } from './schedulers'; import { throttle } from './schedulers';
import { onFullyIdle } from '../hooks/useHeavyAnimationCheck';
interface FolderSummary { interface FolderSummary {
id: number; id: number;
@ -112,7 +114,7 @@ if (DEBUG) {
} }
const updateFolderManagerThrottled = throttle(() => { const updateFolderManagerThrottled = throttle(() => {
onIdle(() => { onFullyIdle(() => {
updateFolderManager(getGlobal()); updateFolderManager(getGlobal());
}); });
}, UPDATE_THROTTLE); }, UPDATE_THROTTLE);