[Refactoring] Main: Simplify column animation
This commit is contained in:
parent
c67560d431
commit
4d448e4d07
@ -7,7 +7,7 @@ import { getActions, withGlobal } from '../../../global';
|
|||||||
import { IAnchorPosition } from '../../../types';
|
import { IAnchorPosition } from '../../../types';
|
||||||
|
|
||||||
import buildClassName from '../../../util/buildClassName';
|
import buildClassName from '../../../util/buildClassName';
|
||||||
import useThrottle from '../../../hooks/useThrottle';
|
import useRunThrottled from '../../../hooks/useRunThrottled';
|
||||||
import useFlag from '../../../hooks/useFlag';
|
import useFlag from '../../../hooks/useFlag';
|
||||||
import useLang from '../../../hooks/useLang';
|
import useLang from '../../../hooks/useLang';
|
||||||
import { selectIsAdminInActiveGroupCall } from '../../../global/selectors/calls';
|
import { selectIsAdminInActiveGroupCall } from '../../../global/selectors/calls';
|
||||||
@ -80,7 +80,7 @@ const GroupCallParticipantMenu: FC<OwnProps & StateProps> = ({
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [id]);
|
}, [id]);
|
||||||
|
|
||||||
const runThrottled = useThrottle(VOLUME_CHANGE_THROTTLE);
|
const runThrottled = useRunThrottled(VOLUME_CHANGE_THROTTLE);
|
||||||
|
|
||||||
const handleRemove = useCallback((e: React.SyntheticEvent<any>) => {
|
const handleRemove = useCallback((e: React.SyntheticEvent<any>) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { ChangeEvent } from 'react';
|
import { ChangeEvent } from 'react';
|
||||||
import useDebounce from '../../../hooks/useDebounce';
|
import useRunDebounced from '../../../hooks/useRunDebounced';
|
||||||
import React, {
|
import React, {
|
||||||
FC, memo, useCallback, useEffect,
|
FC, memo, useCallback, useEffect,
|
||||||
} from '../../../lib/teact/teact';
|
} from '../../../lib/teact/teact';
|
||||||
@ -59,7 +59,7 @@ const SettingsNotifications: FC<OwnProps & StateProps> = ({
|
|||||||
loadNotificationSettings();
|
loadNotificationSettings();
|
||||||
}, [loadNotificationSettings]);
|
}, [loadNotificationSettings]);
|
||||||
|
|
||||||
const runDebounced = useDebounce(500, true);
|
const runDebounced = useRunDebounced(500, true);
|
||||||
|
|
||||||
const handleSettingsChange = useCallback((
|
const handleSettingsChange = useCallback((
|
||||||
e: ChangeEvent<HTMLInputElement>,
|
e: ChangeEvent<HTMLInputElement>,
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import {
|
|||||||
getMessageContentFilename, getMessageMediaHash,
|
getMessageContentFilename, getMessageMediaHash,
|
||||||
} from '../../global/helpers';
|
} from '../../global/helpers';
|
||||||
|
|
||||||
import useDebounce from '../../hooks/useDebounce';
|
import useRunDebounced from '../../hooks/useRunDebounced';
|
||||||
|
|
||||||
type StateProps = {
|
type StateProps = {
|
||||||
activeDownloads: Record<string, number[]>;
|
activeDownloads: Record<string, number[]>;
|
||||||
@ -33,17 +33,17 @@ const DownloadManager: FC<StateProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { cancelMessagesMediaDownload } = getActions();
|
const { cancelMessagesMediaDownload } = getActions();
|
||||||
|
|
||||||
const debouncedGlobalUpdate = useDebounce(GLOBAL_UPDATE_DEBOUNCE, true);
|
const runDebounced = useRunDebounced(GLOBAL_UPDATE_DEBOUNCE, true);
|
||||||
|
|
||||||
const handleMessageDownloaded = useCallback((message: ApiMessage) => {
|
const handleMessageDownloaded = useCallback((message: ApiMessage) => {
|
||||||
downloadedMessages.add(message);
|
downloadedMessages.add(message);
|
||||||
debouncedGlobalUpdate(() => {
|
runDebounced(() => {
|
||||||
if (downloadedMessages.size) {
|
if (downloadedMessages.size) {
|
||||||
cancelMessagesMediaDownload({ messages: Array.from(downloadedMessages) });
|
cancelMessagesMediaDownload({ messages: Array.from(downloadedMessages) });
|
||||||
downloadedMessages.clear();
|
downloadedMessages.clear();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, [cancelMessagesMediaDownload, debouncedGlobalUpdate]);
|
}, [cancelMessagesMediaDownload, runDebounced]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const activeMessages = Object.entries(activeDownloads).map(([chatId, messageIds]) => (
|
const activeMessages = Object.entries(activeDownloads).map(([chatId, messageIds]) => (
|
||||||
|
|||||||
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
.has-call-header {
|
.has-call-header {
|
||||||
--call-header-height: 2rem;
|
--call-header-height: 2rem;
|
||||||
|
|
||||||
#LeftColumn, #MiddleColumn, #RightColumn-wrapper {
|
#LeftColumn, #MiddleColumn, #RightColumn-wrapper {
|
||||||
height: calc(100% - 2rem);
|
height: calc(100% - 2rem);
|
||||||
margin-top: 2rem;
|
margin-top: 2rem;
|
||||||
@ -74,9 +75,10 @@
|
|||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
background: black;
|
background: black;
|
||||||
opacity: var(--layer-blackout-opacity);
|
opacity: 0;
|
||||||
transition: opacity var(--layer-transition);
|
transition: opacity var(--layer-transition);
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
pointer-events: none;
|
||||||
|
|
||||||
body.animation-level-0 & {
|
body.animation-level-0 & {
|
||||||
transition: none;
|
transition: none;
|
||||||
@ -87,18 +89,18 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.is-android .middle-column-shown & {
|
body.is-android #Main.left-column-animating & {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body:not(.is-android) #Main:not(.left-column-open) &,
|
||||||
|
body.android-left-blackout-open & {
|
||||||
|
opacity: var(--layer-blackout-opacity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#Main:not(.middle-column-open) & {
|
#Main.left-column-open & {
|
||||||
transform: translate3d(0, 0, 0);
|
transform: translate3d(0, 0, 0);
|
||||||
|
|
||||||
&::after {
|
|
||||||
opacity: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#Main.history-animation-disabled & {
|
#Main.history-animation-disabled & {
|
||||||
@ -154,7 +156,7 @@
|
|||||||
transition: none;
|
transition: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#Main:not(.middle-column-open) & {
|
#Main.left-column-open & {
|
||||||
transform: translate3d(26.5rem, 0, 0);
|
transform: translate3d(26.5rem, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -162,7 +164,7 @@
|
|||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
border-left: none;
|
border-left: none;
|
||||||
|
|
||||||
#Main:not(.middle-column-open) & {
|
#Main.left-column-open & {
|
||||||
transform: translate3d(100vw, 0, 0);
|
transform: translate3d(100vw, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,7 +189,7 @@ body.is-android.animation-level-1 {
|
|||||||
transition: transform var(--layer-transition), opacity var(--layer-transition);
|
transition: transform var(--layer-transition), opacity var(--layer-transition);
|
||||||
}
|
}
|
||||||
|
|
||||||
#Main:not(.middle-column-shown) {
|
#Main.left-column-open:not(.left-column-animating) {
|
||||||
#MiddleColumn {
|
#MiddleColumn {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@ -200,14 +202,15 @@ body.is-android.animation-level-1 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#Main.middle-column-open {
|
#Main:not(.left-column-open) {
|
||||||
#LeftColumn {
|
#LeftColumn {
|
||||||
transform: translate3d(0, 0, 0);
|
transform: translate3d(0, 0, 0);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#Main:not(.right-column-shown) {
|
// @optimization
|
||||||
|
#Main:not(.right-column-open):not(.right-column-animating) {
|
||||||
#RightColumn {
|
#RightColumn {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import React, {
|
import React, {
|
||||||
FC, useEffect, memo, useCallback,
|
FC, useEffect, memo, useCallback, useState, useRef,
|
||||||
} from '../../lib/teact/teact';
|
} from '../../lib/teact/teact';
|
||||||
import { getActions, withGlobal } from '../../global';
|
import { getActions, withGlobal } from '../../global';
|
||||||
|
|
||||||
@ -11,6 +11,7 @@ import '../../global/actions/all';
|
|||||||
import {
|
import {
|
||||||
BASE_EMOJI_KEYWORD_LANG, DEBUG, INACTIVE_MARKER, PAGE_TITLE,
|
BASE_EMOJI_KEYWORD_LANG, DEBUG, INACTIVE_MARKER, PAGE_TITLE,
|
||||||
} from '../../config';
|
} from '../../config';
|
||||||
|
import { IS_ANDROID } from '../../util/environment';
|
||||||
import {
|
import {
|
||||||
selectChatMessage,
|
selectChatMessage,
|
||||||
selectIsForwardModalOpen,
|
selectIsForwardModalOpen,
|
||||||
@ -20,18 +21,19 @@ import {
|
|||||||
} from '../../global/selectors';
|
} from '../../global/selectors';
|
||||||
import { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck';
|
import { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck';
|
||||||
import buildClassName from '../../util/buildClassName';
|
import buildClassName from '../../util/buildClassName';
|
||||||
import { fastRaf } from '../../util/schedulers';
|
|
||||||
import { waitForTransitionEnd } from '../../util/cssAnimationEndListeners';
|
import { waitForTransitionEnd } from '../../util/cssAnimationEndListeners';
|
||||||
import { processDeepLink } from '../../util/deeplink';
|
import { processDeepLink } from '../../util/deeplink';
|
||||||
import stopEvent from '../../util/stopEvent';
|
import stopEvent from '../../util/stopEvent';
|
||||||
import windowSize from '../../util/windowSize';
|
import windowSize from '../../util/windowSize';
|
||||||
import { getAllNotificationsCount } from '../../util/folderManager';
|
import { getAllNotificationsCount } from '../../util/folderManager';
|
||||||
import useShowTransition from '../../hooks/useShowTransition';
|
|
||||||
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 useOnChange from '../../hooks/useOnChange';
|
||||||
import usePreventPinchZoomGesture from '../../hooks/usePreventPinchZoomGesture';
|
import usePreventPinchZoomGesture from '../../hooks/usePreventPinchZoomGesture';
|
||||||
|
import useForceUpdate from '../../hooks/useForceUpdate';
|
||||||
import { LOCATION_HASH } from '../../hooks/useHistoryBack';
|
import { LOCATION_HASH } from '../../hooks/useHistoryBack';
|
||||||
|
import useShowTransition from '../../hooks/useShowTransition';
|
||||||
|
import { fastRaf } from '../../util/schedulers';
|
||||||
|
|
||||||
import StickerSetModal from '../common/StickerSetModal.async';
|
import StickerSetModal from '../common/StickerSetModal.async';
|
||||||
import UnreadCount from '../common/UnreadCounter';
|
import UnreadCount from '../common/UnreadCounter';
|
||||||
@ -59,8 +61,8 @@ type StateProps = {
|
|||||||
connectionState?: ApiUpdateConnectionStateType;
|
connectionState?: ApiUpdateConnectionStateType;
|
||||||
authState?: ApiUpdateAuthorizationStateType;
|
authState?: ApiUpdateAuthorizationStateType;
|
||||||
lastSyncTime?: number;
|
lastSyncTime?: number;
|
||||||
isLeftColumnShown: boolean;
|
isLeftColumnOpen: boolean;
|
||||||
isRightColumnShown: boolean;
|
isRightColumnOpen: boolean;
|
||||||
isMediaViewerOpen: boolean;
|
isMediaViewerOpen: boolean;
|
||||||
isForwardModalOpen: boolean;
|
isForwardModalOpen: boolean;
|
||||||
hasNotifications: boolean;
|
hasNotifications: boolean;
|
||||||
@ -95,8 +97,8 @@ const Main: FC<StateProps> = ({
|
|||||||
connectionState,
|
connectionState,
|
||||||
authState,
|
authState,
|
||||||
lastSyncTime,
|
lastSyncTime,
|
||||||
isLeftColumnShown,
|
isLeftColumnOpen,
|
||||||
isRightColumnShown,
|
isRightColumnOpen,
|
||||||
isMediaViewerOpen,
|
isMediaViewerOpen,
|
||||||
isForwardModalOpen,
|
isForwardModalOpen,
|
||||||
hasNotifications,
|
hasNotifications,
|
||||||
@ -224,51 +226,69 @@ const Main: FC<StateProps> = ({
|
|||||||
};
|
};
|
||||||
}, [activeGroupCallId]);
|
}, [activeGroupCallId]);
|
||||||
|
|
||||||
const {
|
const leftColumnTransition = useShowTransition(
|
||||||
transitionClassNames: middleColumnTransitionClassNames,
|
isLeftColumnOpen, undefined, true, undefined, shouldSkipHistoryAnimations,
|
||||||
} = useShowTransition(!isLeftColumnShown, undefined, true, undefined, shouldSkipHistoryAnimations);
|
|
||||||
|
|
||||||
const {
|
|
||||||
transitionClassNames: rightColumnTransitionClassNames,
|
|
||||||
} = useShowTransition(isRightColumnShown, undefined, true, undefined, shouldSkipHistoryAnimations);
|
|
||||||
|
|
||||||
const className = buildClassName(
|
|
||||||
middleColumnTransitionClassNames.replace(/([\w-]+)/g, 'middle-column-$1'),
|
|
||||||
rightColumnTransitionClassNames.replace(/([\w-]+)/g, 'right-column-$1'),
|
|
||||||
shouldSkipHistoryAnimations && 'history-animation-disabled',
|
|
||||||
);
|
);
|
||||||
|
const willAnimateLeftColumnRef = useRef(false);
|
||||||
|
const forceUpdate = useForceUpdate();
|
||||||
|
|
||||||
// Dispatch heavy transition event when opening middle column
|
// Handle opening middle column
|
||||||
useOnChange(([prevIsLeftColumnShown]) => {
|
useOnChange(([prevIsLeftColumnOpen]) => {
|
||||||
if (prevIsLeftColumnShown === undefined || animationLevel === 0) {
|
if (prevIsLeftColumnOpen === undefined || animationLevel === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
willAnimateLeftColumnRef.current = true;
|
||||||
|
|
||||||
|
if (IS_ANDROID) {
|
||||||
|
fastRaf(() => {
|
||||||
|
document.body.classList.toggle('android-left-blackout-open', !isLeftColumnOpen);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const dispatchHeavyAnimationEnd = dispatchHeavyAnimationEvent();
|
const dispatchHeavyAnimationEnd = dispatchHeavyAnimationEvent();
|
||||||
|
|
||||||
waitForTransitionEnd(document.getElementById('MiddleColumn')!, dispatchHeavyAnimationEnd);
|
waitForTransitionEnd(document.getElementById('MiddleColumn')!, () => {
|
||||||
}, [isLeftColumnShown]);
|
dispatchHeavyAnimationEnd();
|
||||||
|
willAnimateLeftColumnRef.current = false;
|
||||||
|
forceUpdate();
|
||||||
|
});
|
||||||
|
}, [isLeftColumnOpen]);
|
||||||
|
|
||||||
// Dispatch heavy transition event and add body class when opening right column
|
const rightColumnTransition = useShowTransition(
|
||||||
useOnChange(([prevIsRightColumnShown]) => {
|
isRightColumnOpen, undefined, true, undefined, shouldSkipHistoryAnimations,
|
||||||
if (prevIsRightColumnShown === undefined || animationLevel === 0) {
|
);
|
||||||
|
const willAnimateRightColumnRef = useRef(false);
|
||||||
|
const [isNarrowMessageList, setIsNarrowMessageList] = useState(isRightColumnOpen);
|
||||||
|
|
||||||
|
// Handle opening right column
|
||||||
|
useOnChange(([prevIsRightColumnOpen]) => {
|
||||||
|
if (prevIsRightColumnOpen === undefined || animationLevel === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fastRaf(() => {
|
willAnimateRightColumnRef.current = true;
|
||||||
document.body.classList.add('animating-right-column');
|
|
||||||
});
|
|
||||||
|
|
||||||
const dispatchHeavyAnimationEnd = dispatchHeavyAnimationEvent();
|
const dispatchHeavyAnimationEnd = dispatchHeavyAnimationEvent();
|
||||||
|
|
||||||
waitForTransitionEnd(document.getElementById('RightColumn')!, () => {
|
waitForTransitionEnd(document.getElementById('RightColumn')!, () => {
|
||||||
dispatchHeavyAnimationEnd();
|
dispatchHeavyAnimationEnd();
|
||||||
|
willAnimateRightColumnRef.current = false;
|
||||||
fastRaf(() => {
|
forceUpdate();
|
||||||
document.body.classList.remove('animating-right-column');
|
setIsNarrowMessageList(isRightColumnOpen);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}, [isRightColumnShown]);
|
}, [isRightColumnOpen]);
|
||||||
|
|
||||||
|
const className = buildClassName(
|
||||||
|
leftColumnTransition.hasShownClass && 'left-column-shown',
|
||||||
|
leftColumnTransition.hasOpenClass && 'left-column-open',
|
||||||
|
willAnimateLeftColumnRef.current && 'left-column-animating',
|
||||||
|
rightColumnTransition.hasShownClass && 'right-column-shown',
|
||||||
|
rightColumnTransition.hasOpenClass && 'right-column-open',
|
||||||
|
willAnimateRightColumnRef.current && 'right-column-animating',
|
||||||
|
isNarrowMessageList && 'narrow-message-list',
|
||||||
|
shouldSkipHistoryAnimations && 'history-animation-disabled',
|
||||||
|
);
|
||||||
|
|
||||||
const handleBlur = useCallback(() => {
|
const handleBlur = useCallback(() => {
|
||||||
updateIsOnline(false);
|
updateIsOnline(false);
|
||||||
@ -390,8 +410,8 @@ export default memo(withGlobal(
|
|||||||
connectionState: global.connectionState,
|
connectionState: global.connectionState,
|
||||||
authState: global.authState,
|
authState: global.authState,
|
||||||
lastSyncTime: global.lastSyncTime,
|
lastSyncTime: global.lastSyncTime,
|
||||||
isLeftColumnShown: global.isLeftColumnShown,
|
isLeftColumnOpen: global.isLeftColumnShown,
|
||||||
isRightColumnShown: selectIsRightColumnShown(global),
|
isRightColumnOpen: selectIsRightColumnShown(global),
|
||||||
isMediaViewerOpen: selectIsMediaViewerOpen(global),
|
isMediaViewerOpen: selectIsMediaViewerOpen(global),
|
||||||
isForwardModalOpen: selectIsForwardModalOpen(global),
|
isForwardModalOpen: selectIsForwardModalOpen(global),
|
||||||
hasNotifications: Boolean(global.notifications.length),
|
hasNotifications: Boolean(global.notifications.length),
|
||||||
|
|||||||
@ -4,18 +4,18 @@ import React, {
|
|||||||
|
|
||||||
import { MediaViewerOrigin } from '../../types';
|
import { MediaViewerOrigin } from '../../types';
|
||||||
|
|
||||||
import useDebounce from '../../hooks/useDebounce';
|
|
||||||
import useForceUpdate from '../../hooks/useForceUpdate';
|
import useForceUpdate from '../../hooks/useForceUpdate';
|
||||||
import { animateNumber, timingFunctions } from '../../util/animation';
|
import { animateNumber, timingFunctions } from '../../util/animation';
|
||||||
import arePropsShallowEqual from '../../util/arePropsShallowEqual';
|
import arePropsShallowEqual from '../../util/arePropsShallowEqual';
|
||||||
import { captureEvents, IOS_SCREEN_EDGE_THRESHOLD, RealTouchEvent } from '../../util/captureEvents';
|
import { captureEvents, IOS_SCREEN_EDGE_THRESHOLD, RealTouchEvent } from '../../util/captureEvents';
|
||||||
import { IS_IOS, IS_TOUCH_ENV } from '../../util/environment';
|
import { IS_IOS, IS_TOUCH_ENV } from '../../util/environment';
|
||||||
import { debounce } from '../../util/schedulers';
|
import { debounce } from '../../util/schedulers';
|
||||||
|
import useTimeout from '../../hooks/useTimeout';
|
||||||
|
import useDebouncedCallback from '../../hooks/useDebouncedCallback';
|
||||||
|
|
||||||
import MediaViewerContent from './MediaViewerContent';
|
import MediaViewerContent from './MediaViewerContent';
|
||||||
|
|
||||||
import './MediaViewerSlides.scss';
|
import './MediaViewerSlides.scss';
|
||||||
import useTimeout from '../../hooks/useTimeout';
|
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
messageId?: number;
|
messageId?: number;
|
||||||
@ -96,14 +96,14 @@ const MediaViewerSlides: FC<OwnProps> = ({
|
|||||||
forceUpdate();
|
forceUpdate();
|
||||||
}, [forceUpdate]);
|
}, [forceUpdate]);
|
||||||
|
|
||||||
const setIsActive = useCallback((value: boolean) => {
|
const selectMessageDebounced = useDebouncedCallback(selectMessage, [], DEBOUNCE_MESSAGE, true);
|
||||||
|
const clearSwipeDirectionDebounced = useDebouncedCallback(() => {
|
||||||
|
swipeDirectionRef.current = undefined;
|
||||||
|
}, [], DEBOUNCE_SWIPE, true);
|
||||||
|
const setIsActiveDebounced = useDebouncedCallback((value: boolean) => {
|
||||||
isActiveRef.current = value;
|
isActiveRef.current = value;
|
||||||
forceUpdate();
|
forceUpdate();
|
||||||
}, [forceUpdate]);
|
}, [forceUpdate], DEBOUNCE_ACTIVE, true);
|
||||||
|
|
||||||
const debounceSetMessage = useDebounce(DEBOUNCE_MESSAGE, true);
|
|
||||||
const debounceSwipeDirection = useDebounce(DEBOUNCE_SWIPE, true);
|
|
||||||
const debounceActive = useDebounce(DEBOUNCE_ACTIVE, true);
|
|
||||||
|
|
||||||
const handleToggleFooterVisibility = useCallback((e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
|
const handleToggleFooterVisibility = useCallback((e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
|
||||||
if (!IS_TOUCH_ENV) return;
|
if (!IS_TOUCH_ENV) return;
|
||||||
@ -156,10 +156,8 @@ const MediaViewerSlides: FC<OwnProps> = ({
|
|||||||
transformRef.current.x += offset;
|
transformRef.current.x += offset;
|
||||||
isActiveRef.current = false;
|
isActiveRef.current = false;
|
||||||
setActiveMessageId(mId);
|
setActiveMessageId(mId);
|
||||||
debounceSetMessage(() => selectMessage(mId));
|
selectMessageDebounced(mId);
|
||||||
debounceActive(() => {
|
setIsActiveDebounced(true);
|
||||||
setIsActive(true);
|
|
||||||
});
|
|
||||||
lastTransform = { x: 0, y: 0, scale: 1 };
|
lastTransform = { x: 0, y: 0, scale: 1 };
|
||||||
cancelAnimation = animateNumber({
|
cancelAnimation = animateNumber({
|
||||||
from: transformRef.current.x,
|
from: transformRef.current.x,
|
||||||
@ -351,12 +349,8 @@ const MediaViewerSlides: FC<OwnProps> = ({
|
|||||||
y,
|
y,
|
||||||
} = transformRef.current;
|
} = transformRef.current;
|
||||||
|
|
||||||
debounceSwipeDirection(() => {
|
clearSwipeDirectionDebounced();
|
||||||
swipeDirectionRef.current = undefined;
|
setIsActiveDebounced(true);
|
||||||
});
|
|
||||||
debounceActive(() => {
|
|
||||||
setIsActive(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
// If scale is less than 1 we need to bounce back
|
// If scale is less than 1 we need to bounce back
|
||||||
if (scale < 1) {
|
if (scale < 1) {
|
||||||
@ -476,7 +470,7 @@ const MediaViewerSlides: FC<OwnProps> = ({
|
|||||||
// We shift everything by one screen width and then set new active message id
|
// We shift everything by one screen width and then set new active message id
|
||||||
transformRef.current.x += offset;
|
transformRef.current.x += offset;
|
||||||
setActiveMessageId(mId);
|
setActiveMessageId(mId);
|
||||||
debounceSetMessage(() => selectMessage(mId));
|
selectMessageDebounced(mId);
|
||||||
}
|
}
|
||||||
// Then we always return to the original position
|
// Then we always return to the original position
|
||||||
cancelAnimation = animateNumber({
|
cancelAnimation = animateNumber({
|
||||||
@ -493,14 +487,15 @@ const MediaViewerSlides: FC<OwnProps> = ({
|
|||||||
return undefined;
|
return undefined;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [
|
}, [
|
||||||
isZoomed,
|
isZoomed,
|
||||||
onClose,
|
onClose,
|
||||||
setTransform,
|
setTransform,
|
||||||
getMessageId,
|
getMessageId,
|
||||||
activeMessageId,
|
activeMessageId,
|
||||||
setIsActive,
|
selectMessageDebounced,
|
||||||
|
setIsActiveDebounced,
|
||||||
|
clearSwipeDirectionDebounced,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (!activeMessageId) return undefined;
|
if (!activeMessageId) return undefined;
|
||||||
|
|||||||
@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
/* stylelint-disable-next-line plugin/no-low-performance-animation-properties */
|
/* stylelint-disable-next-line plugin/no-low-performance-animation-properties */
|
||||||
transition: bottom 150ms ease-out, transform var(--layer-transition);
|
transition: bottom 150ms ease-out, transform var(--layer-transition);
|
||||||
|
|
||||||
body.keyboard-visible & {
|
body.keyboard-visible & {
|
||||||
position: relative;
|
position: relative;
|
||||||
bottom: calc(0px - env(safe-area-inset-bottom));
|
bottom: calc(0px - env(safe-area-inset-bottom));
|
||||||
@ -95,6 +96,7 @@
|
|||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
&.with-bottom-shift {
|
&.with-bottom-shift {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
|
||||||
.last-in-list {
|
.last-in-list {
|
||||||
margin-bottom: 4.25rem;
|
margin-bottom: 4.25rem;
|
||||||
|
|
||||||
@ -317,9 +319,7 @@
|
|||||||
transform: translate3d(calc(var(--right-column-width) / -2), 0, 0);
|
transform: translate3d(calc(var(--right-column-width) / -2), 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
body:not(.animating-right-column) #Main.right-column-open &.select-mode-active,
|
#Main.narrow-message-list & {
|
||||||
#Main.right-column-open &:not(.select-mode-active),
|
|
||||||
body.animating-right-column &:not(.select-mode-active) {
|
|
||||||
width: calc(100% - var(--right-column-width));
|
width: calc(100% - var(--right-column-width));
|
||||||
|
|
||||||
.messages-container {
|
.messages-container {
|
||||||
|
|||||||
@ -185,7 +185,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// @optimization
|
// @optimization
|
||||||
@include while-transition() {
|
#Main.right-column-animating & {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,7 +31,7 @@
|
|||||||
transition: none;
|
transition: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:not(.middle-column-open) {
|
&.left-column-open {
|
||||||
transform: translate3d(100vw, 0, 0) !important;
|
transform: translate3d(100vw, 0, 0) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -211,7 +211,7 @@ const SymbolMenu: FC<OwnProps & StateProps> = ({
|
|||||||
const className = buildClassName(
|
const className = buildClassName(
|
||||||
'SymbolMenu mobile-menu',
|
'SymbolMenu mobile-menu',
|
||||||
transitionClassNames,
|
transitionClassNames,
|
||||||
!isLeftColumnShown && 'middle-column-open',
|
isLeftColumnShown && 'left-column-open',
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { useCallback } from '../../../lib/teact/teact';
|
import { useCallback } from '../../../lib/teact/teact';
|
||||||
|
|
||||||
import { fastRaf } from '../../../util/schedulers';
|
import { fastRaf } from '../../../util/schedulers';
|
||||||
import useDebounce from '../../../hooks/useDebounce';
|
import useRunDebounced from '../../../hooks/useRunDebounced';
|
||||||
import useFlag from '../../../hooks/useFlag';
|
import useFlag from '../../../hooks/useFlag';
|
||||||
|
|
||||||
const DEBOUNCE = 1000;
|
const DEBOUNCE = 1000;
|
||||||
@ -13,7 +13,7 @@ export default function useStickyDates() {
|
|||||||
// so we will add `position: sticky` only after first scroll. There would be no animation on the first show though.
|
// so we will add `position: sticky` only after first scroll. There would be no animation on the first show though.
|
||||||
const [isScrolled, markIsScrolled] = useFlag(false);
|
const [isScrolled, markIsScrolled] = useFlag(false);
|
||||||
|
|
||||||
const runDebounced = useDebounce(DEBOUNCE, true);
|
const runDebounced = useRunDebounced(DEBOUNCE, true);
|
||||||
|
|
||||||
const updateStickyDates = useCallback((container: HTMLDivElement, hasTools?: boolean) => {
|
const updateStickyDates = useCallback((container: HTMLDivElement, hasTools?: boolean) => {
|
||||||
markIsScrolled();
|
markIsScrolled();
|
||||||
|
|||||||
@ -114,8 +114,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#Main.right-column-open &,
|
#Main.right-column-shown & {
|
||||||
body.animating-right-column & {
|
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import {
|
|||||||
ManagementScreens, NewChatMembersProgress, ProfileState, RightColumnContent,
|
ManagementScreens, NewChatMembersProgress, ProfileState, RightColumnContent,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
|
|
||||||
import { MIN_SCREEN_WIDTH_FOR_STATIC_RIGHT_COLUMN } from '../../config';
|
import { ANIMATION_END_DELAY, MIN_SCREEN_WIDTH_FOR_STATIC_RIGHT_COLUMN } from '../../config';
|
||||||
import captureEscKeyListener from '../../util/captureEscKeyListener';
|
import captureEscKeyListener from '../../util/captureEscKeyListener';
|
||||||
import {
|
import {
|
||||||
selectAreActiveChatsLoaded,
|
selectAreActiveChatsLoaded,
|
||||||
@ -41,7 +41,7 @@ type StateProps = {
|
|||||||
nextManagementScreen?: ManagementScreens;
|
nextManagementScreen?: ManagementScreens;
|
||||||
};
|
};
|
||||||
|
|
||||||
const CLOSE_ANIMATION_DURATION = 300;
|
const ANIMATION_DURATION = 450 + ANIMATION_END_DELAY;
|
||||||
const MAIN_SCREENS_COUNT = Object.keys(RightColumnContent).length / 2;
|
const MAIN_SCREENS_COUNT = Object.keys(RightColumnContent).length / 2;
|
||||||
const MANAGEMENT_SCREENS_COUNT = Object.keys(ManagementScreens).length / 2;
|
const MANAGEMENT_SCREENS_COUNT = Object.keys(ManagementScreens).length / 2;
|
||||||
|
|
||||||
@ -191,7 +191,7 @@ const RightColumn: FC<StateProps> = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setShouldSkipTransition(!isOpen);
|
setShouldSkipTransition(!isOpen);
|
||||||
}, CLOSE_ANIMATION_DURATION);
|
}, ANIMATION_DURATION);
|
||||||
}, [isOpen]);
|
}, [isOpen]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -300,7 +300,6 @@ const RightColumn: FC<StateProps> = ({
|
|||||||
profileState={profileState}
|
profileState={profileState}
|
||||||
managementScreen={managementScreen}
|
managementScreen={managementScreen}
|
||||||
onClose={close}
|
onClose={close}
|
||||||
shouldSkipAnimation={shouldSkipTransition || shouldSkipHistoryAnimations}
|
|
||||||
onScreenSelect={setManagementScreen}
|
onScreenSelect={setManagementScreen}
|
||||||
/>
|
/>
|
||||||
<Transition
|
<Transition
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { getActions, withGlobal } from '../../global';
|
|||||||
import { ManagementScreens, ProfileState } from '../../types';
|
import { ManagementScreens, ProfileState } from '../../types';
|
||||||
import { ApiExportedInvite } from '../../api/types';
|
import { ApiExportedInvite } from '../../api/types';
|
||||||
|
|
||||||
|
import { ANIMATION_END_DELAY } from '../../config';
|
||||||
import { IS_SINGLE_COLUMN_LAYOUT } from '../../util/environment';
|
import { IS_SINGLE_COLUMN_LAYOUT } from '../../util/environment';
|
||||||
import { debounce } from '../../util/schedulers';
|
import { debounce } from '../../util/schedulers';
|
||||||
import buildClassName from '../../util/buildClassName';
|
import buildClassName from '../../util/buildClassName';
|
||||||
@ -43,7 +44,6 @@ type OwnProps = {
|
|||||||
isGifSearch?: boolean;
|
isGifSearch?: boolean;
|
||||||
isPollResults?: boolean;
|
isPollResults?: boolean;
|
||||||
isAddingChatMembers?: boolean;
|
isAddingChatMembers?: boolean;
|
||||||
shouldSkipAnimation?: boolean;
|
|
||||||
profileState?: ProfileState;
|
profileState?: ProfileState;
|
||||||
managementScreen?: ManagementScreens;
|
managementScreen?: ManagementScreens;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
@ -61,9 +61,10 @@ type StateProps = {
|
|||||||
gifSearchQuery?: string;
|
gifSearchQuery?: string;
|
||||||
isEditingInvite?: boolean;
|
isEditingInvite?: boolean;
|
||||||
currentInviteInfo?: ApiExportedInvite;
|
currentInviteInfo?: ApiExportedInvite;
|
||||||
|
shouldSkipHistoryAnimations?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const COLUMN_CLOSE_DELAY_MS = 300;
|
const COLUMN_ANIMATION_DURATION = 450 + ANIMATION_END_DELAY;
|
||||||
const runDebouncedForSearch = debounce((cb) => cb(), 200, false);
|
const runDebouncedForSearch = debounce((cb) => cb(), 200, false);
|
||||||
|
|
||||||
enum HeaderContent {
|
enum HeaderContent {
|
||||||
@ -121,10 +122,10 @@ const RightHeader: FC<OwnProps & StateProps> = ({
|
|||||||
messageSearchQuery,
|
messageSearchQuery,
|
||||||
stickerSearchQuery,
|
stickerSearchQuery,
|
||||||
gifSearchQuery,
|
gifSearchQuery,
|
||||||
shouldSkipAnimation,
|
|
||||||
isEditingInvite,
|
isEditingInvite,
|
||||||
canViewStatistics,
|
canViewStatistics,
|
||||||
currentInviteInfo,
|
currentInviteInfo,
|
||||||
|
shouldSkipHistoryAnimations,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
setLocalTextSearchQuery,
|
setLocalTextSearchQuery,
|
||||||
@ -179,7 +180,7 @@ const RightHeader: FC<OwnProps & StateProps> = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setShouldSkipTransition(!isColumnOpen);
|
setShouldSkipTransition(!isColumnOpen);
|
||||||
}, COLUMN_CLOSE_DELAY_MS);
|
}, COLUMN_ANIMATION_DURATION);
|
||||||
}, [isColumnOpen]);
|
}, [isColumnOpen]);
|
||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
@ -436,7 +437,7 @@ const RightHeader: FC<OwnProps & StateProps> = ({
|
|||||||
const buttonClassName = buildClassName(
|
const buttonClassName = buildClassName(
|
||||||
'animated-close-icon',
|
'animated-close-icon',
|
||||||
isBackButton && 'state-back',
|
isBackButton && 'state-back',
|
||||||
(shouldSkipTransition || shouldSkipAnimation) && 'no-transition',
|
(shouldSkipTransition || shouldSkipHistoryAnimations) && 'no-transition',
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -452,7 +453,7 @@ const RightHeader: FC<OwnProps & StateProps> = ({
|
|||||||
<div ref={backButtonRef} className={buttonClassName} />
|
<div ref={backButtonRef} className={buttonClassName} />
|
||||||
</Button>
|
</Button>
|
||||||
<Transition
|
<Transition
|
||||||
name={(shouldSkipTransition || shouldSkipAnimation) ? 'none' : 'slide-fade'}
|
name={(shouldSkipTransition || shouldSkipHistoryAnimations) ? 'none' : 'slide-fade'}
|
||||||
activeKey={renderingContentKey}
|
activeKey={renderingContentKey}
|
||||||
>
|
>
|
||||||
{renderHeaderContent()}
|
{renderHeaderContent()}
|
||||||
@ -495,6 +496,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
gifSearchQuery,
|
gifSearchQuery,
|
||||||
isEditingInvite,
|
isEditingInvite,
|
||||||
currentInviteInfo,
|
currentInviteInfo,
|
||||||
|
shouldSkipHistoryAnimations: global.shouldSkipHistoryAnimations,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
)(RightHeader));
|
)(RightHeader));
|
||||||
|
|||||||
@ -1,9 +0,0 @@
|
|||||||
import { useMemo } from '../lib/teact/teact';
|
|
||||||
|
|
||||||
import { debounce } from '../util/schedulers';
|
|
||||||
|
|
||||||
export default function useDebounce(ms: number, noFirst?: boolean, noLast?: boolean) {
|
|
||||||
return useMemo(() => {
|
|
||||||
return debounce((cb) => cb(), ms, !noFirst, !noLast);
|
|
||||||
}, [ms, noFirst, noLast]);
|
|
||||||
}
|
|
||||||
18
src/hooks/useDebouncedCallback.ts
Normal file
18
src/hooks/useDebouncedCallback.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { useCallback, useMemo } from '../lib/teact/teact';
|
||||||
|
|
||||||
|
import { debounce } from '../util/schedulers';
|
||||||
|
|
||||||
|
export default function useDebouncedCallback<T extends AnyToVoidFunction>(
|
||||||
|
fn: T,
|
||||||
|
deps: any[],
|
||||||
|
ms: number,
|
||||||
|
noFirst?: boolean,
|
||||||
|
noLast?: boolean,
|
||||||
|
) {
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
const fnMemo = useCallback(fn, deps);
|
||||||
|
|
||||||
|
return useMemo(() => {
|
||||||
|
return debounce(fnMemo, ms, !noFirst, !noLast);
|
||||||
|
}, [fnMemo, ms, noFirst, noLast]);
|
||||||
|
}
|
||||||
@ -1,21 +1,20 @@
|
|||||||
import { useState } from '../lib/teact/teact';
|
import { useCallback, useRef, useState } from '../lib/teact/teact';
|
||||||
|
|
||||||
import useDebounce from './useDebounce';
|
import useRunDebounced from './useRunDebounced';
|
||||||
import useOnChange from './useOnChange';
|
import useOnChange from './useOnChange';
|
||||||
import useHeavyAnimationCheck from './useHeavyAnimationCheck';
|
import useHeavyAnimationCheck, { isHeavyAnimating } from './useHeavyAnimationCheck';
|
||||||
import useFlag from './useFlag';
|
import useForceUpdate from './useForceUpdate';
|
||||||
|
|
||||||
export default function useDebouncedMemo<R extends any, D extends any[]>(
|
export default function useDebouncedMemo<R extends any, D extends any[]>(
|
||||||
resolverFn: () => R, ms: number, dependencies: D,
|
resolverFn: () => R, ms: number, dependencies: D,
|
||||||
): R | undefined {
|
): R | undefined {
|
||||||
const runDebounced = useDebounce(ms, true);
|
|
||||||
const [value, setValue] = useState<R>();
|
const [value, setValue] = useState<R>();
|
||||||
const [isFrozen, freeze, unfreeze] = useFlag();
|
const { isFrozen, updateWhenUnfrozen } = useHeavyAnimationFreeze();
|
||||||
|
const runDebounced = useRunDebounced(ms, true);
|
||||||
useHeavyAnimationCheck(freeze, unfreeze);
|
|
||||||
|
|
||||||
useOnChange(() => {
|
useOnChange(() => {
|
||||||
if (isFrozen) {
|
if (isFrozen) {
|
||||||
|
updateWhenUnfrozen();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,3 +25,30 @@ export default function useDebouncedMemo<R extends any, D extends any[]>(
|
|||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function useHeavyAnimationFreeze() {
|
||||||
|
const isPending = useRef(false);
|
||||||
|
|
||||||
|
const updateWhenUnfrozen = useCallback(() => {
|
||||||
|
isPending.current = true;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const forceUpdate = useForceUpdate();
|
||||||
|
const handleUnfreeze = useCallback(() => {
|
||||||
|
if (!isPending.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isPending.current = false;
|
||||||
|
forceUpdate();
|
||||||
|
}, [forceUpdate]);
|
||||||
|
useHeavyAnimationCheck(noop, handleUnfreeze);
|
||||||
|
|
||||||
|
return {
|
||||||
|
isFrozen: isHeavyAnimating(),
|
||||||
|
updateWhenUnfrozen,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function noop() {
|
||||||
|
}
|
||||||
|
|||||||
7
src/hooks/useRunDebounced.ts
Normal file
7
src/hooks/useRunDebounced.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import useDebouncedCallback from './useDebouncedCallback';
|
||||||
|
|
||||||
|
export default function useRunDebounced(ms: number, noFirst?: boolean, noLast?: boolean) {
|
||||||
|
return useDebouncedCallback((cb: NoneToVoidFunction) => {
|
||||||
|
cb();
|
||||||
|
}, [], ms, noFirst, noLast);
|
||||||
|
}
|
||||||
7
src/hooks/useRunThrottled.ts
Normal file
7
src/hooks/useRunThrottled.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import useThrottledCallback from './useThrottledCallback';
|
||||||
|
|
||||||
|
export default function useRunThrottled(ms: number, noFirst?: boolean) {
|
||||||
|
return useThrottledCallback((cb: NoneToVoidFunction) => {
|
||||||
|
cb();
|
||||||
|
}, [], ms, noFirst);
|
||||||
|
}
|
||||||
@ -60,6 +60,8 @@ const useShowTransition = (
|
|||||||
return {
|
return {
|
||||||
shouldRender,
|
shouldRender,
|
||||||
transitionClassNames,
|
transitionClassNames,
|
||||||
|
hasShownClass: shouldRender,
|
||||||
|
hasOpenClass: shouldHaveOpenClassName,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,11 +0,0 @@
|
|||||||
import { useMemo } from '../lib/teact/teact';
|
|
||||||
|
|
||||||
import { throttle } from '../util/schedulers';
|
|
||||||
|
|
||||||
const useThrottle = (ms: number, noFirst = false) => {
|
|
||||||
return useMemo(() => {
|
|
||||||
return throttle((cb) => cb(), ms, !noFirst);
|
|
||||||
}, [ms, noFirst]);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default useThrottle;
|
|
||||||
17
src/hooks/useThrottledCallback.ts
Normal file
17
src/hooks/useThrottledCallback.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { useCallback, useMemo } from '../lib/teact/teact';
|
||||||
|
|
||||||
|
import { throttle } from '../util/schedulers';
|
||||||
|
|
||||||
|
export default function useThrottledCallback<T extends AnyToVoidFunction>(
|
||||||
|
fn: T,
|
||||||
|
deps: any[],
|
||||||
|
ms: number,
|
||||||
|
noFirst?: boolean,
|
||||||
|
) {
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
const fnMemo = useCallback(fn, deps);
|
||||||
|
|
||||||
|
return useMemo(() => {
|
||||||
|
return throttle(fnMemo, ms, !noFirst);
|
||||||
|
}, [fnMemo, ms, noFirst]);
|
||||||
|
}
|
||||||
@ -1,28 +0,0 @@
|
|||||||
import { useState } from '../lib/teact/teact';
|
|
||||||
|
|
||||||
import useThrottle from './useThrottle';
|
|
||||||
import useOnChange from './useOnChange';
|
|
||||||
import useHeavyAnimationCheck from './useHeavyAnimationCheck';
|
|
||||||
import useFlag from './useFlag';
|
|
||||||
|
|
||||||
export default function useThrottledMemo<R extends any, D extends any[]>(
|
|
||||||
resolverFn: () => R, ms: number, dependencies: D,
|
|
||||||
): R | undefined {
|
|
||||||
const runThrottled = useThrottle(ms, true);
|
|
||||||
const [value, setValue] = useState<R>();
|
|
||||||
const [isFrozen, freeze, unfreeze] = useFlag();
|
|
||||||
|
|
||||||
useHeavyAnimationCheck(freeze, unfreeze);
|
|
||||||
|
|
||||||
useOnChange(() => {
|
|
||||||
if (isFrozen) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
runThrottled(() => {
|
|
||||||
setValue(resolverFn());
|
|
||||||
});
|
|
||||||
}, [...dependencies, isFrozen]);
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
@ -464,9 +464,7 @@ export function setTarget($element: VirtualElement, target: Node) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useState<T>(): [T, StateHookSetter<T>];
|
export function useState<T>(initial?: T, debugKey?: string): [T, StateHookSetter<T>] {
|
||||||
export function useState<T>(initial: T): [T, StateHookSetter<T>];
|
|
||||||
export function useState<T>(initial?: T): [T, StateHookSetter<T>] {
|
|
||||||
const { cursor, byCursor } = renderingInstance.hooks.state;
|
const { cursor, byCursor } = renderingInstance.hooks.state;
|
||||||
|
|
||||||
if (byCursor[cursor] === undefined) {
|
if (byCursor[cursor] === undefined) {
|
||||||
@ -501,7 +499,9 @@ export function useState<T>(initial?: T): [T, StateHookSetter<T>] {
|
|||||||
componentInstance.Component && (componentInstance.Component as FC_withDebug).DEBUG_contentComponentName
|
componentInstance.Component && (componentInstance.Component as FC_withDebug).DEBUG_contentComponentName
|
||||||
? `> ${(componentInstance.Component as FC_withDebug).DEBUG_contentComponentName}`
|
? `> ${(componentInstance.Component as FC_withDebug).DEBUG_contentComponentName}`
|
||||||
: '',
|
: '',
|
||||||
`Forced update at cursor #${cursor}, next value: `,
|
debugKey
|
||||||
|
? `State update for ${debugKey}, next value: `
|
||||||
|
: `State update at cursor #${cursor}, next value: `,
|
||||||
byCursor[cursor].nextValue,
|
byCursor[cursor].nextValue,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// @optimization
|
// @optimization
|
||||||
@mixin while-transition() {
|
@mixin while-transition() {
|
||||||
.Transition > div:not(.Transition__slide--active) &, body.animating-right-column & {
|
.Transition > div:not(.Transition__slide--active) & {
|
||||||
@content;
|
@content;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
export default function findInViewport(
|
export default function findInViewport(
|
||||||
container: HTMLElement,
|
container: HTMLElement,
|
||||||
selectorOrElements: string | NodeListOf<HTMLElement>,
|
selectorOrElements: string | NodeListOf<HTMLElement> | HTMLElement[],
|
||||||
margin = 0,
|
margin = 0,
|
||||||
isDense = false,
|
isDense = false,
|
||||||
shouldContainBottom = false,
|
shouldContainBottom = false,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user