[Perf] Composer: Optimize message typing
This commit is contained in:
parent
fbda8e8094
commit
fc0365d5b9
@ -59,7 +59,7 @@ const SettingsNotifications: FC<OwnProps & StateProps> = ({
|
||||
loadNotificationSettings();
|
||||
}, [loadNotificationSettings]);
|
||||
|
||||
const runDebounced = useDebounce(500, false, true);
|
||||
const runDebounced = useDebounce(500, true);
|
||||
|
||||
const handleSettingsChange = useCallback((
|
||||
e: ChangeEvent<HTMLInputElement>,
|
||||
|
||||
@ -104,9 +104,9 @@ const MediaViewerSlides: FC<OwnProps> = ({
|
||||
forceUpdate();
|
||||
}, [forceUpdate]);
|
||||
|
||||
const debounceSetMessage = useDebounce(DEBOUNCE_MESSAGE, false);
|
||||
const debounceSwipe = useDebounce(DEBOUNCE_SWIPE, false);
|
||||
const debounceActive = useDebounce(DEBOUNCE_ACTIVE, false);
|
||||
const debounceSetMessage = useDebounce(DEBOUNCE_MESSAGE, true);
|
||||
const debounceSwipe = useDebounce(DEBOUNCE_SWIPE, true);
|
||||
const debounceActive = useDebounce(DEBOUNCE_ACTIVE, true);
|
||||
|
||||
const handleToggleFooterVisibility = useCallback(() => {
|
||||
if (IS_TOUCH_ENV && (isPhoto || isGif) && hasFooter) {
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
import React, {
|
||||
FC, memo, useEffect, useMemo,
|
||||
} from '../../../lib/teact/teact';
|
||||
import React, { FC, memo, useEffect } from '../../../lib/teact/teact';
|
||||
import { getDispatch, withGlobal } from '../../../lib/teact/teactn';
|
||||
|
||||
import { ApiMessage, ApiMessageEntityTypes, ApiWebPage } from '../../../api/types';
|
||||
@ -12,6 +10,7 @@ import parseMessageInput from '../../../util/parseMessageInput';
|
||||
import useOnChange from '../../../hooks/useOnChange';
|
||||
import useShowTransition from '../../../hooks/useShowTransition';
|
||||
import useCurrentOrPrev from '../../../hooks/useCurrentOrPrev';
|
||||
import useDebouncedMemo from '../../../hooks/useDebouncedMemo';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
|
||||
import WebPage from '../message/WebPage';
|
||||
@ -32,6 +31,7 @@ type StateProps = {
|
||||
theme: ISettings['theme'];
|
||||
};
|
||||
|
||||
const DEBOUNCE_MS = 300;
|
||||
const RE_LINK = new RegExp(RE_LINK_TEMPLATE, 'i');
|
||||
|
||||
const WebPagePreview: FC<OwnProps & StateProps> = ({
|
||||
@ -49,7 +49,7 @@ const WebPagePreview: FC<OwnProps & StateProps> = ({
|
||||
toggleMessageWebPage,
|
||||
} = getDispatch();
|
||||
|
||||
const link = useMemo(() => {
|
||||
const link = useDebouncedMemo(() => {
|
||||
const { text, entities } = parseMessageInput(messageText);
|
||||
|
||||
const linkEntity = entities && entities.find(({ type }) => type === ApiMessageEntityTypes.TextUrl);
|
||||
@ -63,7 +63,7 @@ const WebPagePreview: FC<OwnProps & StateProps> = ({
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}, [messageText]);
|
||||
}, DEBOUNCE_MS, [messageText]);
|
||||
|
||||
useEffect(() => {
|
||||
if (link) {
|
||||
|
||||
@ -3,25 +3,29 @@ import { getDispatch } from '../../../../lib/teact/teactn';
|
||||
import { InlineBotSettings } from '../../../../types';
|
||||
import useFlag from '../../../../hooks/useFlag';
|
||||
import usePrevious from '../../../../hooks/usePrevious';
|
||||
import useDebouncedMemo from '../../../../hooks/useDebouncedMemo';
|
||||
|
||||
const tempEl = document.createElement('div');
|
||||
const DEBOUNCE_MS = 300;
|
||||
const INLINE_BOT_QUERY_REGEXP = /^@([a-z0-9_]{1,32})[\u00A0\u0020]+(.*)/i;
|
||||
const HAS_NEW_LINE = /^@([a-z0-9_]{1,32})[\u00A0\u0020]+\n{2,}/i;
|
||||
|
||||
const tempEl = document.createElement('div');
|
||||
|
||||
export default function useInlineBotTooltip(
|
||||
isAllowed: boolean,
|
||||
chatId: string,
|
||||
html: string,
|
||||
inlineBots?: Record<string, false | InlineBotSettings>,
|
||||
) {
|
||||
const [isOpen, markIsOpen, unmarkIsOpen] = useFlag();
|
||||
const text = getPlainText(html);
|
||||
const { queryInlineBot, resetInlineBot } = getDispatch();
|
||||
const { username, query, canShowHelp } = parseStartWithUsernameString(text);
|
||||
const usernameLowered = username.toLowerCase();
|
||||
|
||||
const [isOpen, markIsOpen, unmarkIsOpen] = useFlag();
|
||||
const {
|
||||
username, query, canShowHelp, usernameLowered,
|
||||
} = useDebouncedMemo(() => parseBotQuery(html), DEBOUNCE_MS, [html]) || {};
|
||||
const prevQuery = usePrevious(query);
|
||||
const prevUsername = usePrevious(username);
|
||||
const inlineBotData = inlineBots?.[usernameLowered];
|
||||
const inlineBotData = usernameLowered ? inlineBots?.[usernameLowered] : undefined;
|
||||
const {
|
||||
id: botId,
|
||||
switchPm,
|
||||
@ -63,14 +67,33 @@ export default function useInlineBotTooltip(
|
||||
|
||||
return {
|
||||
isOpen,
|
||||
closeTooltip: unmarkIsOpen,
|
||||
loadMore,
|
||||
username,
|
||||
id: botId,
|
||||
isGallery,
|
||||
switchPm,
|
||||
results,
|
||||
closeTooltip: unmarkIsOpen,
|
||||
help: canShowHelp && help ? `@${username} ${help}` : undefined,
|
||||
loadMore,
|
||||
};
|
||||
}
|
||||
|
||||
function parseBotQuery(html: string) {
|
||||
const text = getPlainText(html);
|
||||
const result = text.match(INLINE_BOT_QUERY_REGEXP);
|
||||
if (!result) {
|
||||
return {
|
||||
username: '',
|
||||
query: '',
|
||||
canShowHelp: false,
|
||||
usernameLowered: '',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
username: result[1],
|
||||
query: result[2],
|
||||
canShowHelp: result[2] === '' && !text.match(HAS_NEW_LINE),
|
||||
usernameLowered: result[1].toLowerCase(),
|
||||
};
|
||||
}
|
||||
|
||||
@ -79,16 +102,3 @@ function getPlainText(html: string) {
|
||||
|
||||
return tempEl.innerText;
|
||||
}
|
||||
|
||||
function parseStartWithUsernameString(text: string) {
|
||||
const result = text.match(INLINE_BOT_QUERY_REGEXP);
|
||||
if (!result) {
|
||||
return { username: '', query: '', canShowHelp: false };
|
||||
}
|
||||
|
||||
return {
|
||||
username: result[1],
|
||||
query: result[2],
|
||||
canShowHelp: result[2] === '' && !text.match(HAS_NEW_LINE),
|
||||
};
|
||||
}
|
||||
|
||||
@ -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.
|
||||
const [isScrolled, markIsScrolled] = useFlag(false);
|
||||
|
||||
const runDebounced = useDebounce(DEBOUNCE, false);
|
||||
const runDebounced = useDebounce(DEBOUNCE, true);
|
||||
|
||||
const updateStickyDates = useCallback((container: HTMLDivElement, hasTools?: boolean) => {
|
||||
markIsScrolled();
|
||||
|
||||
@ -2,8 +2,8 @@ import { useMemo } from '../lib/teact/teact';
|
||||
|
||||
import { debounce } from '../util/schedulers';
|
||||
|
||||
export default function useDebounce(ms: number, shouldRunFirst?: boolean, shouldRunLast?: boolean) {
|
||||
export default function useDebounce(ms: number, noFirst?: boolean, noLast?: boolean) {
|
||||
return useMemo(() => {
|
||||
return debounce((cb) => cb(), ms, shouldRunFirst, shouldRunLast);
|
||||
}, [ms, shouldRunFirst, shouldRunLast]);
|
||||
return debounce((cb) => cb(), ms, !noFirst, !noLast);
|
||||
}, [ms, noFirst, noLast]);
|
||||
}
|
||||
|
||||
28
src/hooks/useDebouncedMemo.ts
Normal file
28
src/hooks/useDebouncedMemo.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { useState } from '../lib/teact/teact';
|
||||
|
||||
import useDebounce from './useDebounce';
|
||||
import useOnChange from './useOnChange';
|
||||
import useHeavyAnimationCheck from './useHeavyAnimationCheck';
|
||||
import useFlag from './useFlag';
|
||||
|
||||
export default function useDebouncedMemo<R extends any, D extends any[]>(
|
||||
resolverFn: () => R, ms: number, dependencies: D,
|
||||
): R | undefined {
|
||||
const runDebounced = useDebounce(ms, true);
|
||||
const [value, setValue] = useState<R>();
|
||||
const [isFrozen, freeze, unfreeze] = useFlag();
|
||||
|
||||
useHeavyAnimationCheck(freeze, unfreeze);
|
||||
|
||||
useOnChange(() => {
|
||||
if (isFrozen) {
|
||||
return;
|
||||
}
|
||||
|
||||
runDebounced(() => {
|
||||
setValue(resolverFn());
|
||||
});
|
||||
}, [...dependencies, isFrozen]);
|
||||
|
||||
return value;
|
||||
}
|
||||
@ -5,7 +5,9 @@ import useOnChange from './useOnChange';
|
||||
import useHeavyAnimationCheck from './useHeavyAnimationCheck';
|
||||
import useFlag from './useFlag';
|
||||
|
||||
export default <R extends any, D extends any[]>(resolverFn: () => R, ms: number, dependencies: D) => {
|
||||
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();
|
||||
@ -20,7 +22,7 @@ export default <R extends any, D extends any[]>(resolverFn: () => R, ms: number,
|
||||
runThrottled(() => {
|
||||
setValue(resolverFn());
|
||||
});
|
||||
}, dependencies.concat([isFrozen]));
|
||||
}, [...dependencies, isFrozen]);
|
||||
|
||||
return value;
|
||||
};
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user