[IMPROVEMENT] Markdown perf (#1796)

This commit is contained in:
Diego Mello 2020-02-28 13:18:03 -03:00 committed by GitHub
parent 64002ba149
commit 39d9a00933
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 164 additions and 850 deletions

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@ import { themes } from '../../constants/colors';
import styles from './styles'; import styles from './styles';
const AtMention = React.memo(({ const AtMention = React.memo(({
mention, mentions, username, navToRoomInfo, preview, style = [], useRealName, theme mention, mentions, username, navToRoomInfo, style = [], useRealName, theme
}) => { }) => {
let mentionStyle = { ...styles.mention, color: themes[theme].buttonText }; let mentionStyle = { ...styles.mention, color: themes[theme].buttonText };
if (mention === 'all' || mention === 'here') { if (mention === 'all' || mention === 'here') {
@ -40,8 +40,8 @@ const AtMention = React.memo(({
if (user) { if (user) {
return ( return (
<Text <Text
style={[preview ? { ...styles.text, color: themes[theme].bodyText } : mentionStyle, ...style]} style={[mentionStyle, ...style]}
onPress={preview ? undefined : handlePress} onPress={handlePress}
> >
{useRealName ? user.name : user.username} {useRealName ? user.name : user.username}
</Text> </Text>
@ -60,7 +60,6 @@ AtMention.propTypes = {
username: PropTypes.string, username: PropTypes.string,
navToRoomInfo: PropTypes.func, navToRoomInfo: PropTypes.func,
style: PropTypes.array, style: PropTypes.array,
preview: PropTypes.bool,
useRealName: PropTypes.bool, useRealName: PropTypes.bool,
theme: PropTypes.string, theme: PropTypes.string,
mentions: PropTypes.oneOfType([PropTypes.array, PropTypes.object]) mentions: PropTypes.oneOfType([PropTypes.array, PropTypes.object])

View File

@ -7,7 +7,7 @@ import { themes } from '../../constants/colors';
import styles from './styles'; import styles from './styles';
const Hashtag = React.memo(({ const Hashtag = React.memo(({
hashtag, channels, navToRoomInfo, preview, style = [], theme hashtag, channels, navToRoomInfo, style = [], theme
}) => { }) => {
const handlePress = () => { const handlePress = () => {
const index = channels.findIndex(channel => channel.name === hashtag); const index = channels.findIndex(channel => channel.name === hashtag);
@ -21,8 +21,8 @@ const Hashtag = React.memo(({
if (channels && channels.length && channels.findIndex(channel => channel.name === hashtag) !== -1) { if (channels && channels.length && channels.findIndex(channel => channel.name === hashtag) !== -1) {
return ( return (
<Text <Text
style={[preview ? { ...styles.text, color: themes[theme].bodyText } : styles.mention, ...style]} style={[styles.mention, ...style]}
onPress={preview ? undefined : handlePress} onPress={handlePress}
> >
{hashtag} {hashtag}
</Text> </Text>
@ -39,7 +39,6 @@ Hashtag.propTypes = {
hashtag: PropTypes.string, hashtag: PropTypes.string,
navToRoomInfo: PropTypes.func, navToRoomInfo: PropTypes.func,
style: PropTypes.array, style: PropTypes.array,
preview: PropTypes.bool,
theme: PropTypes.string, theme: PropTypes.string,
channels: PropTypes.oneOfType([PropTypes.array, PropTypes.object]) channels: PropTypes.oneOfType([PropTypes.array, PropTypes.object])
}; };

View File

@ -10,7 +10,7 @@ import EventEmitter from '../../utils/events';
import I18n from '../../i18n'; import I18n from '../../i18n';
const Link = React.memo(({ const Link = React.memo(({
children, link, preview, theme children, link, theme
}) => { }) => {
const handlePress = () => { const handlePress = () => {
if (!link) { if (!link) {
@ -28,13 +28,9 @@ const Link = React.memo(({
// if you have a [](https://rocket.chat) render https://rocket.chat // if you have a [](https://rocket.chat) render https://rocket.chat
return ( return (
<Text <Text
onPress={preview ? undefined : handlePress} onPress={handlePress}
onLongPress={preview ? undefined : onLongPress} onLongPress={onLongPress}
style={ style={{ ...styles.link, color: themes[theme].actionTintColor }}
!preview
? { ...styles.link, color: themes[theme].actionTintColor }
: { color: themes[theme].bodyText }
}
> >
{ childLength !== 0 ? children : link } { childLength !== 0 ? children : link }
</Text> </Text>
@ -44,8 +40,7 @@ const Link = React.memo(({
Link.propTypes = { Link.propTypes = {
children: PropTypes.node, children: PropTypes.node,
link: PropTypes.string, link: PropTypes.string,
theme: PropTypes.string, theme: PropTypes.string
preview: PropTypes.bool
}; };
export default Link; export default Link;

View File

@ -3,6 +3,7 @@ import { Text, Image } from 'react-native';
import { Parser, Node } from 'commonmark'; import { Parser, Node } from 'commonmark';
import Renderer from 'commonmark-react-renderer'; import Renderer from 'commonmark-react-renderer';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import removeMarkdown from 'remove-markdown';
import shortnameToUnicode from '../../utils/shortnameToUnicode'; import shortnameToUnicode from '../../utils/shortnameToUnicode';
import I18n from '../../i18n'; import I18n from '../../i18n';
@ -18,6 +19,7 @@ import MarkdownEmoji from './Emoji';
import MarkdownTable from './Table'; import MarkdownTable from './Table';
import MarkdownTableRow from './TableRow'; import MarkdownTableRow from './TableRow';
import MarkdownTableCell from './TableCell'; import MarkdownTableCell from './TableCell';
import mergeTextNodes from './mergeTextNodes';
import styles from './styles'; import styles from './styles';
@ -83,10 +85,10 @@ class Markdown extends PureComponent {
constructor(props) { constructor(props) {
super(props); super(props);
this.renderer = this.createRenderer(props.preview); this.renderer = this.createRenderer();
} }
createRenderer = (preview = false) => new Renderer({ createRenderer = () => new Renderer({
renderers: { renderers: {
text: this.renderText, text: this.renderText,
@ -119,7 +121,7 @@ class Markdown extends PureComponent {
table_row: this.renderTableRow, table_row: this.renderTableRow,
table_cell: this.renderTableCell, table_cell: this.renderTableCell,
editedIndicator: preview ? () => null : this.renderEditedIndicator editedIndicator: this.renderEditedIndicator
}, },
renderParagraphsInLists: true renderParagraphsInLists: true
}); });
@ -141,19 +143,15 @@ class Markdown extends PureComponent {
renderText = ({ context, literal }) => { renderText = ({ context, literal }) => {
const { const {
numberOfLines, preview, style = [] numberOfLines, style = []
} = this.props; } = this.props;
const defaultStyle = [ const defaultStyle = [
this.isMessageContainsOnlyEmoji && !preview ? styles.textBig : {}, this.isMessageContainsOnlyEmoji ? styles.textBig : {},
...context.map(type => styles[type]) ...context.map(type => styles[type])
]; ];
return ( return (
<Text <Text
style={[ style={[styles.text, defaultStyle, ...style]}
styles.text,
!preview ? defaultStyle : {},
...style
]}
numberOfLines={numberOfLines} numberOfLines={numberOfLines}
> >
{literal} {literal}
@ -162,18 +160,16 @@ class Markdown extends PureComponent {
} }
renderCodeInline = ({ literal }) => { renderCodeInline = ({ literal }) => {
const { preview, theme, style = [] } = this.props; const { theme, style = [] } = this.props;
return ( return (
<Text <Text
style={[ style={[
!preview {
? { ...styles.codeInline,
...styles.codeInline, color: themes[theme].bodyText,
color: themes[theme].bodyText, backgroundColor: themes[theme].bannerBackground,
backgroundColor: themes[theme].bannerBackground, borderColor: themes[theme].bannerBackground
borderColor: themes[theme].bannerBackground },
}
: { ...styles.text, color: themes[theme].bodyText },
...style ...style
]} ]}
> >
@ -183,18 +179,16 @@ class Markdown extends PureComponent {
}; };
renderCodeBlock = ({ literal }) => { renderCodeBlock = ({ literal }) => {
const { preview, theme, style = [] } = this.props; const { theme, style = [] } = this.props;
return ( return (
<Text <Text
style={[ style={[
!preview {
? { ...styles.codeBlock,
...styles.codeBlock, color: themes[theme].bodyText,
color: themes[theme].bodyText, backgroundColor: themes[theme].bannerBackground,
backgroundColor: themes[theme].bannerBackground, borderColor: themes[theme].bannerBackground
borderColor: themes[theme].bannerBackground },
}
: { ...styles.text, color: themes[theme].bodyText },
...style ...style
]} ]}
> >
@ -221,11 +215,10 @@ class Markdown extends PureComponent {
}; };
renderLink = ({ children, href }) => { renderLink = ({ children, href }) => {
const { preview, theme } = this.props; const { theme } = this.props;
return ( return (
<MarkdownLink <MarkdownLink
link={href} link={href}
preview={preview}
theme={theme} theme={theme}
> >
{children} {children}
@ -235,14 +228,13 @@ class Markdown extends PureComponent {
renderHashtag = ({ hashtag }) => { renderHashtag = ({ hashtag }) => {
const { const {
channels, navToRoomInfo, style, preview, theme channels, navToRoomInfo, style, theme
} = this.props; } = this.props;
return ( return (
<MarkdownHashtag <MarkdownHashtag
hashtag={hashtag} hashtag={hashtag}
channels={channels} channels={channels}
navToRoomInfo={navToRoomInfo} navToRoomInfo={navToRoomInfo}
preview={preview}
theme={theme} theme={theme}
style={style} style={style}
/> />
@ -251,7 +243,7 @@ class Markdown extends PureComponent {
renderAtMention = ({ mentionName }) => { renderAtMention = ({ mentionName }) => {
const { const {
username, mentions, navToRoomInfo, useRealName, preview, style, theme username, mentions, navToRoomInfo, useRealName, style, theme
} = this.props; } = this.props;
return ( return (
<MarkdownAtMention <MarkdownAtMention
@ -260,7 +252,6 @@ class Markdown extends PureComponent {
useRealName={useRealName} useRealName={useRealName}
username={username} username={username}
navToRoomInfo={navToRoomInfo} navToRoomInfo={navToRoomInfo}
preview={preview}
theme={theme} theme={theme}
style={style} style={style}
/> />
@ -269,13 +260,13 @@ class Markdown extends PureComponent {
renderEmoji = ({ emojiName, literal }) => { renderEmoji = ({ emojiName, literal }) => {
const { const {
getCustomEmoji, baseUrl, customEmojis = true, preview, style, theme getCustomEmoji, baseUrl, customEmojis = true, style, theme
} = this.props; } = this.props;
return ( return (
<MarkdownEmoji <MarkdownEmoji
emojiName={emojiName} emojiName={emojiName}
literal={literal} literal={literal}
isMessageContainsOnlyEmoji={this.isMessageContainsOnlyEmoji && !preview} isMessageContainsOnlyEmoji={this.isMessageContainsOnlyEmoji}
getCustomEmoji={getCustomEmoji} getCustomEmoji={getCustomEmoji}
baseUrl={baseUrl} baseUrl={baseUrl}
customEmojis={customEmojis} customEmojis={customEmojis}
@ -336,10 +327,7 @@ class Markdown extends PureComponent {
}; };
renderBlockQuote = ({ children }) => { renderBlockQuote = ({ children }) => {
const { preview, theme } = this.props; const { theme } = this.props;
if (preview) {
return children;
}
return ( return (
<MarkdownBlockQuote theme={theme}> <MarkdownBlockQuote theme={theme}>
{children} {children}
@ -367,7 +355,9 @@ class Markdown extends PureComponent {
} }
render() { render() {
const { msg, preview = false } = this.props; const {
msg, numberOfLines, preview = false, theme, style = []
} = this.props;
if (!msg) { if (!msg) {
return null; return null;
@ -378,19 +368,22 @@ class Markdown extends PureComponent {
// Ex: '[ ](https://open.rocket.chat/group/test?msg=abcdef) Test' // Ex: '[ ](https://open.rocket.chat/group/test?msg=abcdef) Test'
// Return: 'Test' // Return: 'Test'
m = m.replace(/^\[([\s]]*)\]\(([^)]*)\)\s/, '').trim(); m = m.replace(/^\[([\s]]*)\]\(([^)]*)\)\s/, '').trim();
m = shortnameToUnicode(m);
if (preview) { if (preview) {
m = m.split('\n').reduce((lines, line) => `${ lines } ${ line }`, ''); m = m.replace(/\n+/g, ' ');
const ast = parser.parse(m); m = shortnameToUnicode(m);
return this.renderer.render(ast); m = removeMarkdown(m);
return (
<Text style={[styles.text, { color: themes[theme].bodyText }, ...style]} numberOfLines={numberOfLines}>
{m}
</Text>
);
} }
const ast = parser.parse(m); let ast = parser.parse(m);
ast = mergeTextNodes(ast);
this.isMessageContainsOnlyEmoji = isOnlyEmoji(m) && emojiCount(m) <= 3; this.isMessageContainsOnlyEmoji = isOnlyEmoji(m) && emojiCount(m) <= 3;
this.editedMessage(ast); this.editedMessage(ast);
return this.renderer.render(ast); return this.renderer.render(ast);
} }
} }

View File

@ -0,0 +1,27 @@
// TODO: should we add this to our commonmark fork instead?
// we loop through nodes and try to merge all texts
export default function mergeTextNodes(ast) {
// https://github.com/commonmark/commonmark.js/blob/master/lib/node.js#L268
const walker = ast.walker();
let event;
// eslint-disable-next-line no-cond-assign
while (event = walker.next()) {
const { entering, node } = event;
const { type } = node;
if (entering && type === 'text') {
while (node._next && node._next.type === 'text') {
const next = node._next;
node.literal += next.literal;
node._next = next._next;
if (node._next) {
node._next._prev = node;
}
if (node._parent._lastChild === next) {
node._parent._lastChild = node;
}
}
walker.resumeAt(node, false);
}
}
return ast;
}

View File

@ -6,7 +6,6 @@ import I18n from '../../i18n';
import styles from './styles'; import styles from './styles';
import Markdown from '../../containers/markdown'; import Markdown from '../../containers/markdown';
import { themes } from '../../constants/colors'; import { themes } from '../../constants/colors';
import shortnameToUnicode from '../../utils/shortnameToUnicode';
const formatMsg = ({ const formatMsg = ({
lastMessage, type, showLastMessage, username, useRealName lastMessage, type, showLastMessage, username, useRealName
@ -37,11 +36,7 @@ const formatMsg = ({
prefix = `${ useRealName ? name : lastMessage.u.username }: `; prefix = `${ useRealName ? name : lastMessage.u.username }: `;
} }
let msg = `${ prefix }${ lastMessage.msg.replace(/[\n\t\r]/igm, '') }`; return `${ prefix }${ lastMessage.msg }`;
if (msg) {
msg = shortnameToUnicode(msg);
}
return msg;
}; };
const arePropsEqual = (oldProps, newProps) => _.isEqual(oldProps, newProps); const arePropsEqual = (oldProps, newProps) => _.isEqual(oldProps, newProps);