diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index a8f799828..5ac7696b3 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -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 } diff --git a/src/components/middle/composer/MessageInput.tsx b/src/components/middle/composer/MessageInput.tsx index b0163fec3..75dea1251 100644 --- a/src/components/middle/composer/MessageInput.tsx +++ b/src/components/middle/composer/MessageInput.tsx @@ -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 = ({ }, [html]); const focusInput = useCallback(() => { - // Avoid focusing during animation - if (inputRef.current!.closest('.from, .to')) { + if (isHeavyAnimating()) { setTimeout(focusInput, FOCUS_DELAY_MS); return; } diff --git a/src/global/cache.ts b/src/global/cache.ts index fe7690643..78a6de808 100644 --- a/src/global/cache.ts +++ b/src/global/cache.ts @@ -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; - }); -} diff --git a/src/hooks/useHeavyAnimationCheck.ts b/src/hooks/useHeavyAnimationCheck.ts index 84075b4b1..a94307497 100644 --- a/src/hooks/useHeavyAnimationCheck.ts +++ b/src/hooks/useHeavyAnimationCheck.ts @@ -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]); -}; +} diff --git a/src/lib/teact/teactn.tsx b/src/lib/teact/teactn.tsx index 1000ea6bb..47879bf4e 100644 --- a/src/lib/teact/teactn.tsx +++ b/src/lib/teact/teactn.tsx @@ -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(); +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) { diff --git a/src/util/schedulers.ts b/src/util/schedulers.ts index 6b5a5177f..6422ec2bc 100644 --- a/src/util/schedulers.ts +++ b/src/util/schedulers.ts @@ -104,11 +104,11 @@ export function throttleWith(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); }