Timeline Perf Improvement (#1521)

* emojify msg txt find&replace instead of recursion

* move findAndReplace func in its own file

* improve find and replace

* move markdown file to plugins

* make find and replace work without g flag regex

* fix pagination stop on msg arrive

* render blurhash in small size
This commit is contained in:
Ajay Bura 2023-10-30 16:58:47 +11:00 committed by GitHub
parent 3713125f57
commit c854c7f9d2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 65 additions and 30 deletions

View file

@ -3,7 +3,8 @@ import { Descendant, Text } from 'slate';
import { sanitizeText } from '../../utils/sanitize';
import { BlockType } from './types';
import { CustomElement } from './slate';
import { parseBlockMD, parseInlineMD, replaceMatch } from '../../utils/markdown';
import { parseBlockMD, parseInlineMD } from '../../plugins/markdown';
import { findAndReplace } from '../../utils/findAndReplace';
export type OutputOptions = {
allowTextFormatting?: boolean;
@ -69,14 +70,14 @@ const elementToCustomHtml = (node: CustomElement, children: string): string => {
}
};
const HTML_TAG_REG = /<([\w-]+)(?: [^>]*)?(?:(?:\/>)|(?:>.*?<\/\1>))/;
const ignoreHTMLParseInlineMD = (text: string): string => {
if (text === '') return text;
const match = text.match(HTML_TAG_REG);
if (!match) return parseInlineMD(text);
const [matchedTxt] = match;
return replaceMatch((txt) => [ignoreHTMLParseInlineMD(txt)], text, match, matchedTxt).join('');
};
const HTML_TAG_REG_G = /<([\w-]+)(?: [^>]*)?(?:(?:\/>)|(?:>.*?<\/\1>))/g;
const ignoreHTMLParseInlineMD = (text: string): string =>
findAndReplace(
text,
HTML_TAG_REG_G,
(match) => match[0],
(txt) => parseInlineMD(txt)
).join('');
export const toMatrixCustomHTML = (
node: Descendant | Descendant[],

View file

@ -345,7 +345,6 @@ const useTimelinePagination = (
return async (backwards: boolean) => {
if (fetching) return;
const targetTimeline = timelineRef.current;
const { linkedTimelines: lTimelines } = timelineRef.current;
const timelinesEventsCount = lTimelines.map(timelineToEventsCount);
@ -385,7 +384,6 @@ const useTimelinePagination = (
}
fetching = false;
if (targetTimeline !== timelineRef.current) return;
if (alive()) {
recalibratePagination(lTimelines, timelinesEventsCount, backwards);
}

View file

@ -98,7 +98,13 @@ export const ImageContent = as<'div', ImageContentProps>(
</Overlay>
)}
{typeof blurHash === 'string' && !load && (
<BlurhashCanvas style={{ width: '100%', height: '100%' }} hash={blurHash} punch={1} />
<BlurhashCanvas
style={{ width: '100%', height: '100%' }}
width={32}
height={32}
hash={blurHash}
punch={1}
/>
)}
{!autoPlay && srcState.status === AsyncStatus.Idle && (
<Box className={css.AbsoluteContainer} alignItems="Center" justifyContent="Center">

View file

@ -88,7 +88,13 @@ export const VideoContent = as<'div', VideoContentProps>(
return (
<Box className={classNames(css.RelativeBase, className)} {...props} ref={ref}>
{typeof blurHash === 'string' && !load && (
<BlurhashCanvas style={{ width: '100%', height: '100%' }} hash={blurHash} punch={1} />
<BlurhashCanvas
style={{ width: '100%', height: '100%' }}
width={32}
height={32}
hash={blurHash}
punch={1}
/>
)}
{thumbSrcState.status === AsyncStatus.Success && !load && (
<Box className={css.AbsoluteContainer} alignItems="Center" justifyContent="Center">

View file

@ -18,11 +18,11 @@ import { getMxIdLocalPart, getRoomWithCanonicalAlias } from '../utils/matrix';
import { getMemberDisplayName } from '../utils/room';
import { EMOJI_PATTERN, URL_NEG_LB } from '../utils/regex';
import { getHexcodeForEmoji, getShortcodeFor } from './emoji';
import { replaceMatch } from '../utils/markdown';
import { findAndReplace } from '../utils/findAndReplace';
const ReactPrism = lazy(() => import('./react-prism/ReactPrism'));
const EMOJI_REG = new RegExp(`${URL_NEG_LB}(${EMOJI_PATTERN})`);
const EMOJI_REG_G = new RegExp(`${URL_NEG_LB}(${EMOJI_PATTERN})`, 'g');
export const LINKIFY_OPTS: LinkifyOpts = {
attributes: {
@ -35,26 +35,22 @@ export const LINKIFY_OPTS: LinkifyOpts = {
ignoreTags: ['span'],
};
const stringToEmojifyJSX = (text: string): (string | JSX.Element)[] => {
const match = text.match(EMOJI_REG);
if (!match) return [text];
const [emoji] = match;
return replaceMatch(
stringToEmojifyJSX,
const textToEmojifyJSX = (text: string): (string | JSX.Element)[] =>
findAndReplace(
text,
match,
<span className={css.EmoticonBase}>
<span className={css.Emoticon()} title={getShortcodeFor(getHexcodeForEmoji(emoji))}>
{emoji}
EMOJI_REG_G,
(match, pushIndex) => (
<span key={pushIndex} className={css.EmoticonBase}>
<span className={css.Emoticon()} title={getShortcodeFor(getHexcodeForEmoji(match[0]))}>
{match[0]}
</span>
</span>
</span>
),
(txt) => txt
);
};
export const emojifyAndLinkify = (text: string, linkify?: boolean) => {
const emojifyJSX = stringToEmojifyJSX(text);
const emojifyJSX = textToEmojifyJSX(text);
if (linkify) {
return <Linkify options={LINKIFY_OPTS}>{emojifyJSX}</Linkify>;

View file

@ -0,0 +1,28 @@
export type ReplaceCallback<R> = (
match: RegExpExecArray | RegExpMatchArray,
pushIndex: number
) => R;
export type ConvertPartCallback<R> = (text: string, pushIndex: number) => R;
export const findAndReplace = <ReplaceReturnType, ConvertReturnType>(
text: string,
regex: RegExp,
replace: ReplaceCallback<ReplaceReturnType>,
convertPart: ConvertPartCallback<ConvertReturnType>
): Array<ReplaceReturnType | ConvertReturnType> => {
const result: Array<ReplaceReturnType | ConvertReturnType> = [];
let lastEnd = 0;
let match: RegExpExecArray | RegExpMatchArray | null = regex.exec(text);
while (match !== null && typeof match.index === 'number') {
result.push(convertPart(text.slice(lastEnd, match.index), result.length));
result.push(replace(match, result.length));
lastEnd = match.index + match[0].length;
if (regex.global) match = regex.exec(text);
}
result.push(convertPart(text.slice(lastEnd), result.length));
return result;
};