diff --git a/app/containers/markdown/MessageBody/BigEmoji.js b/app/containers/markdown/MessageBody/BigEmoji.js new file mode 100644 index 000000000..b26e36077 --- /dev/null +++ b/app/containers/markdown/MessageBody/BigEmoji.js @@ -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) => )} + +); + +BigEmoji.propTypes = { + value: PropTypes.object +}; + +export default BigEmoji; diff --git a/app/containers/markdown/MessageBody/Bold.js b/app/containers/markdown/MessageBody/Bold.js new file mode 100644 index 000000000..d0d7a1d82 --- /dev/null +++ b/app/containers/markdown/MessageBody/Bold.js @@ -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 }) => ( + + {value.map((block, index) => { + switch (block.type) { + case 'PLAIN_TEXT': + return ; + case 'STRIKE': + return ; + case 'ITALIC': + return ; + default: + return null; + } + })} + +); + +Bold.propTypes = { + value: PropTypes.string +}; + +export default Bold; diff --git a/app/containers/markdown/NewMarkdown/Code.js b/app/containers/markdown/MessageBody/Code.js similarity index 81% rename from app/containers/markdown/NewMarkdown/Code.js rename to app/containers/markdown/MessageBody/Code.js index 593934c7a..3e8414f81 100644 --- a/app/containers/markdown/NewMarkdown/Code.js +++ b/app/containers/markdown/MessageBody/Code.js @@ -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 = ({ { + const { theme } = useTheme(); + const emojiUnicode = shortnameToUnicode(emojiHandle); + return ( + + {emojiUnicode} + + ); +}; + +Emoji.propTypes = { + emojiHandle: PropTypes.string, + style: PropTypes.object, + isBigEmoji: PropTypes.bool +}; + +export default Emoji; diff --git a/app/containers/markdown/MessageBody/Heading.js b/app/containers/markdown/MessageBody/Heading.js new file mode 100644 index 000000000..82e781ef5 --- /dev/null +++ b/app/containers/markdown/MessageBody/Heading.js @@ -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 ( + + {block.value} + + ); + default: + return null; + } + })} + + ); +}; + +Heading.propTypes = { + value: PropTypes.string, + level: PropTypes.number +}; + +export default Heading; diff --git a/app/containers/markdown/MessageBody/Inline.js b/app/containers/markdown/MessageBody/Inline.js new file mode 100644 index 000000000..ca84f4cfe --- /dev/null +++ b/app/containers/markdown/MessageBody/Inline.js @@ -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 }) => ( + + {value.map((block) => { + switch (block.type) { + case 'PLAIN_TEXT': + return ; + case 'BOLD': + return ; + case 'STRIKE': + return ; + case 'ITALIC': + return ; + case 'LINK': + // eslint-disable-next-line jsx-a11y/anchor-is-valid + return ; + // case 'MENTION_USER': + // return ; + case 'EMOJI': + return ; + // case 'MENTION_CHANNEL': + // // case 'COLOR': + // return ; + case 'INLINE_CODE': + return ; + default: + return null; + } + })} + +); + +Inline.propTypes = { + value: PropTypes.object +}; + +export default Inline; diff --git a/app/containers/markdown/MessageBody/Italic.js b/app/containers/markdown/MessageBody/Italic.js new file mode 100644 index 000000000..24cb082e2 --- /dev/null +++ b/app/containers/markdown/MessageBody/Italic.js @@ -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 }) => ( + + {value.map((block, index) => { + switch (block.type) { + case 'PLAIN_TEXT': + return ; + case 'STRIKE': + return ; + case 'BOLD': + return ; + default: + return null; + } + })} + +); + +Italic.propTypes = { + value: PropTypes.string +}; + +export default Italic; diff --git a/app/containers/markdown/MessageBody/Link.js b/app/containers/markdown/MessageBody/Link.js new file mode 100644 index 000000000..07b49b9e3 --- /dev/null +++ b/app/containers/markdown/MessageBody/Link.js @@ -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 ( + + {((block) => { + switch (block.type) { + case 'PLAIN_TEXT': + return block.value; + case 'STRIKE': + return ; + case 'ITALIC': + return ; + case 'BOLD': + return ; + default: + return null; + } + })(label)} + + ); +}; + +Link.propTypes = { + value: { + src: PropTypes.string, + label: PropTypes.string + } +}; + +export default Link; diff --git a/app/containers/markdown/MessageBody/Paragraph.js b/app/containers/markdown/MessageBody/Paragraph.js new file mode 100644 index 000000000..f73d1f76e --- /dev/null +++ b/app/containers/markdown/MessageBody/Paragraph.js @@ -0,0 +1,13 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import Inline from './Inline'; + +const Paragraph = ({ value, mentions }) => ; + +Paragraph.propTypes = { + value: PropTypes.string, + mentions: PropTypes.string +}; + +export default Paragraph; diff --git a/app/containers/markdown/MessageBody/Plain.js b/app/containers/markdown/MessageBody/Plain.js new file mode 100644 index 000000000..3cf0e6d4b --- /dev/null +++ b/app/containers/markdown/MessageBody/Plain.js @@ -0,0 +1,15 @@ +import React from 'react'; +import { Text } from 'react-native'; +import PropTypes from 'prop-types'; + +const Plain = ({ value }) => ( + + {value} + +); + +Plain.propTypes = { + value: PropTypes.string +}; + +export default Plain; diff --git a/app/containers/markdown/MessageBody/Quote.js b/app/containers/markdown/MessageBody/Quote.js new file mode 100644 index 000000000..8f9f9fe02 --- /dev/null +++ b/app/containers/markdown/MessageBody/Quote.js @@ -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 ( + <> + + + + {value} + + + + ); +}; + +Quote.propTypes = { + value: PropTypes.string +}; + +export default Quote; diff --git a/app/containers/markdown/MessageBody/Strike.js b/app/containers/markdown/MessageBody/Strike.js new file mode 100644 index 000000000..f33c5b104 --- /dev/null +++ b/app/containers/markdown/MessageBody/Strike.js @@ -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 }) => ( + + {value.map((block, index) => { + switch (block.type) { + case 'PLAIN_TEXT': + return ; + case 'BOLD': + return ; + case 'ITALIC': + return ; + default: + return null; + } + })} + +); + +Strike.propTypes = { + value: PropTypes.string +}; + +export default Strike; diff --git a/app/containers/markdown/MessageBody/index.js b/app/containers/markdown/MessageBody/index.js new file mode 100644 index 000000000..37aa53d1d --- /dev/null +++ b/app/containers/markdown/MessageBody/index.js @@ -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 ; + } + + return ( + <> + {tokens.map((block, index) => { + switch (block.type) { + case 'UNORDERED_LIST': + return ; + case 'ORDERED_LIST': + return ; + case 'TASK': + return ; + case 'QUOTE': + return ; + case 'PARAGRAPH': + return ; + case 'CODE': + return ; + case 'LINK': + // eslint-disable-next-line jsx-a11y/anchor-is-valid + return ; + case 'HEADING': + return ; + default: + return null; + } + })} + + ); +}; + +Body.propTypes = { + tokens: PropTypes.array, + mentions: PropTypes.array +}; + +export default Body; diff --git a/app/containers/markdown/NewMarkdown/BigEmoji.js b/app/containers/markdown/NewMarkdown/BigEmoji.js deleted file mode 100644 index 5bf78fdb4..000000000 --- a/app/containers/markdown/NewMarkdown/BigEmoji.js +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react'; -import { PropTypes } from 'react-native'; - -import MarkdownEmoji from '../Emoji'; - -const BigEmoji = ({ value }) => ( - <> - - -); - -BigEmoji.propTypes = { - value: PropTypes.string -}; - -export default BigEmoji; diff --git a/app/containers/markdown/NewMarkdown/Body.js b/app/containers/markdown/NewMarkdown/Body.js deleted file mode 100644 index 5fb5ecb65..000000000 --- a/app/containers/markdown/NewMarkdown/Body.js +++ /dev/null @@ -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 ; - case 'ORDERED_LIST': - return ; - case 'QUOTE': - return ; - case 'PARAGRAPH': - return ( - - {block.value?.value} - - ); - case 'CODE': - return ; - case 'LINK': - return ; - default: - return null; - } - })} - -); - -Body.propTypes = { - md: PropTypes.array, - theme: PropTypes.string, - style: PropTypes.object, - numberOfLines: PropTypes.number -}; - -export default withTheme(Body); diff --git a/app/containers/markdown/index.js b/app/containers/markdown/index.js index 6eef6e7e9..c74e0d972 100644 --- a/app/containers/markdown/index.js +++ b/app/containers/markdown/index.js @@ -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 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 ; + } + 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); diff --git a/app/lib/methods/sendMessage.js b/app/lib/methods/sendMessage.js index 466c9a3e0..8c3529d6f 100644 --- a/app/lib/methods/sendMessage.js +++ b/app/lib/methods/sendMessage.js @@ -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; + } } }) ); diff --git a/app/presentation/RoomItem/LastMessage.js b/app/presentation/RoomItem/LastMessage.js index 48e7c2b25..25fc2c08d 100644 --- a/app/presentation/RoomItem/LastMessage.js +++ b/app/presentation/RoomItem/LastMessage.js @@ -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}