[Refactoring] Revise hook dependencies (#2424)
This commit is contained in:
parent
cf4c199eba
commit
9c25abbd9a
@ -40,7 +40,12 @@
|
|||||||
"no-console": "error",
|
"no-console": "error",
|
||||||
"semi": "error",
|
"semi": "error",
|
||||||
"no-implicit-coercion": "error",
|
"no-implicit-coercion": "error",
|
||||||
"react-hooks/exhaustive-deps": "error",
|
"react-hooks/exhaustive-deps": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"additionalHooks": "(useSyncEffect|useAsync|useDebouncedCallback|useThrottledCallback|useEffectWithPrevDeps|useLayoutEffectWithPrevDeps)$"
|
||||||
|
}
|
||||||
|
],
|
||||||
"arrow-body-style": "off",
|
"arrow-body-style": "off",
|
||||||
"no-else-return": "off",
|
"no-else-return": "off",
|
||||||
"no-plusplus": "off",
|
"no-plusplus": "off",
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import buildClassName from '../../util/buildClassName';
|
|||||||
import renderText from '../common/helpers/renderText';
|
import renderText from '../common/helpers/renderText';
|
||||||
import useLang from '../../hooks/useLang';
|
import useLang from '../../hooks/useLang';
|
||||||
import { isoToEmoji } from '../../util/emoji';
|
import { isoToEmoji } from '../../util/emoji';
|
||||||
import useOnChange from '../../hooks/useOnChange';
|
import useSyncEffect from '../../hooks/useSyncEffect';
|
||||||
|
|
||||||
import DropdownMenu from '../ui/DropdownMenu';
|
import DropdownMenu from '../ui/DropdownMenu';
|
||||||
import MenuItem from '../ui/MenuItem';
|
import MenuItem from '../ui/MenuItem';
|
||||||
@ -53,11 +53,11 @@ const CountryCodeInput: FC<OwnProps & StateProps> = ({
|
|||||||
setFilteredList(getFilteredList(phoneCodeList, filterValue));
|
setFilteredList(getFilteredList(phoneCodeList, filterValue));
|
||||||
}, [phoneCodeList]);
|
}, [phoneCodeList]);
|
||||||
|
|
||||||
useOnChange(([prevPhoneCodeList]) => {
|
useSyncEffect(([prevPhoneCodeList]) => {
|
||||||
if (prevPhoneCodeList?.length === 0 && phoneCodeList.length > 0) {
|
if (!prevPhoneCodeList?.length && phoneCodeList.length) {
|
||||||
updateFilter(filter);
|
setFilteredList(getFilteredList(phoneCodeList, filter));
|
||||||
}
|
}
|
||||||
}, [phoneCodeList, updateFilter]);
|
}, [phoneCodeList, filter]);
|
||||||
|
|
||||||
const handleChange = useCallback((country: ApiCountryCode) => {
|
const handleChange = useCallback((country: ApiCountryCode) => {
|
||||||
onChange(country);
|
onChange(country);
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import generateIdFor from '../../util/generateIdFor';
|
|||||||
|
|
||||||
import useHeavyAnimationCheck from '../../hooks/useHeavyAnimationCheck';
|
import useHeavyAnimationCheck from '../../hooks/useHeavyAnimationCheck';
|
||||||
import useBackgroundMode from '../../hooks/useBackgroundMode';
|
import useBackgroundMode from '../../hooks/useBackgroundMode';
|
||||||
import useOnChange from '../../hooks/useOnChange';
|
import useSyncEffect from '../../hooks/useSyncEffect';
|
||||||
import useAppLayout from '../../hooks/useAppLayout';
|
import useAppLayout from '../../hooks/useAppLayout';
|
||||||
|
|
||||||
export type OwnProps = {
|
export type OwnProps = {
|
||||||
@ -229,13 +229,13 @@ const AnimatedSticker: FC<OwnProps> = ({
|
|||||||
fastRaf(unfreezeAnimation);
|
fastRaf(unfreezeAnimation);
|
||||||
}, [unfreezeAnimation]);
|
}, [unfreezeAnimation]);
|
||||||
|
|
||||||
useOnChange(([prevNoLoop]) => {
|
useSyncEffect(([prevNoLoop]) => {
|
||||||
if (prevNoLoop !== undefined && noLoop !== prevNoLoop) {
|
if (prevNoLoop !== undefined && noLoop !== prevNoLoop) {
|
||||||
animation?.setNoLoop(noLoop);
|
animation?.setNoLoop(noLoop);
|
||||||
}
|
}
|
||||||
}, [noLoop, animation]);
|
}, [noLoop, animation]);
|
||||||
|
|
||||||
useOnChange(([prevSharedCanvasCoords, prevIsMobile]) => {
|
useSyncEffect(([prevSharedCanvasCoords, prevIsMobile]) => {
|
||||||
if (
|
if (
|
||||||
(prevSharedCanvasCoords !== undefined && sharedCanvasCoords !== prevSharedCanvasCoords)
|
(prevSharedCanvasCoords !== undefined && sharedCanvasCoords !== prevSharedCanvasCoords)
|
||||||
|| (prevIsMobile !== undefined && isMobile !== prevIsMobile)
|
|| (prevIsMobile !== undefined && isMobile !== prevIsMobile)
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect } from '../../lib/teact/teact';
|
import React from '../../lib/teact/teact';
|
||||||
import { getActions, getGlobal, withGlobal } from '../../global';
|
import { getActions, getGlobal, withGlobal } from '../../global';
|
||||||
|
|
||||||
import { ApiMediaFormat } from '../../api/types';
|
import { ApiMediaFormat } from '../../api/types';
|
||||||
@ -13,8 +13,6 @@ import {
|
|||||||
selectTabState,
|
selectTabState,
|
||||||
} from '../../global/selectors';
|
} from '../../global/selectors';
|
||||||
import { DARK_THEME_BG_COLOR, LIGHT_THEME_BG_COLOR } from '../../config';
|
import { DARK_THEME_BG_COLOR, LIGHT_THEME_BG_COLOR } from '../../config';
|
||||||
import useFlag from '../../hooks/useFlag';
|
|
||||||
import useShowTransition from '../../hooks/useShowTransition';
|
|
||||||
import { pause } from '../../util/schedulers';
|
import { pause } from '../../util/schedulers';
|
||||||
import { preloadImage } from '../../util/files';
|
import { preloadImage } from '../../util/files';
|
||||||
import preloadFonts from '../../util/fonts';
|
import preloadFonts from '../../util/fonts';
|
||||||
@ -22,6 +20,10 @@ import * as mediaLoader from '../../util/mediaLoader';
|
|||||||
import { Bundles, loadModule } from '../../util/moduleLoader';
|
import { Bundles, loadModule } from '../../util/moduleLoader';
|
||||||
import buildClassName from '../../util/buildClassName';
|
import buildClassName from '../../util/buildClassName';
|
||||||
|
|
||||||
|
import useFlag from '../../hooks/useFlag';
|
||||||
|
import useShowTransition from '../../hooks/useShowTransition';
|
||||||
|
import useEffectOnce from '../../hooks/useEffectOnce';
|
||||||
|
|
||||||
import styles from './UiLoader.module.scss';
|
import styles from './UiLoader.module.scss';
|
||||||
|
|
||||||
import telegramLogoPath from '../../assets/telegram-logo.svg';
|
import telegramLogoPath from '../../assets/telegram-logo.svg';
|
||||||
@ -114,7 +116,7 @@ const UiLoader: FC<OwnProps & StateProps> = ({
|
|||||||
shouldRender: shouldRenderMask, transitionClassNames,
|
shouldRender: shouldRenderMask, transitionClassNames,
|
||||||
} = useShowTransition(!isReady, undefined, true);
|
} = useShowTransition(!isReady, undefined, true);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffectOnce(() => {
|
||||||
let timeout: number | undefined;
|
let timeout: number | undefined;
|
||||||
|
|
||||||
const safePreload = async () => {
|
const safePreload = async () => {
|
||||||
@ -145,8 +147,7 @@ const UiLoader: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
setIsUiReady({ uiReadyState: 0 });
|
setIsUiReady({ uiReadyState: 0 });
|
||||||
};
|
};
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
});
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import { selectTabState, selectCurrentChat, selectIsForumPanelOpen } from '../..
|
|||||||
import useFoldersReducer from '../../hooks/reducers/useFoldersReducer';
|
import useFoldersReducer from '../../hooks/reducers/useFoldersReducer';
|
||||||
import { useResize } from '../../hooks/useResize';
|
import { useResize } from '../../hooks/useResize';
|
||||||
import { useHotkeys } from '../../hooks/useHotkeys';
|
import { useHotkeys } from '../../hooks/useHotkeys';
|
||||||
import useOnChange from '../../hooks/useOnChange';
|
import useSyncEffect from '../../hooks/useSyncEffect';
|
||||||
|
|
||||||
import Transition from '../ui/Transition';
|
import Transition from '../ui/Transition';
|
||||||
import LeftMain from './main/LeftMain';
|
import LeftMain from './main/LeftMain';
|
||||||
@ -372,7 +372,7 @@ const LeftColumn: FC<StateProps> = ({
|
|||||||
}
|
}
|
||||||
}, [clearTwoFaError, loadPasswordInfo, settingsScreen]);
|
}, [clearTwoFaError, loadPasswordInfo, settingsScreen]);
|
||||||
|
|
||||||
useOnChange(() => {
|
useSyncEffect(() => {
|
||||||
if (nextSettingsScreen !== undefined) {
|
if (nextSettingsScreen !== undefined) {
|
||||||
setContent(LeftColumnContent.Settings);
|
setContent(LeftColumnContent.Settings);
|
||||||
setSettingsScreen(nextSettingsScreen);
|
setSettingsScreen(nextSettingsScreen);
|
||||||
|
|||||||
@ -117,7 +117,7 @@ const ChatList: FC<OwnProps> = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
openChat({ id: chatId, shouldReplaceHistory: true });
|
openChat({ id: chatId, shouldReplaceHistory: true });
|
||||||
}, [], DRAG_ENTER_DEBOUNCE, true);
|
}, [openChat], DRAG_ENTER_DEBOUNCE, true);
|
||||||
|
|
||||||
const handleDragLeave = useCallback((e: React.DragEvent<HTMLDivElement>) => {
|
const handleDragLeave = useCallback((e: React.DragEvent<HTMLDivElement>) => {
|
||||||
const rect = e.currentTarget.getBoundingClientRect();
|
const rect = e.currentTarget.getBoundingClientRect();
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import React, { memo, useRef } from '../../lib/teact/teact';
|
import React, { memo, useCallback, useRef } from '../../lib/teact/teact';
|
||||||
import { withGlobal } from '../../global';
|
import { withGlobal } from '../../global';
|
||||||
|
|
||||||
import type { TabState } from '../../global/types';
|
import type { TabState } from '../../global/types';
|
||||||
@ -9,7 +9,7 @@ import buildStyle from '../../util/buildStyle';
|
|||||||
import { selectTabState } from '../../global/selectors';
|
import { selectTabState } from '../../global/selectors';
|
||||||
|
|
||||||
import useWindowSize from '../../hooks/useWindowSize';
|
import useWindowSize from '../../hooks/useWindowSize';
|
||||||
import useOnChange from '../../hooks/useOnChange';
|
import useSyncEffect from '../../hooks/useSyncEffect';
|
||||||
import useForceUpdate from '../../hooks/useForceUpdate';
|
import useForceUpdate from '../../hooks/useForceUpdate';
|
||||||
import useAppLayout from '../../hooks/useAppLayout';
|
import useAppLayout from '../../hooks/useAppLayout';
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ const ConfettiContainer: FC<StateProps> = ({ confetti }) => {
|
|||||||
lastConfettiTime, top, width, left, height,
|
lastConfettiTime, top, width, left, height,
|
||||||
} = confetti || {};
|
} = confetti || {};
|
||||||
|
|
||||||
function generateConfetti(w: number, h: number, amount = defaultConfettiAmount) {
|
const generateConfetti = useCallback((w: number, h: number, amount = defaultConfettiAmount) => {
|
||||||
for (let i = 0; i < amount; i++) {
|
for (let i = 0; i < amount; i++) {
|
||||||
const leftSide = i % 2;
|
const leftSide = i % 2;
|
||||||
const pos = {
|
const pos = {
|
||||||
@ -83,9 +83,9 @@ const ConfettiContainer: FC<StateProps> = ({ confetti }) => {
|
|||||||
frameCount: 0,
|
frameCount: 0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}, [defaultConfettiAmount]);
|
||||||
|
|
||||||
const updateCanvas = () => {
|
const updateCanvas = useCallback(() => {
|
||||||
if (!canvasRef.current || !isRafStartedRef.current) {
|
if (!canvasRef.current || !isRafStartedRef.current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -166,9 +166,9 @@ const ConfettiContainer: FC<StateProps> = ({ confetti }) => {
|
|||||||
} else {
|
} else {
|
||||||
isRafStartedRef.current = false;
|
isRafStartedRef.current = false;
|
||||||
}
|
}
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
useOnChange(([prevConfettiTime]) => {
|
useSyncEffect(([prevConfettiTime]) => {
|
||||||
let hideTimeout: ReturnType<typeof setTimeout>;
|
let hideTimeout: ReturnType<typeof setTimeout>;
|
||||||
if (prevConfettiTime !== lastConfettiTime) {
|
if (prevConfettiTime !== lastConfettiTime) {
|
||||||
generateConfetti(width || windowSize.width, height || windowSize.height);
|
generateConfetti(width || windowSize.width, height || windowSize.height);
|
||||||
@ -179,11 +179,10 @@ const ConfettiContainer: FC<StateProps> = ({ confetti }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return () => {
|
return () => {
|
||||||
if (hideTimeout) {
|
clearTimeout(hideTimeout);
|
||||||
clearTimeout(hideTimeout);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}, [lastConfettiTime, updateCanvas]);
|
// eslint-disable-next-line react-hooks/exhaustive-deps -- Old timeout should be cleared only if new confetti is generated
|
||||||
|
}, [lastConfettiTime, forceUpdate, updateCanvas]);
|
||||||
|
|
||||||
if (!lastConfettiTime || Date.now() - lastConfettiTime > CONFETTI_FADEOUT_TIMEOUT) {
|
if (!lastConfettiTime || Date.now() - lastConfettiTime > CONFETTI_FADEOUT_TIMEOUT) {
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|||||||
@ -35,7 +35,7 @@ import { fastRaf } from '../../util/schedulers';
|
|||||||
import useEffectWithPrevDeps from '../../hooks/useEffectWithPrevDeps';
|
import useEffectWithPrevDeps from '../../hooks/useEffectWithPrevDeps';
|
||||||
import useBackgroundMode from '../../hooks/useBackgroundMode';
|
import useBackgroundMode from '../../hooks/useBackgroundMode';
|
||||||
import useBeforeUnload from '../../hooks/useBeforeUnload';
|
import useBeforeUnload from '../../hooks/useBeforeUnload';
|
||||||
import useOnChange from '../../hooks/useOnChange';
|
import useSyncEffect from '../../hooks/useSyncEffect';
|
||||||
import usePreventPinchZoomGesture from '../../hooks/usePreventPinchZoomGesture';
|
import usePreventPinchZoomGesture from '../../hooks/usePreventPinchZoomGesture';
|
||||||
import useForceUpdate from '../../hooks/useForceUpdate';
|
import useForceUpdate from '../../hooks/useForceUpdate';
|
||||||
import useShowTransition from '../../hooks/useShowTransition';
|
import useShowTransition from '../../hooks/useShowTransition';
|
||||||
@ -272,7 +272,7 @@ const Main: FC<OwnProps & StateProps> = ({
|
|||||||
ignoreCache: true,
|
ignoreCache: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [lastSyncTime, isMasterTab] as const);
|
}, [lastSyncTime, isMasterTab, loadCustomEmojis]);
|
||||||
|
|
||||||
// Sticker sets
|
// Sticker sets
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -324,7 +324,7 @@ const Main: FC<OwnProps & StateProps> = ({
|
|||||||
type: parsedLocationHash.type,
|
type: parsedLocationHash.type,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [lastSyncTime] as const);
|
}, [lastSyncTime, openChat]);
|
||||||
|
|
||||||
const leftColumnTransition = useShowTransition(
|
const leftColumnTransition = useShowTransition(
|
||||||
isLeftColumnOpen, undefined, true, undefined, shouldSkipHistoryAnimations,
|
isLeftColumnOpen, undefined, true, undefined, shouldSkipHistoryAnimations,
|
||||||
@ -333,8 +333,8 @@ const Main: FC<OwnProps & StateProps> = ({
|
|||||||
const forceUpdate = useForceUpdate();
|
const forceUpdate = useForceUpdate();
|
||||||
|
|
||||||
// Handle opening middle column
|
// Handle opening middle column
|
||||||
useOnChange(([prevIsLeftColumnOpen]) => {
|
useSyncEffect(([prevIsLeftColumnOpen]) => {
|
||||||
if (prevIsLeftColumnOpen === undefined || animationLevel === 0) {
|
if (prevIsLeftColumnOpen === undefined || isLeftColumnOpen === prevIsLeftColumnOpen || animationLevel === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -353,7 +353,7 @@ const Main: FC<OwnProps & StateProps> = ({
|
|||||||
willAnimateLeftColumnRef.current = false;
|
willAnimateLeftColumnRef.current = false;
|
||||||
forceUpdate();
|
forceUpdate();
|
||||||
});
|
});
|
||||||
}, [isLeftColumnOpen]);
|
}, [animationLevel, forceUpdate, isLeftColumnOpen]);
|
||||||
|
|
||||||
const rightColumnTransition = useShowTransition(
|
const rightColumnTransition = useShowTransition(
|
||||||
isRightColumnOpen, undefined, true, undefined, shouldSkipHistoryAnimations,
|
isRightColumnOpen, undefined, true, undefined, shouldSkipHistoryAnimations,
|
||||||
@ -362,8 +362,8 @@ const Main: FC<OwnProps & StateProps> = ({
|
|||||||
const [isNarrowMessageList, setIsNarrowMessageList] = useState(isRightColumnOpen);
|
const [isNarrowMessageList, setIsNarrowMessageList] = useState(isRightColumnOpen);
|
||||||
|
|
||||||
// Handle opening right column
|
// Handle opening right column
|
||||||
useOnChange(([prevIsRightColumnOpen]) => {
|
useSyncEffect(([prevIsRightColumnOpen]) => {
|
||||||
if (prevIsRightColumnOpen === undefined) {
|
if (prevIsRightColumnOpen === undefined || isRightColumnOpen === prevIsRightColumnOpen) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -382,7 +382,7 @@ const Main: FC<OwnProps & StateProps> = ({
|
|||||||
forceUpdate();
|
forceUpdate();
|
||||||
setIsNarrowMessageList(isRightColumnOpen);
|
setIsNarrowMessageList(isRightColumnOpen);
|
||||||
});
|
});
|
||||||
}, [isRightColumnOpen]);
|
}, [animationLevel, forceUpdate, isRightColumnOpen]);
|
||||||
|
|
||||||
const className = buildClassName(
|
const className = buildClassName(
|
||||||
leftColumnTransition.hasShownClass && 'left-column-shown',
|
leftColumnTransition.hasShownClass && 'left-column-shown',
|
||||||
|
|||||||
@ -18,7 +18,7 @@ import { extractCurrentThemeParams, validateHexColor } from '../../util/themeSty
|
|||||||
|
|
||||||
import useInterval from '../../hooks/useInterval';
|
import useInterval from '../../hooks/useInterval';
|
||||||
import useLang from '../../hooks/useLang';
|
import useLang from '../../hooks/useLang';
|
||||||
import useOnChange from '../../hooks/useOnChange';
|
import useSyncEffect from '../../hooks/useSyncEffect';
|
||||||
import useWebAppFrame from './hooks/useWebAppFrame';
|
import useWebAppFrame from './hooks/useWebAppFrame';
|
||||||
import usePrevious from '../../hooks/usePrevious';
|
import usePrevious from '../../hooks/usePrevious';
|
||||||
import useFlag from '../../hooks/useFlag';
|
import useFlag from '../../hooks/useFlag';
|
||||||
@ -258,20 +258,20 @@ const WebAppModal: FC<OwnProps & StateProps> = ({
|
|||||||
}, [handlePopupClose]);
|
}, [handlePopupClose]);
|
||||||
|
|
||||||
// Notify view that height changed
|
// Notify view that height changed
|
||||||
useOnChange(() => {
|
useSyncEffect(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
sendViewport();
|
sendViewport();
|
||||||
}, ANIMATION_WAIT);
|
}, ANIMATION_WAIT);
|
||||||
}, [mainButton?.isVisible, sendViewport]);
|
}, [mainButton?.isVisible, sendViewport]);
|
||||||
|
|
||||||
// Notify view that theme changed
|
// Notify view that theme changed
|
||||||
useOnChange(() => {
|
useSyncEffect(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
sendTheme();
|
sendTheme();
|
||||||
}, ANIMATION_WAIT);
|
}, ANIMATION_WAIT);
|
||||||
}, [theme, sendTheme]);
|
}, [theme, sendTheme]);
|
||||||
|
|
||||||
useOnChange(([prevIsPaymentModalOpen]) => {
|
useSyncEffect(([prevIsPaymentModalOpen]) => {
|
||||||
if (isPaymentModalOpen === prevIsPaymentModalOpen) return;
|
if (isPaymentModalOpen === prevIsPaymentModalOpen) return;
|
||||||
if (webApp?.slug && !isPaymentModalOpen && paymentStatus) {
|
if (webApp?.slug && !isPaymentModalOpen && paymentStatus) {
|
||||||
sendEvent({
|
sendEvent({
|
||||||
@ -285,7 +285,7 @@ const WebAppModal: FC<OwnProps & StateProps> = ({
|
|||||||
slug: undefined,
|
slug: undefined,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [isPaymentModalOpen, paymentStatus, sendEvent, setWebAppPaymentSlug, webApp] as const);
|
}, [isPaymentModalOpen, paymentStatus, sendEvent, setWebAppPaymentSlug, webApp]);
|
||||||
|
|
||||||
const handleToggleClick = useCallback(() => {
|
const handleToggleClick = useCallback(() => {
|
||||||
toggleAttachBot({
|
toggleAttachBot({
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import renderText from '../../common/helpers/renderText';
|
|||||||
import { getUserFullName } from '../../../global/helpers';
|
import { getUserFullName } from '../../../global/helpers';
|
||||||
|
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
import useOnChange from '../../../hooks/useOnChange';
|
import useSyncEffect from '../../../hooks/useSyncEffect';
|
||||||
|
|
||||||
import Modal from '../../ui/Modal';
|
import Modal from '../../ui/Modal';
|
||||||
import Button from '../../ui/Button';
|
import Button from '../../ui/Button';
|
||||||
@ -170,11 +170,11 @@ const PremiumMainModal: FC<OwnProps & StateProps> = ({
|
|||||||
}
|
}
|
||||||
}, [isSuccess, showConfetti]);
|
}, [isSuccess, showConfetti]);
|
||||||
|
|
||||||
useOnChange(([prevIsPremium]) => {
|
useSyncEffect(([prevIsPremium]) => {
|
||||||
if (prevIsPremium === isPremium) return;
|
if (prevIsPremium === isPremium) return;
|
||||||
|
|
||||||
showConfetti();
|
showConfetti();
|
||||||
}, [isPremium]);
|
}, [isPremium, showConfetti]);
|
||||||
|
|
||||||
if (!promo) return undefined;
|
if (!promo) return undefined;
|
||||||
|
|
||||||
|
|||||||
@ -120,7 +120,7 @@ const MediaViewerSlides: FC<OwnProps> = ({
|
|||||||
forceUpdate();
|
forceUpdate();
|
||||||
}, [forceUpdate]);
|
}, [forceUpdate]);
|
||||||
|
|
||||||
const selectMediaDebounced = useDebouncedCallback(selectMedia, [], DEBOUNCE_MESSAGE, true);
|
const selectMediaDebounced = useDebouncedCallback(selectMedia, [selectMedia], DEBOUNCE_MESSAGE, true);
|
||||||
const clearSwipeDirectionDebounced = useDebouncedCallback(() => {
|
const clearSwipeDirectionDebounced = useDebouncedCallback(() => {
|
||||||
swipeDirectionRef.current = undefined;
|
swipeDirectionRef.current = undefined;
|
||||||
}, [], DEBOUNCE_SWIPE, true);
|
}, [], DEBOUNCE_SWIPE, true);
|
||||||
|
|||||||
@ -50,7 +50,7 @@ import resetScroll, { patchChromiumScroll } from '../../util/resetScroll';
|
|||||||
import fastSmoothScroll, { isAnimatingScroll } from '../../util/fastSmoothScroll';
|
import fastSmoothScroll, { isAnimatingScroll } from '../../util/fastSmoothScroll';
|
||||||
import renderText from '../common/helpers/renderText';
|
import renderText from '../common/helpers/renderText';
|
||||||
|
|
||||||
import useOnChange from '../../hooks/useOnChange';
|
import useSyncEffect from '../../hooks/useSyncEffect';
|
||||||
import useStickyDates from './hooks/useStickyDates';
|
import useStickyDates from './hooks/useStickyDates';
|
||||||
import { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck';
|
import { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck';
|
||||||
import useLang from '../../hooks/useLang';
|
import useLang from '../../hooks/useLang';
|
||||||
@ -196,7 +196,7 @@ const MessageList: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
const areMessagesLoaded = Boolean(messageIds);
|
const areMessagesLoaded = Boolean(messageIds);
|
||||||
|
|
||||||
useOnChange(() => {
|
useSyncEffect(() => {
|
||||||
// We only need it first time when message list appears
|
// We only need it first time when message list appears
|
||||||
if (areMessagesLoaded) {
|
if (areMessagesLoaded) {
|
||||||
onTickEnd(() => {
|
onTickEnd(() => {
|
||||||
@ -206,24 +206,24 @@ const MessageList: FC<OwnProps & StateProps> = ({
|
|||||||
}, [areMessagesLoaded]);
|
}, [areMessagesLoaded]);
|
||||||
|
|
||||||
// Updated every time (to be used from intersection callback closure)
|
// Updated every time (to be used from intersection callback closure)
|
||||||
useOnChange(() => {
|
useSyncEffect(() => {
|
||||||
memoFirstUnreadIdRef.current = firstUnreadId;
|
memoFirstUnreadIdRef.current = firstUnreadId;
|
||||||
}, [firstUnreadId]);
|
}, [firstUnreadId]);
|
||||||
|
|
||||||
useOnChange(() => {
|
useEffect(() => {
|
||||||
if (!isCurrentUserPremium && isChannelChat && isReady && lastSyncTime) {
|
if (!isCurrentUserPremium && isChannelChat && isReady && lastSyncTime) {
|
||||||
loadSponsoredMessages({ chatId });
|
loadSponsoredMessages({ chatId });
|
||||||
}
|
}
|
||||||
}, [isCurrentUserPremium, chatId, isReady, isChannelChat, lastSyncTime]);
|
}, [isCurrentUserPremium, chatId, isReady, isChannelChat, lastSyncTime, loadSponsoredMessages]);
|
||||||
|
|
||||||
// Updated only once when messages are loaded (as we want the unread divider to keep its position)
|
// Updated only once when messages are loaded (as we want the unread divider to keep its position)
|
||||||
useOnChange(() => {
|
useSyncEffect(() => {
|
||||||
if (areMessagesLoaded) {
|
if (areMessagesLoaded) {
|
||||||
memoUnreadDividerBeforeIdRef.current = memoFirstUnreadIdRef.current;
|
memoUnreadDividerBeforeIdRef.current = memoFirstUnreadIdRef.current;
|
||||||
}
|
}
|
||||||
}, [areMessagesLoaded]);
|
}, [areMessagesLoaded]);
|
||||||
|
|
||||||
useOnChange(() => {
|
useSyncEffect(() => {
|
||||||
memoFocusingIdRef.current = focusingId;
|
memoFocusingIdRef.current = focusingId;
|
||||||
}, [focusingId]);
|
}, [focusingId]);
|
||||||
|
|
||||||
@ -344,7 +344,7 @@ const MessageList: FC<OwnProps & StateProps> = ({
|
|||||||
}, [isChatLoaded, messageIds, loadMoreAround, focusingId, isRestricted]);
|
}, [isChatLoaded, messageIds, loadMoreAround, focusingId, isRestricted]);
|
||||||
|
|
||||||
// Remember scroll position before repositioning it
|
// Remember scroll position before repositioning it
|
||||||
useOnChange(() => {
|
useSyncEffect(() => {
|
||||||
if (!messageIds || !listItemElementsRef.current) {
|
if (!messageIds || !listItemElementsRef.current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -488,7 +488,8 @@ const MessageList: FC<OwnProps & StateProps> = ({
|
|||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.timeEnd('scrollTop');
|
console.timeEnd('scrollTop');
|
||||||
}
|
}
|
||||||
// This should match deps for `useOnChange` above
|
// This should match deps for `useSyncEffect` above
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps -- `as const` not yet supported by linter
|
||||||
}, [messageIds, isViewportNewest, containerHeight, hasTools] as const);
|
}, [messageIds, isViewportNewest, containerHeight, hasTools] as const);
|
||||||
|
|
||||||
useEffectWithPrevDeps(([prevIsSelectModeActive]) => {
|
useEffectWithPrevDeps(([prevIsSelectModeActive]) => {
|
||||||
|
|||||||
@ -61,7 +61,7 @@ import useLang from '../../hooks/useLang';
|
|||||||
import useHistoryBack from '../../hooks/useHistoryBack';
|
import useHistoryBack from '../../hooks/useHistoryBack';
|
||||||
import usePrevious from '../../hooks/usePrevious';
|
import usePrevious from '../../hooks/usePrevious';
|
||||||
import useForceUpdate from '../../hooks/useForceUpdate';
|
import useForceUpdate from '../../hooks/useForceUpdate';
|
||||||
import useOnChange from '../../hooks/useOnChange';
|
import useSyncEffect from '../../hooks/useSyncEffect';
|
||||||
import useAppLayout from '../../hooks/useAppLayout';
|
import useAppLayout from '../../hooks/useAppLayout';
|
||||||
|
|
||||||
import Transition from '../ui/Transition';
|
import Transition from '../ui/Transition';
|
||||||
@ -243,7 +243,7 @@ const MiddleColumn: FC<OwnProps & StateProps> = ({
|
|||||||
: undefined;
|
: undefined;
|
||||||
}, [chatId, openChat]);
|
}, [chatId, openChat]);
|
||||||
|
|
||||||
useOnChange(() => {
|
useSyncEffect(() => {
|
||||||
setDropAreaState(DropAreaState.None);
|
setDropAreaState(DropAreaState.None);
|
||||||
setIsNotchShown(undefined);
|
setIsNotchShown(undefined);
|
||||||
}, [chatId]);
|
}, [chatId]);
|
||||||
@ -704,7 +704,7 @@ function useIsReady(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
useOnChange(() => {
|
useSyncEffect(() => {
|
||||||
if (!withAnimations) {
|
if (!withAnimations) {
|
||||||
setIsReady(true);
|
setIsReady(true);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -88,7 +88,7 @@ import useContextMenuHandlers from '../../../hooks/useContextMenuHandlers';
|
|||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
import useSendMessageAction from '../../../hooks/useSendMessageAction';
|
import useSendMessageAction from '../../../hooks/useSendMessageAction';
|
||||||
import useInterval from '../../../hooks/useInterval';
|
import useInterval from '../../../hooks/useInterval';
|
||||||
import useOnChange from '../../../hooks/useOnChange';
|
import useSyncEffect from '../../../hooks/useSyncEffect';
|
||||||
import { useStateRef } from '../../../hooks/useStateRef';
|
import { useStateRef } from '../../../hooks/useStateRef';
|
||||||
import useVoiceRecording from './hooks/useVoiceRecording';
|
import useVoiceRecording from './hooks/useVoiceRecording';
|
||||||
import useClipboardPaste from './hooks/useClipboardPaste';
|
import useClipboardPaste from './hooks/useClipboardPaste';
|
||||||
@ -338,7 +338,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
|||||||
}, [chat, chatId, isReady, lastSyncTime, loadSendAs, sendAsPeerIds]);
|
}, [chat, chatId, isReady, lastSyncTime, loadSendAs, sendAsPeerIds]);
|
||||||
|
|
||||||
const shouldAnimateSendAsButtonRef = useRef(false);
|
const shouldAnimateSendAsButtonRef = useRef(false);
|
||||||
useOnChange(([prevChatId, prevSendAsPeerIds]) => {
|
useSyncEffect(([prevChatId, prevSendAsPeerIds]) => {
|
||||||
// We only animate send-as button if `sendAsPeerIds` was missing when opening the chat
|
// We only animate send-as button if `sendAsPeerIds` was missing when opening the chat
|
||||||
shouldAnimateSendAsButtonRef.current = Boolean(chatId === prevChatId && sendAsPeerIds && !prevSendAsPeerIds);
|
shouldAnimateSendAsButtonRef.current = Boolean(chatId === prevChatId && sendAsPeerIds && !prevSendAsPeerIds);
|
||||||
}, [chatId, sendAsPeerIds]);
|
}, [chatId, sendAsPeerIds]);
|
||||||
|
|||||||
@ -185,7 +185,7 @@ const MessageInput: FC<OwnProps & StateProps> = ({
|
|||||||
if (prevHtml !== undefined && prevHtml !== html) {
|
if (prevHtml !== undefined && prevHtml !== html) {
|
||||||
updateInputHeight(!html.length);
|
updateInputHeight(!html.length);
|
||||||
}
|
}
|
||||||
}, [html]);
|
}, [html, updateInputHeight]);
|
||||||
|
|
||||||
const chatIdRef = useRef(chatId);
|
const chatIdRef = useRef(chatId);
|
||||||
chatIdRef.current = chatId;
|
chatIdRef.current = chatId;
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import type { ISettings } from '../../../types';
|
|||||||
import { RE_LINK_TEMPLATE } from '../../../config';
|
import { RE_LINK_TEMPLATE } from '../../../config';
|
||||||
import { selectTabState, selectNoWebPage, selectTheme } from '../../../global/selectors';
|
import { selectTabState, selectNoWebPage, selectTheme } from '../../../global/selectors';
|
||||||
import parseMessageInput from '../../../util/parseMessageInput';
|
import parseMessageInput from '../../../util/parseMessageInput';
|
||||||
import useOnChange from '../../../hooks/useOnChange';
|
import useSyncEffect from '../../../hooks/useSyncEffect';
|
||||||
import useShowTransition from '../../../hooks/useShowTransition';
|
import useShowTransition from '../../../hooks/useShowTransition';
|
||||||
import useCurrentOrPrev from '../../../hooks/useCurrentOrPrev';
|
import useCurrentOrPrev from '../../../hooks/useCurrentOrPrev';
|
||||||
import useDebouncedMemo from '../../../hooks/useDebouncedMemo';
|
import useDebouncedMemo from '../../../hooks/useDebouncedMemo';
|
||||||
@ -78,10 +78,10 @@ const WebPagePreview: FC<OwnProps & StateProps> = ({
|
|||||||
}
|
}
|
||||||
}, [chatId, toggleMessageWebPage, clearWebPagePreview, link, loadWebPagePreview, threadId]);
|
}, [chatId, toggleMessageWebPage, clearWebPagePreview, link, loadWebPagePreview, threadId]);
|
||||||
|
|
||||||
useOnChange(() => {
|
useSyncEffect(() => {
|
||||||
clearWebPagePreview();
|
clearWebPagePreview();
|
||||||
toggleMessageWebPage({ chatId, threadId });
|
toggleMessageWebPage({ chatId, threadId });
|
||||||
}, [chatId]);
|
}, [chatId, clearWebPagePreview, threadId, toggleMessageWebPage]);
|
||||||
|
|
||||||
const isShown = Boolean(webPagePreview && messageText.length && !noWebPage && !disabled);
|
const isShown = Boolean(webPagePreview && messageText.length && !noWebPage && !disabled);
|
||||||
const { shouldRender, transitionClassNames } = useShowTransition(isShown);
|
const { shouldRender, transitionClassNames } = useShowTransition(isShown);
|
||||||
|
|||||||
@ -56,6 +56,7 @@ const useEditing = (
|
|||||||
focusEditableElement(messageInput, true);
|
focusEditableElement(messageInput, true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps -- `as const` not yet supported by linter
|
||||||
}, [editedMessage, replyingToId, setHtml] as const);
|
}, [editedMessage, replyingToId, setHtml] as const);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import { LOCAL_MESSAGE_MIN_ID, MESSAGE_LIST_SLICE } from '../../../config';
|
|||||||
import { IS_SCROLL_PATCH_NEEDED, MESSAGE_LIST_SENSITIVE_AREA } from '../../../util/environment';
|
import { IS_SCROLL_PATCH_NEEDED, MESSAGE_LIST_SENSITIVE_AREA } from '../../../util/environment';
|
||||||
import { debounce } from '../../../util/schedulers';
|
import { debounce } from '../../../util/schedulers';
|
||||||
import { useIntersectionObserver, useOnIntersect } from '../../../hooks/useIntersectionObserver';
|
import { useIntersectionObserver, useOnIntersect } from '../../../hooks/useIntersectionObserver';
|
||||||
import useOnChange from '../../../hooks/useOnChange';
|
import useSyncEffect from '../../../hooks/useSyncEffect';
|
||||||
|
|
||||||
const FAB_THRESHOLD = 50;
|
const FAB_THRESHOLD = 50;
|
||||||
const NOTCH_THRESHOLD = 1; // Notch has zero height so we at least need a 1px margin to intersect
|
const NOTCH_THRESHOLD = 1; // Notch has zero height so we at least need a 1px margin to intersect
|
||||||
@ -136,14 +136,16 @@ export default function useScrollHooks(
|
|||||||
|
|
||||||
useOnIntersect(fabTriggerRef, observeIntersectionForNotch);
|
useOnIntersect(fabTriggerRef, observeIntersectionForNotch);
|
||||||
|
|
||||||
useOnChange(() => {
|
const toggleScrollToolsRef = useRef<typeof toggleScrollTools>();
|
||||||
|
toggleScrollToolsRef.current = toggleScrollTools;
|
||||||
|
useSyncEffect(() => {
|
||||||
if (isReady) {
|
if (isReady) {
|
||||||
toggleScrollTools();
|
toggleScrollToolsRef.current!();
|
||||||
}
|
}
|
||||||
}, [isReady]);
|
}, [isReady]);
|
||||||
|
|
||||||
// Workaround for FAB and notch flickering with tall incoming message
|
// Workaround for FAB and notch flickering with tall incoming message
|
||||||
useOnChange(() => {
|
useSyncEffect(() => {
|
||||||
freezeForFab();
|
freezeForFab();
|
||||||
freezeForNotch();
|
freezeForNotch();
|
||||||
|
|
||||||
@ -151,7 +153,7 @@ export default function useScrollHooks(
|
|||||||
unfreezeForNotch();
|
unfreezeForNotch();
|
||||||
unfreezeForFab();
|
unfreezeForFab();
|
||||||
}, TOOLS_FREEZE_TIMEOUT);
|
}, TOOLS_FREEZE_TIMEOUT);
|
||||||
}, [messageIds]);
|
}, [freezeForFab, freezeForNotch, messageIds, unfreezeForFab, unfreezeForNotch]);
|
||||||
|
|
||||||
return { backwardsTriggerRef, forwardsTriggerRef, fabTriggerRef };
|
return { backwardsTriggerRef, forwardsTriggerRef, fabTriggerRef };
|
||||||
}
|
}
|
||||||
|
|||||||
@ -65,7 +65,7 @@ const Invoice: FC<OwnProps> = ({
|
|||||||
contentEl.setAttribute(CUSTOM_APPENDIX_ATTRIBUTE, '');
|
contentEl.setAttribute(CUSTOM_APPENDIX_ATTRIBUTE, '');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [shouldAffectAppendix, photoUrl, isInSelectMode, isSelected, theme] as const);
|
}, [shouldAffectAppendix, photoUrl, isInSelectMode, isSelected, theme]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@ -161,7 +161,7 @@ const Location: FC<OwnProps> = ({
|
|||||||
contentEl.setAttribute(CUSTOM_APPENDIX_ATTRIBUTE, '');
|
contentEl.setAttribute(CUSTOM_APPENDIX_ATTRIBUTE, '');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [shouldRenderText, isOwn, isInSelectMode, isSelected, theme, mapBlobUrl] as const);
|
}, [shouldRenderText, isOwn, isInSelectMode, isSelected, theme, mapBlobUrl]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Prevent map refetching for slight location changes
|
// Prevent map refetching for slight location changes
|
||||||
|
|||||||
@ -161,7 +161,7 @@ const Photo: FC<OwnProps> = ({
|
|||||||
} else {
|
} else {
|
||||||
contentEl.classList.add('has-appendix-thumb');
|
contentEl.classList.add('has-appendix-thumb');
|
||||||
}
|
}
|
||||||
}, [shouldAffectAppendix, fullMediaData, isOwn, isInSelectMode, isSelected, theme] as const);
|
}, [shouldAffectAppendix, fullMediaData, isOwn, isInSelectMode, isSelected, theme]);
|
||||||
|
|
||||||
const { width, height, isSmall } = dimensions || calculateMediaDimensions(message, asForwarded, noAvatars, isMobile);
|
const { width, height, isSmall } = dimensions || calculateMediaDimensions(message, asForwarded, noAvatars, isMobile);
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { useEffect, useRef } from '../../../lib/teact/teact';
|
import { useEffect, useRef } from '../../../lib/teact/teact';
|
||||||
|
|
||||||
import useOnChange from '../../../hooks/useOnChange';
|
import useSyncEffect from '../../../hooks/useSyncEffect';
|
||||||
import useForceUpdate from '../../../hooks/useForceUpdate';
|
import useForceUpdate from '../../../hooks/useForceUpdate';
|
||||||
|
|
||||||
export default function useAsyncRendering<T extends any[]>(dependencies: T, delay?: number) {
|
export default function useAsyncRendering<T extends any[]>(dependencies: T, delay?: number) {
|
||||||
@ -9,7 +9,7 @@ export default function useAsyncRendering<T extends any[]>(dependencies: T, dela
|
|||||||
const timeoutRef = useRef<number>();
|
const timeoutRef = useRef<number>();
|
||||||
const forceUpdate = useForceUpdate();
|
const forceUpdate = useForceUpdate();
|
||||||
|
|
||||||
useOnChange(() => {
|
useSyncEffect(() => {
|
||||||
if (isDisabled) {
|
if (isDisabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -20,6 +20,7 @@ export default function useAsyncRendering<T extends any[]>(dependencies: T, dela
|
|||||||
clearTimeout(timeoutRef.current);
|
clearTimeout(timeoutRef.current);
|
||||||
timeoutRef.current = undefined;
|
timeoutRef.current = undefined;
|
||||||
}
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, dependencies);
|
}, dependencies);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@ -38,7 +38,7 @@ export default function useProfileState(
|
|||||||
}, PROGRAMMATIC_SCROLL_TIMEOUT_MS);
|
}, PROGRAMMATIC_SCROLL_TIMEOUT_MS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [tabType, isFirstTab, onProfileStateChange]);
|
}, [tabType, isFirstTab, onProfileStateChange, containerRef]);
|
||||||
|
|
||||||
// Scroll to top
|
// Scroll to top
|
||||||
useEffectWithPrevDeps(([prevProfileState]) => {
|
useEffectWithPrevDeps(([prevProfileState]) => {
|
||||||
@ -70,7 +70,7 @@ export default function useProfileState(
|
|||||||
}, PROGRAMMATIC_SCROLL_TIMEOUT_MS);
|
}, PROGRAMMATIC_SCROLL_TIMEOUT_MS);
|
||||||
|
|
||||||
onProfileStateChange(profileState);
|
onProfileStateChange(profileState);
|
||||||
}, [profileState]);
|
}, [profileState, containerRef, onProfileStateChange]);
|
||||||
|
|
||||||
const determineProfileState = useCallback(() => {
|
const determineProfileState = useCallback(() => {
|
||||||
const container = containerRef.current;
|
const container = containerRef.current;
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import type { ProfileTabType, SharedMediaType } from '../../../types';
|
|||||||
|
|
||||||
import { MEMBERS_SLICE, MESSAGE_SEARCH_SLICE, SHARED_MEDIA_SLICE } from '../../../config';
|
import { MEMBERS_SLICE, MESSAGE_SEARCH_SLICE, SHARED_MEDIA_SLICE } from '../../../config';
|
||||||
import { getMessageContentIds, sortChatIds, sortUserIds } from '../../../global/helpers';
|
import { getMessageContentIds, sortChatIds, sortUserIds } from '../../../global/helpers';
|
||||||
import useOnChange from '../../../hooks/useOnChange';
|
import useSyncEffect from '../../../hooks/useSyncEffect';
|
||||||
import useInfiniteScroll from '../../../hooks/useInfiniteScroll';
|
import useInfiniteScroll from '../../../hooks/useInfiniteScroll';
|
||||||
|
|
||||||
export default function useProfileViewportIds(
|
export default function useProfileViewportIds(
|
||||||
@ -150,11 +150,11 @@ function useInfiniteScrollForSharedMedia(
|
|||||||
) {
|
) {
|
||||||
const messageIdsRef = useRef<number[]>();
|
const messageIdsRef = useRef<number[]>();
|
||||||
|
|
||||||
useOnChange(() => {
|
useSyncEffect(() => {
|
||||||
messageIdsRef.current = undefined;
|
messageIdsRef.current = undefined;
|
||||||
}, [topicId]);
|
}, [topicId]);
|
||||||
|
|
||||||
useOnChange(() => {
|
useSyncEffect(() => {
|
||||||
if (currentResultType === forSharedMediaType && chatMessages && foundIds) {
|
if (currentResultType === forSharedMediaType && chatMessages && foundIds) {
|
||||||
messageIdsRef.current = getMessageContentIds(
|
messageIdsRef.current = getMessageContentIds(
|
||||||
chatMessages,
|
chatMessages,
|
||||||
|
|||||||
@ -18,7 +18,7 @@ import InputText from '../../ui/InputText';
|
|||||||
import RadioGroup from '../../ui/RadioGroup';
|
import RadioGroup from '../../ui/RadioGroup';
|
||||||
import Button from '../../ui/Button';
|
import Button from '../../ui/Button';
|
||||||
import FloatingActionButton from '../../ui/FloatingActionButton';
|
import FloatingActionButton from '../../ui/FloatingActionButton';
|
||||||
import useOnChange from '../../../hooks/useOnChange';
|
import useSyncEffect from '../../../hooks/useSyncEffect';
|
||||||
import CalendarModal from '../../common/CalendarModal';
|
import CalendarModal from '../../common/CalendarModal';
|
||||||
|
|
||||||
const DEFAULT_USAGE_LIMITS = [1, 10, 100];
|
const DEFAULT_USAGE_LIMITS = [1, 10, 100];
|
||||||
@ -64,7 +64,7 @@ const ManageInvite: FC<OwnProps & StateProps> = ({
|
|||||||
onBack: onClose,
|
onBack: onClose,
|
||||||
});
|
});
|
||||||
|
|
||||||
useOnChange(([oldEditingInvite]) => {
|
useSyncEffect(([oldEditingInvite]) => {
|
||||||
if (oldEditingInvite === editingInvite) return;
|
if (oldEditingInvite === editingInvite) return;
|
||||||
if (!editingInvite) {
|
if (!editingInvite) {
|
||||||
setTitle('');
|
setTitle('');
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import React, { memo, useCallback, useRef } from '../../lib/teact/teact';
|
|||||||
import useVideoAutoPause from '../middle/message/hooks/useVideoAutoPause';
|
import useVideoAutoPause from '../middle/message/hooks/useVideoAutoPause';
|
||||||
import useVideoCleanup from '../../hooks/useVideoCleanup';
|
import useVideoCleanup from '../../hooks/useVideoCleanup';
|
||||||
import useBuffering from '../../hooks/useBuffering';
|
import useBuffering from '../../hooks/useBuffering';
|
||||||
import useOnChange from '../../hooks/useOnChange';
|
import useSyncEffect from '../../hooks/useSyncEffect';
|
||||||
|
|
||||||
type OwnProps =
|
type OwnProps =
|
||||||
{
|
{
|
||||||
@ -40,13 +40,13 @@ function OptimizedVideo({
|
|||||||
// This is only needed for browsers not allowing autoplay
|
// This is only needed for browsers not allowing autoplay
|
||||||
const { isBuffered, bufferingHandlers } = useBuffering(true, onTimeUpdate);
|
const { isBuffered, bufferingHandlers } = useBuffering(true, onTimeUpdate);
|
||||||
const { onPlaying: handlePlayingForBuffering, ...otherBufferingHandlers } = bufferingHandlers;
|
const { onPlaying: handlePlayingForBuffering, ...otherBufferingHandlers } = bufferingHandlers;
|
||||||
useOnChange(([prevIsBuffered]) => {
|
useSyncEffect(([prevIsBuffered]) => {
|
||||||
if (prevIsBuffered === undefined) {
|
if (prevIsBuffered === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleReady();
|
handleReady();
|
||||||
}, [isBuffered]);
|
}, [isBuffered, handleReady]);
|
||||||
|
|
||||||
const handlePlaying = useCallback((e) => {
|
const handlePlaying = useCallback((e) => {
|
||||||
handlePlayingForAutoPause();
|
handlePlayingForAutoPause();
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import {
|
|||||||
import { selectTabState } from '../global/selectors';
|
import { selectTabState } from '../global/selectors';
|
||||||
|
|
||||||
import useEffectWithPrevDeps from './useEffectWithPrevDeps';
|
import useEffectWithPrevDeps from './useEffectWithPrevDeps';
|
||||||
import useOnChange from './useOnChange';
|
import useSyncEffect from './useSyncEffect';
|
||||||
|
|
||||||
type Handler = (e: Event) => void;
|
type Handler = (e: Event) => void;
|
||||||
|
|
||||||
@ -47,7 +47,7 @@ const useAudioPlayer = (
|
|||||||
if (onTrackChange) onTrackChange();
|
if (onTrackChange) onTrackChange();
|
||||||
}, [onTrackChange]);
|
}, [onTrackChange]);
|
||||||
|
|
||||||
useOnChange(() => {
|
useSyncEffect(() => {
|
||||||
controllerRef.current = register(trackId, trackType, (eventName, e) => {
|
controllerRef.current = register(trackId, trackType, (eventName, e) => {
|
||||||
switch (eventName) {
|
switch (eventName) {
|
||||||
case 'onPlay': {
|
case 'onPlay': {
|
||||||
@ -105,6 +105,8 @@ const useAudioPlayer = (
|
|||||||
|
|
||||||
if (!isPlaying && !proxy.paused) {
|
if (!isPlaying && !proxy.paused) {
|
||||||
setIsPlaying(true);
|
setIsPlaying(true);
|
||||||
|
// `isPlayingSync` is only needed to help `setIsPlaying` because it is asynchronous
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
isPlayingSync = true;
|
isPlayingSync = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { useRef } from '../lib/teact/teact';
|
import { useRef } from '../lib/teact/teact';
|
||||||
|
|
||||||
import fastBlur from '../lib/fastBlur';
|
import fastBlur from '../lib/fastBlur';
|
||||||
import useOnChange from './useOnChange';
|
import useSyncEffect from './useSyncEffect';
|
||||||
import useBlur from './useBlur';
|
import useBlur from './useBlur';
|
||||||
import { imgToCanvas } from '../util/files';
|
import { imgToCanvas } from '../util/files';
|
||||||
|
|
||||||
@ -13,7 +13,8 @@ export default function useBlurSync(dataUri: string | false | undefined) {
|
|||||||
|
|
||||||
let isChanged = false;
|
let isChanged = false;
|
||||||
|
|
||||||
useOnChange(() => {
|
useSyncEffect(() => {
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
isChanged = true;
|
isChanged = true;
|
||||||
|
|
||||||
blurredRef.current = undefined;
|
blurredRef.current = undefined;
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { useEffect, useRef } from '../lib/teact/teact';
|
|||||||
|
|
||||||
import { IS_CANVAS_FILTER_SUPPORTED } from '../util/environment';
|
import { IS_CANVAS_FILTER_SUPPORTED } from '../util/environment';
|
||||||
import fastBlur from '../lib/fastBlur';
|
import fastBlur from '../lib/fastBlur';
|
||||||
import useOnChange from './useOnChange';
|
import useSyncEffect from './useSyncEffect';
|
||||||
|
|
||||||
const RADIUS = 2;
|
const RADIUS = 2;
|
||||||
const ITERATIONS = 2;
|
const ITERATIONS = 2;
|
||||||
@ -19,7 +19,7 @@ export default function useCanvasBlur(
|
|||||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||||
const isStarted = useRef();
|
const isStarted = useRef();
|
||||||
|
|
||||||
useOnChange(() => {
|
useSyncEffect(() => {
|
||||||
if (!isDisabled) {
|
if (!isDisabled) {
|
||||||
isStarted.current = false;
|
isStarted.current = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { useCallback, useRef, useState } from '../lib/teact/teact';
|
import { useCallback, useRef, useState } from '../lib/teact/teact';
|
||||||
|
|
||||||
import useRunDebounced from './useRunDebounced';
|
import useRunDebounced from './useRunDebounced';
|
||||||
import useOnChange from './useOnChange';
|
import useSyncEffect from './useSyncEffect';
|
||||||
import useHeavyAnimationCheck, { isHeavyAnimating } from './useHeavyAnimationCheck';
|
import useHeavyAnimationCheck, { isHeavyAnimating } from './useHeavyAnimationCheck';
|
||||||
import useForceUpdate from './useForceUpdate';
|
import useForceUpdate from './useForceUpdate';
|
||||||
|
|
||||||
@ -12,7 +12,7 @@ export default function useDebouncedMemo<R extends any, D extends any[]>(
|
|||||||
const { isFrozen, updateWhenUnfrozen } = useHeavyAnimationFreeze();
|
const { isFrozen, updateWhenUnfrozen } = useHeavyAnimationFreeze();
|
||||||
const runDebounced = useRunDebounced(ms, true);
|
const runDebounced = useRunDebounced(ms, true);
|
||||||
|
|
||||||
useOnChange(() => {
|
useSyncEffect(() => {
|
||||||
if (isFrozen) {
|
if (isFrozen) {
|
||||||
updateWhenUnfrozen();
|
updateWhenUnfrozen();
|
||||||
return;
|
return;
|
||||||
@ -21,6 +21,7 @@ export default function useDebouncedMemo<R extends any, D extends any[]>(
|
|||||||
runDebounced(() => {
|
runDebounced(() => {
|
||||||
setValue(resolverFn());
|
setValue(resolverFn());
|
||||||
});
|
});
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [...dependencies, isFrozen]);
|
}, [...dependencies, isFrozen]);
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
|
|||||||
8
src/hooks/useEffectOnce.ts
Normal file
8
src/hooks/useEffectOnce.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { useEffect } from '../lib/teact/teact';
|
||||||
|
|
||||||
|
function useEffectOnce(effect: React.EffectCallback) {
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
useEffect(effect, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useEffectOnce;
|
||||||
@ -1,13 +1,13 @@
|
|||||||
import { useCallback, useRef } from '../lib/teact/teact';
|
import { useCallback, useRef } from '../lib/teact/teact';
|
||||||
|
|
||||||
import useForceUpdate from './useForceUpdate';
|
import useForceUpdate from './useForceUpdate';
|
||||||
import useOnChange from './useOnChange';
|
import useSyncEffect from './useSyncEffect';
|
||||||
|
|
||||||
export default function useForumPanelRender(isForumPanelOpen = false) {
|
export default function useForumPanelRender(isForumPanelOpen = false) {
|
||||||
const shouldRenderForumPanelRef = useRef(isForumPanelOpen);
|
const shouldRenderForumPanelRef = useRef(isForumPanelOpen);
|
||||||
const forceUpdate = useForceUpdate();
|
const forceUpdate = useForceUpdate();
|
||||||
|
|
||||||
useOnChange(() => {
|
useSyncEffect(() => {
|
||||||
if (isForumPanelOpen) {
|
if (isForumPanelOpen) {
|
||||||
shouldRenderForumPanelRef.current = true;
|
shouldRenderForumPanelRef.current = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,12 @@
|
|||||||
import useOnChange from './useOnChange';
|
import { useCallback, useRef } from '../lib/teact/teact';
|
||||||
import { useEffect, useRef } from '../lib/teact/teact';
|
import { getActions } from '../lib/teact/teactn';
|
||||||
|
|
||||||
import { IS_TEST } from '../config';
|
import { IS_TEST } from '../config';
|
||||||
import { fastRaf } from '../util/schedulers';
|
import { fastRaf } from '../util/schedulers';
|
||||||
import { IS_IOS } from '../util/environment';
|
import { IS_IOS } from '../util/environment';
|
||||||
import { getActions } from '../lib/teact/teactn';
|
|
||||||
|
import useSyncEffect from './useSyncEffect';
|
||||||
|
import useEffectOnce from './useEffectOnce';
|
||||||
|
|
||||||
export const LOCATION_HASH = window.location.hash;
|
export const LOCATION_HASH = window.location.hash;
|
||||||
const PATH_BASE = `${window.location.pathname}${window.location.search}`;
|
const PATH_BASE = `${window.location.pathname}${window.location.search}`;
|
||||||
@ -241,7 +244,7 @@ export default function useHistoryBack({
|
|||||||
|
|
||||||
const isFirstRender = useRef(true);
|
const isFirstRender = useRef(true);
|
||||||
|
|
||||||
const pushState = (forceReplace = false) => {
|
const pushState = useCallback((forceReplace = false) => {
|
||||||
// Check if the old state should be replaced
|
// Check if the old state should be replaced
|
||||||
const shouldReplace = forceReplace || historyState[historyCursor].shouldBeReplaced;
|
const shouldReplace = forceReplace || historyState[historyCursor].shouldBeReplaced;
|
||||||
indexRef.current = shouldReplace ? historyCursor : ++historyCursor;
|
indexRef.current = shouldReplace ? historyCursor : ++historyCursor;
|
||||||
@ -276,9 +279,9 @@ export default function useHistoryBack({
|
|||||||
},
|
},
|
||||||
hash: hash ? `#${hash}` : undefined,
|
hash: hash ? `#${hash}` : undefined,
|
||||||
});
|
});
|
||||||
};
|
}, [hash, onBack, shouldBeReplaced]);
|
||||||
|
|
||||||
const processBack = () => {
|
const processBack = useCallback(() => {
|
||||||
// Only process back on open records
|
// Only process back on open records
|
||||||
if (indexRef.current && historyState[indexRef.current] && !wasReplaced.current) {
|
if (indexRef.current && historyState[indexRef.current] && !wasReplaced.current) {
|
||||||
historyState[indexRef.current].isClosed = true;
|
historyState[indexRef.current].isClosed = true;
|
||||||
@ -287,19 +290,19 @@ export default function useHistoryBack({
|
|||||||
historyCursor -= cleanupClosed();
|
historyCursor -= cleanupClosed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}, [shouldBeReplaced]);
|
||||||
|
|
||||||
// Process back navigation when element is unmounted
|
// Process back navigation when element is unmounted
|
||||||
useEffect(() => {
|
useEffectOnce(() => {
|
||||||
isFirstRender.current = false;
|
isFirstRender.current = false;
|
||||||
return () => {
|
return () => {
|
||||||
if (!isActive || wasReplaced.current) return;
|
if (!isActive || wasReplaced.current) return;
|
||||||
processBack();
|
processBack();
|
||||||
};
|
};
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
});
|
||||||
}, []);
|
|
||||||
|
|
||||||
useOnChange(() => {
|
useSyncEffect(([prevIsActive]) => {
|
||||||
|
if (prevIsActive === isActive) return;
|
||||||
if (isFirstRender.current && !isActive) return;
|
if (isFirstRender.current && !isActive) return;
|
||||||
|
|
||||||
if (isActive) {
|
if (isActive) {
|
||||||
@ -307,5 +310,5 @@ export default function useHistoryBack({
|
|||||||
} else {
|
} else {
|
||||||
processBack();
|
processBack();
|
||||||
}
|
}
|
||||||
}, [isActive]);
|
}, [isActive, processBack, pushState]);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import {
|
|||||||
} from '../lib/teact/teact';
|
} from '../lib/teact/teact';
|
||||||
|
|
||||||
import { throttle, debounce } from '../util/schedulers';
|
import { throttle, debounce } from '../util/schedulers';
|
||||||
|
import useEffectOnce from './useEffectOnce';
|
||||||
import useHeavyAnimationCheck from './useHeavyAnimationCheck';
|
import useHeavyAnimationCheck from './useHeavyAnimationCheck';
|
||||||
|
|
||||||
type TargetCallback = (entry: IntersectionObserverEntry) => void;
|
type TargetCallback = (entry: IntersectionObserverEntry) => void;
|
||||||
@ -155,11 +156,10 @@ export function useIntersectionObserver({
|
|||||||
export function useOnIntersect(
|
export function useOnIntersect(
|
||||||
targetRef: RefObject<HTMLDivElement>, observe?: ObserveFn, callback?: TargetCallback,
|
targetRef: RefObject<HTMLDivElement>, observe?: ObserveFn, callback?: TargetCallback,
|
||||||
) {
|
) {
|
||||||
useEffect(() => {
|
useEffectOnce(() => {
|
||||||
return observe ? observe(targetRef.current!, callback) : undefined;
|
return observe ? observe(targetRef.current!, callback) : undefined;
|
||||||
// Arguments should never change
|
// Arguments should never change
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
});
|
||||||
}, []);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useIsIntersecting(
|
export function useIsIntersecting(
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
import * as langProvider from '../util/langProvider';
|
import * as langProvider from '../util/langProvider';
|
||||||
import useForceUpdate from './useForceUpdate';
|
import useForceUpdate from './useForceUpdate';
|
||||||
import useOnChange from './useOnChange';
|
import useSyncEffect from './useSyncEffect';
|
||||||
|
|
||||||
export type LangFn = langProvider.LangFn;
|
export type LangFn = langProvider.LangFn;
|
||||||
|
|
||||||
const useLang = (): LangFn => {
|
const useLang = (): LangFn => {
|
||||||
const forceUpdate = useForceUpdate();
|
const forceUpdate = useForceUpdate();
|
||||||
|
|
||||||
useOnChange(() => {
|
useSyncEffect(() => {
|
||||||
return langProvider.addCallback(forceUpdate);
|
return langProvider.addCallback(forceUpdate);
|
||||||
}, [forceUpdate]);
|
}, [forceUpdate]);
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { useRef } from '../lib/teact/teact';
|
|||||||
|
|
||||||
import usePrevious from './usePrevious';
|
import usePrevious from './usePrevious';
|
||||||
import useForceUpdate from './useForceUpdate';
|
import useForceUpdate from './useForceUpdate';
|
||||||
import useOnChange from './useOnChange';
|
import useSyncEffect from './useSyncEffect';
|
||||||
|
|
||||||
export default function usePrevDuringAnimation(current: any, duration?: number) {
|
export default function usePrevDuringAnimation(current: any, duration?: number) {
|
||||||
const prev = usePrevious(current, true);
|
const prev = usePrevious(current, true);
|
||||||
@ -18,7 +18,7 @@ export default function usePrevDuringAnimation(current: any, duration?: number)
|
|||||||
timeoutRef.current = undefined;
|
timeoutRef.current = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
useOnChange(() => {
|
useSyncEffect(() => {
|
||||||
// When `current` becomes empty
|
// When `current` becomes empty
|
||||||
if (duration && !isCurrentPresent && isPrevPresent && !timeoutRef.current) {
|
if (duration && !isCurrentPresent && isPrevPresent && !timeoutRef.current) {
|
||||||
timeoutRef.current = window.setTimeout(() => {
|
timeoutRef.current = window.setTimeout(() => {
|
||||||
@ -26,7 +26,7 @@ export default function usePrevDuringAnimation(current: any, duration?: number)
|
|||||||
forceUpdate();
|
forceUpdate();
|
||||||
}, duration);
|
}, duration);
|
||||||
}
|
}
|
||||||
}, [current]);
|
}, [duration, forceUpdate, isCurrentPresent, isPrevPresent]);
|
||||||
|
|
||||||
return !timeoutRef.current || !duration || isCurrentPresent ? current : prev;
|
return !timeoutRef.current || !duration || isCurrentPresent ? current : prev;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
import { useRef } from '../lib/teact/teact';
|
import { useRef } from '../lib/teact/teact';
|
||||||
|
|
||||||
import useOnChange from './useOnChange';
|
import useSyncEffect from './useSyncEffect';
|
||||||
|
|
||||||
// Allows to use state value as "silent" dependency in hooks (not causing updates).
|
// Allows to use state value as "silent" dependency in hooks (not causing updates).
|
||||||
// Useful for state values that update frequently (such as controlled input value).
|
// Useful for state values that update frequently (such as controlled input value).
|
||||||
export function useStateRef<T>(value: T) {
|
export function useStateRef<T>(value: T) {
|
||||||
const ref = useRef<T>(value);
|
const ref = useRef<T>(value);
|
||||||
|
|
||||||
useOnChange(() => {
|
useSyncEffect(() => {
|
||||||
ref.current = value;
|
ref.current = value;
|
||||||
}, [value]);
|
}, [value]);
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import usePrevious from './usePrevious';
|
import usePrevious from './usePrevious';
|
||||||
|
|
||||||
const useOnChange = <T extends readonly any[]>(cb: (args: T | readonly []) => void, dependencies: T) => {
|
const useSyncEffect = <T extends readonly any[]>(cb: (args: T | readonly []) => void, dependencies: T) => {
|
||||||
const prevDeps = usePrevious<T>(dependencies);
|
const prevDeps = usePrevious<T>(dependencies);
|
||||||
if (!prevDeps || dependencies.some((d, i) => d !== prevDeps[i])) {
|
if (!prevDeps || dependencies.some((d, i) => d !== prevDeps[i])) {
|
||||||
cb(prevDeps || []);
|
cb(prevDeps || []);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default useOnChange;
|
export default useSyncEffect;
|
||||||
@ -10,7 +10,7 @@ const THROTTLE = 250;
|
|||||||
const useWindowSize = () => {
|
const useWindowSize = () => {
|
||||||
const [size, setSize] = useState<ApiDimensions>(windowSize.get());
|
const [size, setSize] = useState<ApiDimensions>(windowSize.get());
|
||||||
const [isResizing, setIsResizing] = useState(false);
|
const [isResizing, setIsResizing] = useState(false);
|
||||||
const setIsResizingDebounced = useDebouncedCallback(setIsResizing, [], THROTTLE, true);
|
const setIsResizingDebounced = useDebouncedCallback(setIsResizing, [setIsResizing], THROTTLE, true);
|
||||||
|
|
||||||
const result = useMemo(() => ({ ...size, isResizing }), [isResizing, size]);
|
const result = useMemo(() => ({ ...size, isResizing }), [isResizing, size]);
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user