[Perf] TeactN: Check for heavy animations

This commit is contained in:
Alexander Zinchuk 2021-12-10 18:33:12 +01:00
parent 9d58257319
commit ebf20fa013
6 changed files with 42 additions and 44 deletions

View File

@ -65,7 +65,7 @@ interface IWebpWorker extends Worker {
interface Window {
ClipboardItem?: any;
requestIdleCallback: (cb: AnyToVoidFunction) => void;
requestIdleCallback: (cb: AnyToVoidFunction, options:{ timeout?: number }) => void;
}
interface Clipboard { write?: any }

View File

@ -19,6 +19,7 @@ import {
import captureKeyboardListeners from '../../../util/captureKeyboardListeners';
import useLayoutEffectWithPrevDeps from '../../../hooks/useLayoutEffectWithPrevDeps';
import useFlag from '../../../hooks/useFlag';
import { isHeavyAnimating } from '../../../hooks/useHeavyAnimationCheck';
import parseEmojiOnlyString from '../../common/helpers/parseEmojiOnlyString';
import { isSelectionInsideInput } from './helpers/selection';
import useLang from '../../../hooks/useLang';
@ -125,8 +126,7 @@ const MessageInput: FC<OwnProps & StateProps & DispatchProps> = ({
}, [html]);
const focusInput = useCallback(() => {
// Avoid focusing during animation
if (inputRef.current!.closest('.from, .to')) {
if (isHeavyAnimating()) {
setTimeout(focusInput, FOCUS_DELAY_MS);
return;
}

View File

@ -17,7 +17,7 @@ import {
DEFAULT_PLAYBACK_RATE,
} from '../config';
import { IS_SINGLE_COLUMN_LAYOUT } from '../util/environment';
import { ANIMATION_END_EVENT, ANIMATION_START_EVENT } from '../hooks/useHeavyAnimationCheck';
import { isHeavyAnimating } from '../hooks/useHeavyAnimationCheck';
import { pick } from '../util/iteratees';
import { selectCurrentMessageList } from '../modules/selectors';
import { hasStoredSession } from '../util/sessions';
@ -31,11 +31,8 @@ const UPDATE_THROTTLE = 5000;
const updateCacheThrottled = throttle(() => onIdle(updateCache), UPDATE_THROTTLE, false);
let isCaching = false;
let isHeavyAnimating = false;
let unsubscribeFromBeforeUnload: NoneToVoidFunction | undefined;
setupHeavyAnimationListeners();
export function initCache() {
if (GLOBAL_STATE_CACHE_DISABLED) {
return;
@ -200,7 +197,7 @@ function migrateCache(cached: GlobalState, initialState: GlobalState) {
}
function updateCache() {
if (!isCaching || isHeavyAnimating) {
if (!isCaching || isHeavyAnimating()) {
return;
}
@ -344,12 +341,3 @@ function reduceGroupCalls(global: GlobalState): GlobalState['groupCalls'] {
isFallbackConfirmOpen: undefined,
};
}
function setupHeavyAnimationListeners() {
document.addEventListener(ANIMATION_START_EVENT, () => {
isHeavyAnimating = true;
});
document.addEventListener(ANIMATION_END_EVENT, () => {
isHeavyAnimating = false;
});
}

View File

@ -1,12 +1,35 @@
import { useEffect } from '../lib/teact/teact';
export const ANIMATION_START_EVENT = 'tt-event-heavy-animation-start';
export const ANIMATION_END_EVENT = 'tt-event-heavy-animation-end';
const ANIMATION_START_EVENT = 'tt-event-heavy-animation-start';
const ANIMATION_END_EVENT = 'tt-event-heavy-animation-end';
let timeout: number | undefined;
let isAnimating = false;
export const dispatchHeavyAnimationEvent = (duration?: number) => {
export default (
handleAnimationStart: AnyToVoidFunction,
handleAnimationEnd: AnyToVoidFunction,
) => {
useEffect(() => {
if (isAnimating) {
handleAnimationStart();
}
document.addEventListener(ANIMATION_START_EVENT, handleAnimationStart);
document.addEventListener(ANIMATION_END_EVENT, handleAnimationEnd);
return () => {
document.removeEventListener(ANIMATION_END_EVENT, handleAnimationEnd);
document.removeEventListener(ANIMATION_START_EVENT, handleAnimationStart);
};
}, [handleAnimationEnd, handleAnimationStart]);
};
export function isHeavyAnimating() {
return isAnimating;
}
export function dispatchHeavyAnimationEvent(duration?: number) {
if (!isAnimating) {
isAnimating = true;
document.dispatchEvent(new Event(ANIMATION_START_EVENT));
@ -29,23 +52,4 @@ export const dispatchHeavyAnimationEvent = (duration?: number) => {
isAnimating = false;
document.dispatchEvent(new Event(ANIMATION_END_EVENT));
};
};
export default (
handleAnimationStart: AnyToVoidFunction,
handleAnimationEnd: AnyToVoidFunction,
) => {
useEffect(() => {
if (isAnimating) {
handleAnimationStart();
}
document.addEventListener(ANIMATION_START_EVENT, handleAnimationStart);
document.addEventListener(ANIMATION_END_EVENT, handleAnimationEnd);
return () => {
document.removeEventListener(ANIMATION_END_EVENT, handleAnimationEnd);
document.removeEventListener(ANIMATION_START_EVENT, handleAnimationStart);
};
}, [handleAnimationEnd, handleAnimationStart]);
};
}

View File

@ -10,6 +10,7 @@ import arePropsShallowEqual, { getUnequalProps } from '../../util/arePropsShallo
import { orderBy } from '../../util/iteratees';
import { GlobalState, GlobalActions, ActionTypes } from '../../global/types';
import { handleError } from '../../util/handleError';
import { isHeavyAnimating } from '../../hooks/useHeavyAnimationCheck';
export default React;
@ -40,12 +41,17 @@ const containers = new Map<string, {
DEBUG_componentName: string;
}>();
const runCallbacksThrottled = throttleWithRaf(runCallbacks);
function runCallbacks() {
if (isHeavyAnimating()) {
runCallbacksThrottled();
return;
}
callbacks.forEach((cb) => cb(currentGlobal));
}
const runCallbacksThrottled = throttleWithRaf(runCallbacks);
// `noThrottle = true` is used as a workaround for iOS gesture history navigation
export function setGlobal(newGlobal?: GlobalState, noThrottle = false) {
if (typeof newGlobal === 'object' && newGlobal !== currentGlobal) {

View File

@ -104,11 +104,11 @@ export function throttleWith<F extends AnyToVoidFunction>(schedulerFn: Scheduler
};
}
export function onIdle(cb: NoneToVoidFunction) {
export function onIdle(cb: NoneToVoidFunction, timeout?: number) {
// eslint-disable-next-line no-restricted-globals
if (self.requestIdleCallback) {
// eslint-disable-next-line no-restricted-globals
self.requestIdleCallback(cb);
self.requestIdleCallback(cb, { timeout });
} else {
onTickEnd(cb);
}