Add new parser components

This commit is contained in:
Gerzon Z 2021-08-12 17:05:18 -04:00
parent 559f804073
commit ff9b0fec9c
18 changed files with 440 additions and 77 deletions

View File

@ -0,0 +1,18 @@
/* eslint-disable react/no-array-index-key */
import React from 'react';
import PropTypes from 'prop-types';
import Emoji from './Emoji';
const BigEmoji = ({ value }) => (
<>
{value.map((block, index) => <Emoji key={index} value={block.value.value} isBigEmoji />)}
</>
);
BigEmoji.propTypes = {
value: PropTypes.object
};
export default BigEmoji;

View File

@ -0,0 +1,36 @@
/* eslint-disable react/no-array-index-key */
import React from 'react';
import { StyleSheet, Text } from 'react-native';
import PropTypes from 'prop-types';
import Plain from './Plain';
import Strike from './Strike';
import Italic from './Italic';
const styles = StyleSheet.create({
text: {
fontWeight: 'bold'
}
});
const Bold = ({ value }) => (
<Text style={styles.text}>
{value.map((block, index) => {
switch (block.type) {
case 'PLAIN_TEXT':
return <Plain key={index} value={block.value} />;
case 'STRIKE':
return <Strike key={index} value={block.value} />;
case 'ITALIC':
return <Italic key={index} value={block.value} />;
default:
return null;
}
})}
</Text>
);
Bold.propTypes = {
value: PropTypes.string
};
export default Bold;

View File

@ -1,5 +1,6 @@
import React from 'react';
import { PropTypes, Text } from 'react-native';
import { Text } from 'react-native';
import PropTypes from 'prop-types';
import { themes } from '../../../constants/colors';
import { useTheme } from '../../../theme';
@ -11,7 +12,7 @@ const Code = ({
<Text
style={[
{
...(type === 'CODE_INLINE' ? styles.codeInline : styles.codeBlock),
...(type === 'INLINE_CODE' ? styles.codeInline : styles.codeBlock),
color: themes[theme].bodyText,
backgroundColor: themes[theme].bannerBackground,
borderColor: themes[theme].bannerBackground

View File

@ -0,0 +1,32 @@
import React from 'react';
import { Text } from 'react-native';
import PropTypes from 'prop-types';
import shortnameToUnicode from '../../../utils/shortnameToUnicode';
import { themes } from '../../../constants/colors';
import { useTheme } from '../../../theme';
import styles from '../styles';
const Emoji = ({ emojiHandle, style, isBigEmoji }) => {
const { theme } = useTheme();
const emojiUnicode = shortnameToUnicode(emojiHandle);
return (
<Text
style={[
{ color: themes[theme].bodyText },
isBigEmoji ? styles.textBig : styles.text,
style
]}
>
{emojiUnicode}
</Text>
);
};
Emoji.propTypes = {
emojiHandle: PropTypes.string,
style: PropTypes.object,
isBigEmoji: PropTypes.bool
};
export default Emoji;

View File

@ -0,0 +1,35 @@
import React from 'react';
import { Text } from 'react-native';
import PropTypes from 'prop-types';
import { themes } from '../../../constants/colors';
import styles from '../styles';
import { useTheme } from '../../../theme';
const Heading = ({ value, level }) => {
const { theme } = useTheme();
const textStyle = styles[`heading${ level }`];
return (
<>
{value.map((block) => {
switch (block.type) {
case 'PLAIN_TEXT':
return (
<Text style={[textStyle, { color: themes[theme].bodyText }]}>
{block.value}
</Text>
);
default:
return null;
}
})}
</>
);
};
Heading.propTypes = {
value: PropTypes.string,
level: PropTypes.number
};
export default Heading;

View File

@ -0,0 +1,48 @@
import React from 'react';
import { Text } from 'react-native';
import PropTypes from 'prop-types';
import Link from './Link';
import Plain from './Plain';
import Code from './Code';
import Bold from './Bold';
import Strike from './Strike';
import Italic from './Italic';
import Emoji from './Emoji';
const Inline = ({ value }) => (
<Text>
{value.map((block) => {
switch (block.type) {
case 'PLAIN_TEXT':
return <Plain value={block.value} />;
case 'BOLD':
return <Bold value={block.value} />;
case 'STRIKE':
return <Strike value={block.value} />;
case 'ITALIC':
return <Italic value={block.value} />;
case 'LINK':
// eslint-disable-next-line jsx-a11y/anchor-is-valid
return <Link value={block.value} />;
// case 'MENTION_USER':
// return <Mention value={block.value} mentions={mentions} />;
case 'EMOJI':
return <Emoji emojiHandle={`:${ block.value.value }:`} />;
// case 'MENTION_CHANNEL':
// // case 'COLOR':
// return <Plain value={block.value} />;
case 'INLINE_CODE':
return <Code value={block.value} />;
default:
return null;
}
})}
</Text>
);
Inline.propTypes = {
value: PropTypes.object
};
export default Inline;

View File

@ -0,0 +1,37 @@
/* eslint-disable react/no-array-index-key */
import React from 'react';
import { StyleSheet, Text } from 'react-native';
import PropTypes from 'prop-types';
import Plain from './Plain';
import Strike from './Strike';
import Bold from './Bold';
const styles = StyleSheet.create({
text: {
fontStyle: 'italic'
}
});
const Italic = ({ value }) => (
<Text style={styles.text}>
{value.map((block, index) => {
switch (block.type) {
case 'PLAIN_TEXT':
return <Plain key={index} value={block.value} />;
case 'STRIKE':
return <Strike key={index} value={block.value} />;
case 'BOLD':
return <Bold key={index} value={block.value} />;
default:
return null;
}
})}
</Text>
);
Italic.propTypes = {
value: PropTypes.string
};
export default Italic;

View File

@ -0,0 +1,63 @@
import React from 'react';
import { Text, Clipboard } from 'react-native';
import PropTypes from 'prop-types';
import Strike from './Strike';
import Italic from './Italic';
import Bold from './Bold';
import styles from '../styles';
import I18n from '../../../i18n';
import { LISTENER } from '../../Toast';
import { useTheme } from '../../../theme';
import openLink from '../../../utils/openLink';
import EventEmitter from '../../../utils/events';
import { themes } from '../../../constants/colors';
const Link = ({ value }) => {
const { theme } = useTheme();
const { src, label } = value;
const handlePress = () => {
if (!src.value) {
return;
}
openLink(src.value, theme);
};
const onLongPress = () => {
Clipboard.setString(src.value);
EventEmitter.emit(LISTENER, { message: I18n.t('Copied_to_clipboard') });
};
return (
<Text
onPress={handlePress}
onLongPress={onLongPress}
o
style={{ ...styles.link, color: themes[theme].actionTintColor }}
>
{((block) => {
switch (block.type) {
case 'PLAIN_TEXT':
return block.value;
case 'STRIKE':
return <Strike value={block.value} />;
case 'ITALIC':
return <Italic value={block.value} />;
case 'BOLD':
return <Bold value={block.value} />;
default:
return null;
}
})(label)}
</Text>
);
};
Link.propTypes = {
value: {
src: PropTypes.string,
label: PropTypes.string
}
};
export default Link;

View File

@ -0,0 +1,13 @@
import React from 'react';
import PropTypes from 'prop-types';
import Inline from './Inline';
const Paragraph = ({ value, mentions }) => <Inline value={value} mentions={mentions} />;
Paragraph.propTypes = {
value: PropTypes.string,
mentions: PropTypes.string
};
export default Paragraph;

View File

@ -0,0 +1,15 @@
import React from 'react';
import { Text } from 'react-native';
import PropTypes from 'prop-types';
const Plain = ({ value }) => (
<Text accessibilityLabel={value}>
{value}
</Text>
);
Plain.propTypes = {
value: PropTypes.string
};
export default Plain;

View File

@ -0,0 +1,26 @@
import React from 'react';
import { View } from 'react-native';
import PropTypes from 'prop-types';
import { themes } from '../../../constants/colors';
import { useTheme } from '../../../theme';
import styles from '../styles';
const Quote = ({ value }) => {
const theme = useTheme();
return (
<>
<View style={styles.container}>
<View style={[styles.quote, { backgroundColor: themes[theme].borderColor }]} />
<View style={styles.childContainer}>
{value}
</View>
</View>
</>
);
};
Quote.propTypes = {
value: PropTypes.string
};
export default Quote;

View File

@ -0,0 +1,37 @@
/* eslint-disable react/no-array-index-key */
import React from 'react';
import { StyleSheet, Text } from 'react-native';
import PropTypes from 'prop-types';
import Plain from './Plain';
import Bold from './Bold';
import Italic from './Italic';
const styles = StyleSheet.create({
text: {
textDecorationLine: 'line-through'
}
});
const Strike = ({ value }) => (
<Text style={styles.text}>
{value.map((block, index) => {
switch (block.type) {
case 'PLAIN_TEXT':
return <Plain key={index} value={block.value} />;
case 'BOLD':
return <Bold key={index} value={block.value} />;
case 'ITALIC':
return <Italic key={index} value={block.value} />;
default:
return null;
}
})}
</Text>
);
Strike.propTypes = {
value: PropTypes.string
};
export default Strike;

View File

@ -0,0 +1,56 @@
/* eslint-disable react/no-array-index-key */
import React from 'react';
import PropTypes from 'prop-types';
import List from './List';
import Quote from './Quote';
import Paragraph from './Paragraph';
import Heading from './Heading';
import Code from './Code';
import Link from './Link';
import BigEmoji from './BigEmoji';
import { useTheme } from '../../../theme';
const isBigEmoji = tokens => tokens.length === 1 && tokens[0].type === 'BIG_EMOJI';
const Body = ({ tokens, mentions }) => {
const { theme } = useTheme();
if (isBigEmoji(tokens)) {
return <BigEmoji value={tokens[0].value} theme={theme} />;
}
return (
<>
{tokens.map((block, index) => {
switch (block.type) {
case 'UNORDERED_LIST':
return <List type={block.type} value={block.value} key={index} />;
case 'ORDERED_LIST':
return <List type={block.type} value={block.value} key={index} />;
case 'TASK':
return <List type={block.type} value={block.value} key={index} />;
case 'QUOTE':
return <Quote key={index} value={block.value} />;
case 'PARAGRAPH':
return <Paragraph key={index} value={block.value} mentions={mentions} />;
case 'CODE':
return <Code key={index} value={block.value} />;
case 'LINK':
// eslint-disable-next-line jsx-a11y/anchor-is-valid
return <Link key={index} value={block.value} />;
case 'HEADING':
return <Heading key={index} value={block.value} level={block.level} />;
default:
return null;
}
})}
</>
);
};
Body.propTypes = {
tokens: PropTypes.array,
mentions: PropTypes.array
};
export default Body;

View File

@ -1,16 +0,0 @@
import React from 'react';
import { PropTypes } from 'react-native';
import MarkdownEmoji from '../Emoji';
const BigEmoji = ({ value }) => (
<>
<MarkdownEmoji literal={value} />
</>
);
BigEmoji.propTypes = {
value: PropTypes.string
};
export default BigEmoji;

View File

@ -1,53 +0,0 @@
import React from 'react';
import { Text, PropTypes } from 'react-native';
import MarkdownLink from '../Link';
import MarkdownList from '../List';
// import MarkdownListItem from '../ListItem';
// import MarkdownAtMention from '../AtMention';
// import MarkdownHashtag from '../Hashtag';
import MarkdownBlockQuote from '../BlockQuote';
// import MarkdownTable from '../Table';
// import MarkdownTableRow from '../TableRow';
// import MarkdownTableCell from '../TableCell';
import styles from '../styles';
import { withTheme } from '../../../theme';
import { themes } from '../../../constants/colors';
const Body = ({
md, style, numberOfLines, theme
}) => (
<>
{md.map((block, index) => {
switch (block.type) {
case 'UNORDERED_LIST':
return <MarkdownList ordered={false} />;
case 'ORDERED_LIST':
return <MarkdownList ordered />;
case 'QUOTE':
return <MarkdownBlockQuote />;
case 'PARAGRAPH':
return (
<Text style={[styles.text, style, { color: themes[theme].bodyText }]} numberOfLines={numberOfLines} index={index}>
{block.value?.value}
</Text>
);
case 'CODE':
return <MarkdownLink />;
case 'LINK':
return <MarkdownLink />;
default:
return null;
}
})}
</>
);
Body.propTypes = {
md: PropTypes.array,
theme: PropTypes.string,
style: PropTypes.object,
numberOfLines: PropTypes.number
};
export default withTheme(Body);

View File

@ -4,6 +4,7 @@ import { Parser, Node } from 'commonmark';
import Renderer from 'commonmark-react-renderer';
import PropTypes from 'prop-types';
import removeMarkdown from 'remove-markdown';
import { connect } from 'react-redux';
import shortnameToUnicode from '../../utils/shortnameToUnicode';
import I18n from '../../i18n';
@ -23,6 +24,8 @@ import mergeTextNodes from './mergeTextNodes';
import styles from './styles';
import { isValidURL } from '../../utils/url';
import { getUserSelector } from '../../selectors/login';
import MessageBody from './MessageBody';
// Support <http://link|Text>
const formatText = text => text.replace(
@ -69,6 +72,7 @@ class Markdown extends PureComponent {
static propTypes = {
msg: PropTypes.string,
md: PropTypes.string,
user: PropTypes.object,
getCustomEmoji: PropTypes.func,
baseUrl: PropTypes.string,
username: PropTypes.string,
@ -372,13 +376,17 @@ class Markdown extends PureComponent {
render() {
const {
msg, numberOfLines, preview = false, theme, style = [], testID
msg, md, numberOfLines, preview = false, theme, style = [], testID, user, mentions
} = this.props;
if (!msg) {
return null;
}
if (user.enableMessageParserEarlyAdoption && md) {
return <MessageBody tokens={md} theme={theme} style={style} mentions={mentions} />;
}
let m = formatText(msg);
// Ex: '[ ](https://open.rocket.chat/group/test?msg=abcdef) Test'
@ -406,4 +414,8 @@ class Markdown extends PureComponent {
}
}
export default Markdown;
const mapStateToProps = state => ({
user: getUserSelector(state)
});
export default connect(mapStateToProps)(Markdown);

View File

@ -19,10 +19,10 @@ const changeMessageStatus = async(id, tmid, status, message) => {
if (message) {
m.mentions = message.mentions;
m.channels = message.channels;
}
if (message.md) {
m.md = message.md;
if (message.md) {
m.md = message.md;
}
}
})
);
@ -35,6 +35,10 @@ const changeMessageStatus = async(id, tmid, status, message) => {
if (message) {
tm.mentions = message.mentions;
tm.channels = message.channels;
if (message.md) {
tm.md = message.md;
}
}
})
);

View File

@ -54,7 +54,6 @@ const LastMessage = React.memo(({
msg={formatMsg({
lastMessage, type, showLastMessage, username, useRealName
})}
md={lastMessage?.md}
style={[styles.markdownText, { color: alert ? themes[theme].bodyText : themes[theme].auxiliaryText }]}
customEmojis={false}
useRealName={useRealName}