diff --git a/src/components/common/AnimatedSticker.tsx b/src/components/common/AnimatedSticker.tsx index b74fee06c..95c59da81 100644 --- a/src/components/common/AnimatedSticker.tsx +++ b/src/components/common/AnimatedSticker.tsx @@ -1,6 +1,7 @@ import type { RefObject } from 'react'; import type { FC } from '../../lib/teact/teact'; import React, { + getIsHeavyAnimating, memo, useEffect, useRef, useState, } from '../../lib/teact/teact'; @@ -17,7 +18,7 @@ import { IS_ELECTRON } from '../../util/windowEnvironment'; import useColorFilter from '../../hooks/stickers/useColorFilter'; import useEffectWithPrevDeps from '../../hooks/useEffectWithPrevDeps'; import useFlag from '../../hooks/useFlag'; -import useHeavyAnimationCheck, { isHeavyAnimating } from '../../hooks/useHeavyAnimationCheck'; +import useHeavyAnimation from '../../hooks/useHeavyAnimation'; import useLastCallback from '../../hooks/useLastCallback'; import usePriorityPlaybackCheck, { isPriorityPlaybackActive } from '../../hooks/usePriorityPlaybackCheck'; import useSharedIntersectionObserver from '../../hooks/useSharedIntersectionObserver'; @@ -101,8 +102,8 @@ const AnimatedSticker: FC = ({ // Delay initialization until heavy animation ends const [ canInitialize, markCanInitialize, unmarkCanInitialize, - ] = useFlag(!isHeavyAnimating() || shouldForceOnHeavyAnimation); - useHeavyAnimationCheck(unmarkCanInitialize, markCanInitialize, shouldForceOnHeavyAnimation); + ] = useFlag(!getIsHeavyAnimating() || shouldForceOnHeavyAnimation); + useHeavyAnimation(unmarkCanInitialize, markCanInitialize, shouldForceOnHeavyAnimation); useEffect(() => { if (shouldForceOnHeavyAnimation) markCanInitialize(); }, [shouldForceOnHeavyAnimation]); @@ -129,7 +130,7 @@ const AnimatedSticker: FC = ({ || isUnmountedRef.current || !tgsUrl || (sharedCanvas && (!sharedCanvasCoords || !sharedCanvas.offsetWidth || !sharedCanvas.offsetHeight)) - || (isHeavyAnimating() && !shouldForceOnHeavyAnimation) + || (getIsHeavyAnimating() && !shouldForceOnHeavyAnimation) ) { return; } @@ -252,7 +253,7 @@ const AnimatedSticker: FC = ({ } }, [playAnimation, animation, tgsUrl]); - useHeavyAnimationCheck(pauseAnimation, playAnimation, !playKey || shouldForceOnHeavyAnimation); + useHeavyAnimation(pauseAnimation, playAnimation, !playKey || shouldForceOnHeavyAnimation); usePriorityPlaybackCheck(pauseAnimation, playAnimation, !playKey || forceAlways); // Pausing frame may not happen in background, // so we need to make sure it happens right after focusing, @@ -282,5 +283,5 @@ export default memo(AnimatedSticker); function isFrozen(forceAlways = false, forceOnHeavyAnimation = false) { if (forceAlways) return false; - return (!forceOnHeavyAnimation && isHeavyAnimating()) || isPriorityPlaybackActive() || isBackgroundModeActive(); + return (!forceOnHeavyAnimation && getIsHeavyAnimating()) || isPriorityPlaybackActive() || isBackgroundModeActive(); } diff --git a/src/components/common/embedded/EmojiIconBackground.tsx b/src/components/common/embedded/EmojiIconBackground.tsx index e60d01d33..9cf0bcde6 100644 --- a/src/components/common/embedded/EmojiIconBackground.tsx +++ b/src/components/common/embedded/EmojiIconBackground.tsx @@ -9,7 +9,7 @@ import { preloadImage } from '../../../util/files'; import { REM } from '../helpers/mediaDimensions'; import useDynamicColorListener from '../../../hooks/stickers/useDynamicColorListener'; -import { useThrottleForHeavyAnimation } from '../../../hooks/useHeavyAnimationCheck'; +import { useThrottleForHeavyAnimation } from '../../../hooks/useHeavyAnimation'; import useLastCallback from '../../../hooks/useLastCallback'; import useMedia from '../../../hooks/useMedia'; import useOldLang from '../../../hooks/useOldLang'; diff --git a/src/components/left/main/ForumPanel.tsx b/src/components/left/main/ForumPanel.tsx index 9dc1e1852..99c8b3068 100644 --- a/src/components/left/main/ForumPanel.tsx +++ b/src/components/left/main/ForumPanel.tsx @@ -1,5 +1,6 @@ import type { FC } from '../../../lib/teact/teact'; import React, { + beginHeavyAnimation, memo, useEffect, useMemo, useRef, useState, } from '../../../lib/teact/teact'; import { getActions, withGlobal } from '../../../global'; @@ -22,7 +23,6 @@ import { waitForTransitionEnd } from '../../../util/cssAnimationEndListeners'; import { IS_TOUCH_ENV } from '../../../util/windowEnvironment'; import useAppLayout from '../../../hooks/useAppLayout'; -import { dispatchHeavyAnimationEvent } from '../../../hooks/useHeavyAnimationCheck'; import useHistoryBack from '../../../hooks/useHistoryBack'; import useInfiniteScroll from '../../../hooks/useInfiniteScroll'; import { useIntersectionObserver, useOnIntersect } from '../../../hooks/useIntersectionObserver'; @@ -148,8 +148,8 @@ const ForumPanel: FC = ({ requestNextMutation(() => { if (!ref.current) return; - const dispatchHeavyAnimationStop = dispatchHeavyAnimationEvent(); - waitForTransitionEnd(ref.current, dispatchHeavyAnimationStop); + const endHeavyAnimation = beginHeavyAnimation(); + waitForTransitionEnd(ref.current, endHeavyAnimation); onOpenAnimationStart?.(); diff --git a/src/components/main/Main.tsx b/src/components/main/Main.tsx index fbdef7e36..373c0b59d 100644 --- a/src/components/main/Main.tsx +++ b/src/components/main/Main.tsx @@ -1,6 +1,7 @@ import '../../global/actions/all'; import React, { + beginHeavyAnimation, memo, useEffect, useLayoutEffect, useRef, useState, } from '../../lib/teact/teact'; @@ -42,7 +43,6 @@ import useInterval from '../../hooks/schedulers/useInterval'; import useTimeout from '../../hooks/schedulers/useTimeout'; import useAppLayout from '../../hooks/useAppLayout'; import useForceUpdate from '../../hooks/useForceUpdate'; -import { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck'; import useLastCallback from '../../hooks/useLastCallback'; import usePreventPinchZoomGesture from '../../hooks/usePreventPinchZoomGesture'; import useShowTransition from '../../hooks/useShowTransition'; @@ -447,10 +447,10 @@ const Main = ({ }); } - const dispatchHeavyAnimationEnd = dispatchHeavyAnimationEvent(); + const endHeavyAnimation = beginHeavyAnimation(); waitForTransitionEnd(document.getElementById('MiddleColumn')!, () => { - dispatchHeavyAnimationEnd(); + endHeavyAnimation(); willAnimateLeftColumnRef.current = false; forceUpdate(); }); @@ -480,10 +480,10 @@ const Main = ({ willAnimateRightColumnRef.current = true; - const dispatchHeavyAnimationEnd = dispatchHeavyAnimationEvent(); + const endHeavyAnimation = beginHeavyAnimation(); waitForTransitionEnd(document.getElementById('RightColumn')!, () => { - dispatchHeavyAnimationEnd(); + endHeavyAnimation(); willAnimateRightColumnRef.current = false; forceUpdate(); setIsNarrowMessageList(isRightColumnOpen); diff --git a/src/components/mediaViewer/MediaViewer.tsx b/src/components/mediaViewer/MediaViewer.tsx index 36c1d8e1d..500c9d3d5 100644 --- a/src/components/mediaViewer/MediaViewer.tsx +++ b/src/components/mediaViewer/MediaViewer.tsx @@ -1,4 +1,5 @@ import React, { + beginHeavyAnimation, memo, useEffect, useMemo, useRef, } from '../../lib/teact/teact'; import { getActions, withGlobal } from '../../global'; @@ -40,7 +41,6 @@ import useAppLayout from '../../hooks/useAppLayout'; import useElectronDrag from '../../hooks/useElectronDrag'; import useFlag from '../../hooks/useFlag'; import useForceUpdate from '../../hooks/useForceUpdate'; -import { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck'; import useLastCallback from '../../hooks/useLastCallback'; import useOldLang from '../../hooks/useOldLang'; import { exitPictureInPictureIfNeeded, usePictureInPictureSignal } from '../../hooks/usePictureInPicture'; @@ -216,12 +216,12 @@ const MediaViewer = ({ useEffect(() => { if (isGhostAnimation && isOpen && (shouldAnimateOpening || !prevItem)) { - dispatchHeavyAnimationEvent(ANIMATION_DURATION + ANIMATION_END_DELAY); + beginHeavyAnimation(ANIMATION_DURATION + ANIMATION_END_DELAY); animateOpening(hasFooter, origin!, bestImageData!, dimensions!, isVideo, message, mediaIndex); } if (isGhostAnimation && !isOpen && prevItem) { - dispatchHeavyAnimationEvent(ANIMATION_DURATION + ANIMATION_END_DELAY); + beginHeavyAnimation(ANIMATION_DURATION + ANIMATION_END_DELAY); animateClosing(prevOrigin!, prevBestImageData!, prevMessage, prevItem?.mediaIndex); } }, [ diff --git a/src/components/middle/EmojiInteractionAnimation.tsx b/src/components/middle/EmojiInteractionAnimation.tsx index 864c37a4b..10bdbe187 100644 --- a/src/components/middle/EmojiInteractionAnimation.tsx +++ b/src/components/middle/EmojiInteractionAnimation.tsx @@ -1,5 +1,6 @@ import type { FC } from '../../lib/teact/teact'; import React, { + beginHeavyAnimation, memo, useEffect, useLayoutEffect, useRef, } from '../../lib/teact/teact'; import { getActions, withGlobal } from '../../global'; @@ -13,7 +14,6 @@ import buildClassName from '../../util/buildClassName'; import { IS_ANDROID } from '../../util/windowEnvironment'; import useFlag from '../../hooks/useFlag'; -import { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck'; import useLastCallback from '../../hooks/useLastCallback'; import useMedia from '../../hooks/useMedia'; @@ -74,11 +74,11 @@ const EmojiInteractionAnimation: FC = ({ }, [handleCancelAnimation]); useLayoutEffect(() => { - const dispatchHeavyAnimationStop = dispatchHeavyAnimationEvent(); + const endHeavyAnimation = beginHeavyAnimation(); timeoutRef.current = setTimeout(() => { stop(); - dispatchHeavyAnimationStop(); + endHeavyAnimation(); }, PLAYING_DURATION); }, [stop]); diff --git a/src/components/middle/MessageList.tsx b/src/components/middle/MessageList.tsx index 78b9e06af..e0334aae8 100644 --- a/src/components/middle/MessageList.tsx +++ b/src/components/middle/MessageList.tsx @@ -1,5 +1,6 @@ import type { FC } from '../../lib/teact/teact'; import React, { + beginHeavyAnimation, memo, useEffect, useMemo, @@ -66,7 +67,6 @@ import { preventMessageInputBlur } from './helpers/preventMessageInputBlur'; import useInterval from '../../hooks/schedulers/useInterval'; import useEffectWithPrevDeps from '../../hooks/useEffectWithPrevDeps'; -import { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck'; import useLastCallback from '../../hooks/useLastCallback'; import useLayoutEffectWithPrevDeps from '../../hooks/useLayoutEffectWithPrevDeps'; import useNativeCopySelectedMessages from '../../hooks/useNativeCopySelectedMessages'; @@ -629,7 +629,7 @@ const MessageList: FC = ({ useEffectWithPrevDeps(([prevIsSelectModeActive]) => { if (prevIsSelectModeActive !== undefined) { - dispatchHeavyAnimationEvent(SELECT_MODE_ANIMATION_DURATION + ANIMATION_END_DELAY); + beginHeavyAnimation(SELECT_MODE_ANIMATION_DURATION + ANIMATION_END_DELAY); } }, [isSelectModeActive]); diff --git a/src/components/middle/MessageListContent.tsx b/src/components/middle/MessageListContent.tsx index 2e51616ad..7c664620d 100644 --- a/src/components/middle/MessageListContent.tsx +++ b/src/components/middle/MessageListContent.tsx @@ -1,6 +1,6 @@ import type { RefObject } from 'react'; import type { FC } from '../../lib/teact/teact'; -import React, { memo } from '../../lib/teact/teact'; +import React, { getIsHeavyAnimating, memo } from '../../lib/teact/teact'; import { getActions } from '../../global'; import type { MessageListType } from '../../global/types'; @@ -25,7 +25,6 @@ import { isAlbum } from './helpers/groupMessages'; import { preventMessageInputBlur } from './helpers/preventMessageInputBlur'; import useDerivedSignal from '../../hooks/useDerivedSignal'; -import { getIsHeavyAnimating } from '../../hooks/useHeavyAnimationCheck'; import useOldLang from '../../hooks/useOldLang'; import usePreviousDeprecated from '../../hooks/usePreviousDeprecated'; import useMessageObservers from './hooks/useMessageObservers'; diff --git a/src/components/middle/composer/MessageInput.tsx b/src/components/middle/composer/MessageInput.tsx index 4c728315d..705b20a68 100644 --- a/src/components/middle/composer/MessageInput.tsx +++ b/src/components/middle/composer/MessageInput.tsx @@ -1,6 +1,7 @@ import type { ChangeEvent, RefObject } from 'react'; import type { FC } from '../../../lib/teact/teact'; import React, { + getIsHeavyAnimating, memo, useEffect, useLayoutEffect, useRef, useState, } from '../../../lib/teact/teact'; @@ -28,7 +29,6 @@ import { isSelectionInsideInput } from './helpers/selection'; import useAppLayout from '../../../hooks/useAppLayout'; import useDerivedState from '../../../hooks/useDerivedState'; import useFlag from '../../../hooks/useFlag'; -import { isHeavyAnimating } from '../../../hooks/useHeavyAnimationCheck'; import useLastCallback from '../../../hooks/useLastCallback'; import useOldLang from '../../../hooks/useOldLang'; import useInputCustomEmojis from './hooks/useInputCustomEmojis'; @@ -265,7 +265,7 @@ const MessageInput: FC = ({ return; } - if (isHeavyAnimating()) { + if (getIsHeavyAnimating()) { setTimeout(focusInput, FOCUS_DELAY_MS); return; } diff --git a/src/components/middle/message/Message.tsx b/src/components/middle/message/Message.tsx index 021b904a5..f2428fd32 100644 --- a/src/components/middle/message/Message.tsx +++ b/src/components/middle/message/Message.tsx @@ -1,11 +1,7 @@ import type { FC } from '../../../lib/teact/teact'; import React, { - memo, - useCallback, - useEffect, - useMemo, - useRef, - useState, + beginHeavyAnimation, + memo, useCallback, useEffect, useMemo, useRef, useState, } from '../../../lib/teact/teact'; import { getActions, withGlobal } from '../../../global'; @@ -126,7 +122,6 @@ import useContextMenuHandlers from '../../../hooks/useContextMenuHandlers'; import useEnsureMessage from '../../../hooks/useEnsureMessage'; import useEnsureStory from '../../../hooks/useEnsureStory'; import useFlag from '../../../hooks/useFlag'; -import { dispatchHeavyAnimationEvent } from '../../../hooks/useHeavyAnimationCheck'; import { useOnIntersect } from '../../../hooks/useIntersectionObserver'; import useLastCallback from '../../../hooks/useLastCallback'; import useOldLang from '../../../hooks/useOldLang'; @@ -832,7 +827,7 @@ const Message: FC = ({ const container = entry.target.closest('.MessageList'); if (!container) return; - dispatchHeavyAnimationEvent(RESIZE_ANIMATION_DURATION); + beginHeavyAnimation(RESIZE_ANIMATION_DURATION); const resizeDiff = newHeight - lastHeight; const { offsetHeight, scrollHeight, scrollTop } = container; diff --git a/src/components/middle/message/hooks/useVideoAutoPause.ts b/src/components/middle/message/hooks/useVideoAutoPause.ts index 6d1d94127..b94c14935 100644 --- a/src/components/middle/message/hooks/useVideoAutoPause.ts +++ b/src/components/middle/message/hooks/useVideoAutoPause.ts @@ -1,8 +1,8 @@ -import { useEffect, useRef } from '../../../../lib/teact/teact'; +import { getIsHeavyAnimating, useEffect, useRef } from '../../../../lib/teact/teact'; import { requestMeasure } from '../../../../lib/fasterdom/fasterdom'; -import useHeavyAnimationCheck, { isHeavyAnimating } from '../../../../hooks/useHeavyAnimationCheck'; +import useHeavyAnimation from '../../../../hooks/useHeavyAnimation'; import useLastCallback from '../../../../hooks/useLastCallback'; import usePriorityPlaybackCheck, { isPriorityPlaybackActive } from '../../../../hooks/usePriorityPlaybackCheck'; import useBackgroundMode, { isBackgroundModeActive } from '../../../../hooks/window/useBackgroundMode'; @@ -26,7 +26,7 @@ export default function useVideoAutoPause( }); useBackgroundMode(pause, unfreezePlayingOnRaf, !canPlay || isPriority); - useHeavyAnimationCheck(pause, unfreezePlaying, !canPlay || isPriority); + useHeavyAnimation(pause, unfreezePlaying, !canPlay || isPriority); usePriorityPlaybackCheck(pause, unfreezePlaying, !canPlay || isPriority); const handlePlaying = useLastCallback(() => { @@ -81,5 +81,5 @@ function usePlayPause(mediaRef: React.RefObject) { } function isFrozen() { - return isHeavyAnimating() || isPriorityPlaybackActive() || isBackgroundModeActive(); + return getIsHeavyAnimating() || isPriorityPlaybackActive() || isBackgroundModeActive(); } diff --git a/src/components/story/StoryToggler.tsx b/src/components/story/StoryToggler.tsx index d5f6b438a..628a28e78 100644 --- a/src/components/story/StoryToggler.tsx +++ b/src/components/story/StoryToggler.tsx @@ -1,4 +1,6 @@ -import React, { memo, useEffect, useMemo } from '../../lib/teact/teact'; +import React, { + beginHeavyAnimation, memo, useEffect, useMemo, +} from '../../lib/teact/teact'; import { getActions, withGlobal } from '../../global'; import type { ApiChat, ApiUser } from '../../api/types'; @@ -8,7 +10,6 @@ import { ANIMATION_END_DELAY, PREVIEW_AVATAR_COUNT } from '../../config'; import { selectIsForumPanelOpen, selectPerformanceSettingsValue, selectTabState } from '../../global/selectors'; import { animateClosing, animateOpening, ANIMATION_DURATION } from './helpers/ribbonAnimation'; -import { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck'; import useOldLang from '../../hooks/useOldLang'; import useShowTransition from '../../hooks/useShowTransition'; import useStoryPreloader from './hooks/useStoryPreloader'; @@ -98,10 +99,10 @@ function StoryToggler({ useEffect(() => { if (!withAnimation || isForumPanelOpen) return; if (isVisible) { - dispatchHeavyAnimationEvent(ANIMATION_DURATION + ANIMATION_END_DELAY); + beginHeavyAnimation(ANIMATION_DURATION + ANIMATION_END_DELAY); animateClosing(isArchived); } else { - dispatchHeavyAnimationEvent(ANIMATION_DURATION + ANIMATION_END_DELAY); + beginHeavyAnimation(ANIMATION_DURATION + ANIMATION_END_DELAY); animateOpening(isArchived); } }, [isArchived, isVisible, withAnimation, isForumPanelOpen]); diff --git a/src/components/story/StoryViewer.tsx b/src/components/story/StoryViewer.tsx index 15a5811f4..47c476c58 100644 --- a/src/components/story/StoryViewer.tsx +++ b/src/components/story/StoryViewer.tsx @@ -1,4 +1,5 @@ import React, { + beginHeavyAnimation, memo, useCallback, useEffect, useState, } from '../../lib/teact/teact'; import { getActions, withGlobal } from '../../global'; @@ -19,7 +20,6 @@ import { disableDirectTextInput, enableDirectTextInput } from '../../util/direct import { animateClosing, animateOpening } from './helpers/ghostAnimation'; import useFlag from '../../hooks/useFlag'; -import { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck'; import useOldLang from '../../hooks/useOldLang'; import usePreviousDeprecated from '../../hooks/usePreviousDeprecated'; import { dispatchPriorityPlaybackEvent } from '../../hooks/usePriorityPlaybackCheck'; @@ -117,11 +117,11 @@ function StoryViewer({ useEffect(() => { if (isGhostAnimation && !isPrevOpen && isOpen && peerId && thumbnail && origin !== undefined) { - dispatchHeavyAnimationEvent(ANIMATION_DURATION + ANIMATION_END_DELAY); + beginHeavyAnimation(ANIMATION_DURATION + ANIMATION_END_DELAY); animateOpening(peerId, origin, thumbnail, bestImageData, slideSizes.activeSlide); } if (isGhostAnimation && isPrevOpen && !isOpen && prevPeerId && prevBestImageData && prevOrigin !== undefined) { - dispatchHeavyAnimationEvent(ANIMATION_DURATION + ANIMATION_END_DELAY); + beginHeavyAnimation(ANIMATION_DURATION + ANIMATION_END_DELAY); animateClosing(prevPeerId, prevOrigin, prevBestImageData); } }, [ diff --git a/src/components/ui/Menu.tsx b/src/components/ui/Menu.tsx index 7b1e37d0b..7bf8e13ab 100644 --- a/src/components/ui/Menu.tsx +++ b/src/components/ui/Menu.tsx @@ -1,4 +1,5 @@ import React, { + beginHeavyAnimation, type FC, memo, useEffect, useRef, } from '../../lib/teact/teact'; @@ -11,7 +12,6 @@ import { preventMessageInputBlurWithBubbling } from '../middle/helpers/preventMe import useAppLayout from '../../hooks/useAppLayout'; import useEffectWithPrevDeps from '../../hooks/useEffectWithPrevDeps'; -import { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck'; import useHistoryBack from '../../hooks/useHistoryBack'; import useKeyboardListNavigation from '../../hooks/useKeyboardListNavigation'; import useLastCallback from '../../hooks/useLastCallback'; @@ -100,7 +100,7 @@ const Menu: FC = ({ useEffectWithPrevDeps(([prevIsOpen]) => { if (isOpen || (!isOpen && prevIsOpen === true)) { - dispatchHeavyAnimationEvent(ANIMATION_DURATION); + beginHeavyAnimation(ANIMATION_DURATION); } }, [isOpen]); diff --git a/src/components/ui/Modal.tsx b/src/components/ui/Modal.tsx index f919cedb5..aa9332576 100644 --- a/src/components/ui/Modal.tsx +++ b/src/components/ui/Modal.tsx @@ -1,5 +1,5 @@ import type { FC, TeactNode } from '../../lib/teact/teact'; -import React, { useEffect } from '../../lib/teact/teact'; +import React, { beginHeavyAnimation, useEffect } from '../../lib/teact/teact'; import type { TextPart } from '../../types'; @@ -9,7 +9,6 @@ import { disableDirectTextInput, enableDirectTextInput } from '../../util/direct import freezeWhenClosed from '../../util/hoc/freezeWhenClosed'; import trapFocus from '../../util/trapFocus'; -import { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck'; import useHistoryBack from '../../hooks/useHistoryBack'; import useLastCallback from '../../hooks/useLastCallback'; import useLayoutEffectWithPrevDeps from '../../hooks/useLayoutEffectWithPrevDeps'; @@ -110,7 +109,7 @@ const Modal: FC = ({ document.body.classList.toggle('has-open-dialog', Boolean(isOpen)); if (isOpen || (!isOpen && prevIsOpen !== undefined)) { - dispatchHeavyAnimationEvent(ANIMATION_DURATION); + beginHeavyAnimation(ANIMATION_DURATION); } return () => { diff --git a/src/components/ui/Transition.tsx b/src/components/ui/Transition.tsx index 89553a770..7fb3c47e7 100644 --- a/src/components/ui/Transition.tsx +++ b/src/components/ui/Transition.tsx @@ -1,5 +1,7 @@ import type { RefObject } from 'react'; -import React, { useEffect, useLayoutEffect, useRef } from '../../lib/teact/teact'; +import React, { + beginHeavyAnimation, useEffect, useLayoutEffect, useRef, +} from '../../lib/teact/teact'; import { addExtraClass, removeExtraClass, setExtraStyles, toggleExtraClass, } from '../../lib/teact/teact-dom'; @@ -14,7 +16,6 @@ import { omit } from '../../util/iteratees'; import { allowSwipeControlForTransition } from '../../util/swipeController'; import useForceUpdate from '../../hooks/useForceUpdate'; -import { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck'; import usePreviousDeprecated from '../../hooks/usePreviousDeprecated'; import './Transition.scss'; @@ -233,7 +234,7 @@ function Transition({ }); isAnimatingRef.current = true; - const dispatchHeavyAnimationStop = dispatchHeavyAnimationEvent(); + const endHeavyAnimation = beginHeavyAnimation(); onStart?.(); toggleExtraClass(container, `Transition-${name}`, !isBackwards); @@ -245,7 +246,7 @@ function Transition({ requestMutation(() => { if (activeKey !== currentKeyRef.current) { - dispatchHeavyAnimationStop(); + endHeavyAnimation(); return; } @@ -269,7 +270,7 @@ function Transition({ } onStop?.(); - dispatchHeavyAnimationStop(); + endHeavyAnimation(); isAnimatingRef.current = false; cleanup(); @@ -291,7 +292,7 @@ function Transition({ giveUpAnimationEnd(); isSwipeJustCancelledRef.current = true; onStop?.(); - dispatchHeavyAnimationStop(); + endHeavyAnimation(); isAnimatingRef.current = false; }, ); @@ -424,7 +425,7 @@ function performSlideOptimized( } isAnimatingRef.current = true; - const dispatchHeavyAnimationStop = dispatchHeavyAnimationEvent(); + const endHeavyAnimation = beginHeavyAnimation(); onStart?.(); toggleExtraClass(container, `Transition-${name}`, !isBackwards); @@ -473,7 +474,7 @@ function performSlideOptimized( requestMutation(() => { if (activeKey !== currentKeyRef.current) { - dispatchHeavyAnimationStop(); + endHeavyAnimation(); return; } @@ -490,7 +491,7 @@ function performSlideOptimized( } onStop?.(); - dispatchHeavyAnimationStop(); + endHeavyAnimation(); isAnimatingRef.current = false; cleanup(); diff --git a/src/global/cache.ts b/src/global/cache.ts index 4110a9665..30414556e 100644 --- a/src/global/cache.ts +++ b/src/global/cache.ts @@ -1,4 +1,5 @@ /* eslint-disable eslint-multitab-tt/no-immediate-global */ +import { getIsHeavyAnimating, onFullyIdle } from '../lib/teact/teact'; import { addCallback, removeCallback } from '../lib/teact/teactn'; import type { ApiAvailableReaction, ApiMessage } from '../api/types'; @@ -44,7 +45,6 @@ import { } from './selectors'; import { getIsMobile } from '../hooks/useAppLayout'; -import { isHeavyAnimating, onFullyIdle } from '../hooks/useHeavyAnimationCheck'; const UPDATE_THROTTLE = 5000; @@ -249,7 +249,7 @@ function unsafeMigrateCache(cached: GlobalState, initialState: GlobalState) { function updateCache(force?: boolean) { const global = getGlobal(); - if (isRemovingCache || !isCaching || global.isLoggingOut || (!force && isHeavyAnimating())) { + if (isRemovingCache || !isCaching || global.isLoggingOut || (!force && getIsHeavyAnimating())) { return; } diff --git a/src/hooks/useDebouncedMemo.ts b/src/hooks/useDebouncedMemo.ts index a340984e0..3cccf0613 100644 --- a/src/hooks/useDebouncedMemo.ts +++ b/src/hooks/useDebouncedMemo.ts @@ -1,7 +1,9 @@ -import { useCallback, useRef, useState } from '../lib/teact/teact'; +import { + getIsHeavyAnimating, useCallback, useRef, useState, +} from '../lib/teact/teact'; import useForceUpdate from './useForceUpdate'; -import useHeavyAnimationCheck, { isHeavyAnimating } from './useHeavyAnimationCheck'; +import useHeavyAnimation from './useHeavyAnimation'; import useRunDebounced from './useRunDebounced'; import useSyncEffect from './useSyncEffect'; @@ -43,10 +45,10 @@ function useHeavyAnimationFreeze() { isPending.current = false; forceUpdate(); }, [forceUpdate]); - useHeavyAnimationCheck(noop, handleUnfreeze); + useHeavyAnimation(noop, handleUnfreeze); return { - isFrozen: isHeavyAnimating(), + isFrozen: getIsHeavyAnimating(), updateWhenUnfrozen, }; } diff --git a/src/hooks/useFocusAfterAnimation.tsx b/src/hooks/useFocusAfterAnimation.ts similarity index 100% rename from src/hooks/useFocusAfterAnimation.tsx rename to src/hooks/useFocusAfterAnimation.ts diff --git a/src/hooks/useHeavyAnimationCheck.ts b/src/hooks/useHeavyAnimation.ts similarity index 52% rename from src/hooks/useHeavyAnimationCheck.ts rename to src/hooks/useHeavyAnimation.ts index 5adc63419..8945acd7b 100644 --- a/src/hooks/useHeavyAnimationCheck.ts +++ b/src/hooks/useHeavyAnimation.ts @@ -1,26 +1,23 @@ import { + getIsHeavyAnimating, useCallback, useEffect, useMemo, useRef, } from '../lib/teact/teact'; -import { requestMeasure } from '../lib/fasterdom/fasterdom'; import { createCallbackManager } from '../util/callbacks'; -import { onIdle } from '../util/schedulers'; -import { createSignal } from '../util/signals'; import useLastCallback from './useLastCallback'; -// Make sure to end even if end callback was not called (which was some hardly-reproducible bug) -const AUTO_END_TIMEOUT = 1000; +export const startCallbacks = createCallbackManager(); +export const endCallbacks = createCallbackManager(); -const startCallbacks = createCallbackManager(); -const endCallbacks = createCallbackManager(); +getIsHeavyAnimating.subscribe(() => { + if (getIsHeavyAnimating()) { + startCallbacks.runCallbacks(); + } else { + endCallbacks.runCallbacks(); + } +}); -let counter = 0; - -const [getIsAnimating, setIsAnimating] = createSignal(false); - -export const getIsHeavyAnimating = getIsAnimating; - -export default function useHeavyAnimationCheck( +export default function useHeavyAnimation( onStart?: AnyToVoidFunction, onEnd?: AnyToVoidFunction, isDisabled = false, @@ -33,7 +30,7 @@ export default function useHeavyAnimationCheck( return undefined; } - if (getIsAnimating()) { + if (getIsHeavyAnimating()) { lastOnStart(); } @@ -57,7 +54,7 @@ export function useThrottleForHeavyAnimation(afterH return useMemo(() => { return (...args: Parameters) => { if (!isScheduledRef.current) { - if (!getIsAnimating()) { + if (!getIsHeavyAnimating()) { fnMemo(...args); return; } @@ -73,48 +70,3 @@ export function useThrottleForHeavyAnimation(afterH }; }, [fnMemo]); } - -export function isHeavyAnimating() { - return getIsAnimating(); -} - -export function dispatchHeavyAnimationEvent(duration = AUTO_END_TIMEOUT) { - counter++; - - if (counter === 1) { - setIsAnimating(true); - startCallbacks.runCallbacks(); - } - - const timeout = window.setTimeout(onEnd, duration); - - let hasEnded = false; - - function onEnd() { - if (hasEnded) return; - hasEnded = true; - - clearTimeout(timeout); - - counter--; - - if (counter === 0) { - setIsAnimating(false); - endCallbacks.runCallbacks(); - } - } - - return onEnd; -} - -export function onFullyIdle(cb: NoneToVoidFunction) { - onIdle(() => { - if (getIsAnimating()) { - requestMeasure(() => { - onFullyIdle(cb); - }); - } else { - cb(); - } - }); -} diff --git a/src/hooks/useIntersectionObserver.ts b/src/hooks/useIntersectionObserver.ts index c7d0ea04c..88f960585 100644 --- a/src/hooks/useIntersectionObserver.ts +++ b/src/hooks/useIntersectionObserver.ts @@ -7,7 +7,7 @@ import { type CallbackManager, createCallbackManager } from '../util/callbacks'; import { debounce, throttle, throttleWith, } from '../util/schedulers'; -import useHeavyAnimationCheck from './useHeavyAnimationCheck'; +import useHeavyAnimation from './useHeavyAnimation'; import useLastCallback from './useLastCallback'; type TargetCallback = (entry: IntersectionObserverEntry) => void; @@ -71,7 +71,7 @@ export function useIntersectionObserver({ } }); - useHeavyAnimationCheck(freeze, unfreeze); + useHeavyAnimation(freeze, unfreeze); useEffect(() => { if (isDisabled) { diff --git a/src/hooks/useMountAfterHeavyAnimation.ts b/src/hooks/useMountAfterHeavyAnimation.ts index 25b2527bd..7b8dba180 100644 --- a/src/hooks/useMountAfterHeavyAnimation.ts +++ b/src/hooks/useMountAfterHeavyAnimation.ts @@ -1,7 +1,6 @@ -import { useEffect, useSignal } from '../lib/teact/teact'; +import { getIsHeavyAnimating, useEffect, useSignal } from '../lib/teact/teact'; import useDerivedState from './useDerivedState'; -import { getIsHeavyAnimating } from './useHeavyAnimationCheck'; export default function useMountAfterHeavyAnimation(hasIntersected: boolean) { const [getNoHeavyAnimation, setNoHeavyAnimation] = useSignal(false); diff --git a/src/hooks/useVideoCleanup.ts b/src/hooks/useVideoCleanup.ts index 14d608648..dce93de67 100644 --- a/src/hooks/useVideoCleanup.ts +++ b/src/hooks/useVideoCleanup.ts @@ -1,8 +1,7 @@ import type { RefObject } from 'react'; -import { useEffect } from '../lib/teact/teact'; +import { onFullyIdle, useEffect } from '../lib/teact/teact'; import unloadVideo from '../util/browser/unloadVideo'; -import { onFullyIdle } from './useHeavyAnimationCheck'; // Fix for memory leak when unmounting video element export default function useVideoCleanup(videoRef: RefObject, dependencies: any[]) { diff --git a/src/lib/teact/heavyAnimation.ts b/src/lib/teact/heavyAnimation.ts new file mode 100644 index 000000000..d95473f44 --- /dev/null +++ b/src/lib/teact/heavyAnimation.ts @@ -0,0 +1,50 @@ +import { onIdle } from '../../util/schedulers'; +import { createSignal } from '../../util/signals'; +import { requestMeasure } from '../fasterdom/fasterdom'; + +const AUTO_END_TIMEOUT = 1000; + +let counter = 0; + +const [getIsAnimating, setIsAnimating] = createSignal(false); + +export const getIsHeavyAnimating = getIsAnimating; + +export function beginHeavyAnimation(duration = AUTO_END_TIMEOUT) { + counter++; + + if (counter === 1) { + setIsAnimating(true); + } + + const timeout = window.setTimeout(onEnd, duration); + + let hasEnded = false; + + function onEnd() { + if (hasEnded) return; + hasEnded = true; + + clearTimeout(timeout); + + counter--; + + if (counter === 0) { + setIsAnimating(false); + } + } + + return onEnd; +} + +export function onFullyIdle(cb: NoneToVoidFunction) { + onIdle(() => { + if (getIsAnimating()) { + requestMeasure(() => { + onFullyIdle(cb); + }); + } else { + cb(); + } + }); +} diff --git a/src/lib/teact/teact.ts b/src/lib/teact/teact.ts index df9fa3f0f..864dce4b6 100644 --- a/src/lib/teact/teact.ts +++ b/src/lib/teact/teact.ts @@ -9,6 +9,8 @@ import { throttleWith } from '../../util/schedulers'; import { createSignal, isSignal, type Signal } from '../../util/signals'; import { requestMeasure, requestMutation } from '../fasterdom/fasterdom'; +export { getIsHeavyAnimating, beginHeavyAnimation, onFullyIdle } from './heavyAnimation'; + export type Props = AnyLiteral; export type FC

= (props: P) => any; // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/src/lib/teact/teactn.tsx b/src/lib/teact/teactn.tsx index 5b70417b1..c1eac1f9b 100644 --- a/src/lib/teact/teactn.tsx +++ b/src/lib/teact/teactn.tsx @@ -6,10 +6,9 @@ import { handleError } from '../../util/handleError'; import { orderBy } from '../../util/iteratees'; import { throttleWithTickEnd } from '../../util/schedulers'; import { requestMeasure } from '../fasterdom/fasterdom'; -import React, { DEBUG_resolveComponentName, useEffect } from './teact'; +import React, { DEBUG_resolveComponentName, getIsHeavyAnimating, useEffect } from './teact'; import useForceUpdate from '../../hooks/useForceUpdate'; -import { isHeavyAnimating } from '../../hooks/useHeavyAnimationCheck'; import useUniqueId from '../../hooks/useUniqueId'; export default React; @@ -75,7 +74,7 @@ let forceOnHeavyAnimation = true; function runCallbacks() { if (forceOnHeavyAnimation) { forceOnHeavyAnimation = false; - } else if (isHeavyAnimating()) { + } else if (getIsHeavyAnimating()) { requestMeasure(runCallbacksThrottled); return; } diff --git a/src/util/animateScroll.ts b/src/util/animateScroll.ts index c50e1d7d8..213ba4588 100644 --- a/src/util/animateScroll.ts +++ b/src/util/animateScroll.ts @@ -1,3 +1,4 @@ +import { beginHeavyAnimation } from '../lib/teact/teact'; import { getGlobal } from '../global'; import type { ScrollTargetPosition } from '../types'; @@ -14,13 +15,11 @@ import { selectCanAnimateInterface } from '../global/selectors'; import { animateSingle, cancelSingleAnimation } from './animation'; import { IS_ANDROID } from './windowEnvironment'; -import { dispatchHeavyAnimationEvent } from '../hooks/useHeavyAnimationCheck'; - type Params = Parameters; let isAnimating = false; let currentArgs: Parameters | undefined; -let onHeavyAnimationStop: NoneToVoidFunction | undefined; +let onHeavyAnimationEnd: NoneToVoidFunction | undefined; export default function animateScroll(...args: Params | [...Params, boolean]) { currentArgs = args.slice(0, 8) as Params; @@ -127,9 +126,9 @@ function createMutateFunction( isAnimating = true; - const prevOnHeavyAnimationStop = onHeavyAnimationStop; - onHeavyAnimationStop = dispatchHeavyAnimationEvent(); - prevOnHeavyAnimationStop?.(); + const prevOnHeavyAnimationEnd = onHeavyAnimationEnd; + onHeavyAnimationEnd = beginHeavyAnimation(); + prevOnHeavyAnimationEnd?.(); animateSingle(() => { const t = Math.min((Date.now() - startAt) / duration, 1); @@ -143,8 +142,8 @@ function createMutateFunction( if (!isAnimating) { currentArgs = undefined; - onHeavyAnimationStop!(); - onHeavyAnimationStop = undefined; + onHeavyAnimationEnd!(); + onHeavyAnimationEnd = undefined; } return isAnimating; diff --git a/src/util/folderManager.ts b/src/util/folderManager.ts index ff5120435..5c3f49ede 100644 --- a/src/util/folderManager.ts +++ b/src/util/folderManager.ts @@ -1,3 +1,4 @@ +import { onFullyIdle } from '../lib/teact/teact'; import { addCallback } from '../lib/teact/teactn'; import { addActionHandler, getGlobal } from '../global'; @@ -23,8 +24,6 @@ import { createCallbackManager } from './callbacks'; import { areSortedArraysEqual, unique } from './iteratees'; import { throttle } from './schedulers'; -import { onFullyIdle } from '../hooks/useHeavyAnimationCheck'; - interface FolderSummary { id: number; listIds?: Set; diff --git a/src/util/multitab.ts b/src/util/multitab.ts index 1c88b0ff2..11a9328de 100644 --- a/src/util/multitab.ts +++ b/src/util/multitab.ts @@ -1,4 +1,5 @@ /* eslint-disable eslint-multitab-tt/set-global-only-variable */ +import { onFullyIdle } from '../lib/teact/teact'; import { addCallback } from '../lib/teact/teactn'; import { getActions, getGlobal, setGlobal } from '../global'; @@ -24,8 +25,6 @@ import { getCurrentTabId, signalPasscodeHash, subscribeToTokenDied } from './est import { omit } from './iteratees'; import { IS_MULTITAB_SUPPORTED } from './windowEnvironment'; -import { onFullyIdle } from '../hooks/useHeavyAnimationCheck'; - type BroadcastChannelRefreshLangpack = { type: 'langpackRefresh'; langCode: string;