Skip to content

Commit

Permalink
Timeline Perf Improvement (#1521)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
ajbura authored Oct 30, 2023
1 parent 3713125 commit c854c7f
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 30 deletions.
19 changes: 10 additions & 9 deletions src/app/components/editor/output.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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[],
Expand Down
2 changes: 0 additions & 2 deletions src/app/organisms/room/RoomTimeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -385,7 +384,6 @@ const useTimelinePagination = (
}

fetching = false;
if (targetTimeline !== timelineRef.current) return;
if (alive()) {
recalibratePagination(lTimelines, timelinesEventsCount, backwards);
}
Expand Down
8 changes: 7 additions & 1 deletion src/app/organisms/room/message/ImageContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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">
Expand Down
8 changes: 7 additions & 1 deletion src/app/organisms/room/message/VideoContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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">
Expand Down
File renamed without changes.
30 changes: 13 additions & 17 deletions src/app/plugins/react-custom-html-parser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand All @@ -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>;
Expand Down
28 changes: 28 additions & 0 deletions src/app/utils/findAndReplace.ts
Original file line number Diff line number Diff line change
@@ -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;
};

0 comments on commit c854c7f

Please sign in to comment.