From c7caf9b442d8ff7f64a4e1837191899006cdcbd8 Mon Sep 17 00:00:00 2001 From: Martin Kraft Date: Wed, 27 Jun 2018 12:08:25 -0400 Subject: [PATCH] =?UTF-8?q?MM-10970:=20Adds=20new=20FormattedMarkdownText?= =?UTF-8?q?=20to=20format=20translations=20using=E2=80=A6=20(#1818)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * MM-10970: Adds new FormattedMarkdownText to format translations using markdown. * MM-10970: Makes defaultText required. Changes prop validation of theme. Moves computing styles to render. --- app/components/formatted_markdown_text.js | 116 ++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 app/components/formatted_markdown_text.js diff --git a/app/components/formatted_markdown_text.js b/app/components/formatted_markdown_text.js new file mode 100644 index 000000000..a87ebdad0 --- /dev/null +++ b/app/components/formatted_markdown_text.js @@ -0,0 +1,116 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; +import {Text} from 'react-native'; +import PropTypes from 'prop-types'; +import Renderer from 'commonmark-react-renderer'; +import {Parser} from 'commonmark'; +import {injectIntl, intlShape} from 'react-intl'; +import MarkdownLink from 'app/components/markdown/markdown_link'; +import {concatStyles, changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; +import {getMarkdownTextStyles} from 'app/utils/markdown'; +import CustomPropTypes from 'app/constants/custom_prop_types'; + +const TARGET_BLANK_URL_PREFIX = '!'; + +/* +* Translations component with a similar API to component except the message string +* accepts markdown. It supports the following non-block-level markdown: +* - *italic* +* - **bold** +* - `inline code` +* - ~~strikethrough~~ +* - [link](http://example.com/) +* - line\nbreaks +* +* Note: Line breaks (\n) in a defaultMessage parameter string must be surrounded by curly brackets {} in JSX. Example: +* +*/ +class FormattedMarkdownText extends React.PureComponent { + static propTypes = { + intl: intlShape.isRequired, + id: PropTypes.string.isRequired, + theme: PropTypes.object.isRequired, + defaultMessage: PropTypes.string.isRequired, + values: PropTypes.object, + style: CustomPropTypes.Style, + }; + + constructor(props) { + super(props); + this.parser = this.createParser(); + this.renderer = this.createRenderer(); + this.textStyles = null; + this.baseTextStyle = null; + } + + createParser = () => { + return new Parser(); + } + + createRenderer = () => { + return new Renderer({ + renderers: { + text: this.renderText, + emph: Renderer.forwardChildren, + strong: Renderer.forwardChildren, + code: this.renderCodeSpan, + link: this.renderLink, + hardBreak: this.renderBreak, + softBreak: this.renderBreak, + paragraph: this.renderParagraph, + del: Renderer.forwardChildren, + }, + }); + } + + computeTextStyle = (baseStyle, context) => { + return concatStyles(baseStyle, context.map((type) => this.textStyles[type])); + } + + renderText = ({context, literal}) => { + const style = this.computeTextStyle(this.props.style || this.baseTextStyle, context); + return {literal}; + } + + renderCodeSpan = ({context, literal}) => { + const style = this.computeTextStyle([this.baseTextStyle, this.textStyles.code], context); + return {literal}; + } + + renderLink = ({children, href}) => { + var url = href[0] === TARGET_BLANK_URL_PREFIX ? href.substring(1, href.length) : href; + return {children}; + } + + renderBreak = () => { + return {'\n'}; + } + + renderParagraph = ({children}) => { + return {children}; + } + + render() { + const {id, defaultMessage, values, theme} = this.props; + const messageDescriptor = {id, defaultMessage}; + const message = this.props.intl.formatMessage(messageDescriptor, values); + const ast = this.parser.parse(message); + this.textStyles = getMarkdownTextStyles(theme); + this.baseTextStyle = getStyleSheet(theme).message; + return this.renderer.render(ast); + } +} + +const getStyleSheet = makeStyleSheetFromTheme((theme) => { + return { + message: { + color: changeOpacity(theme.centerChannelColor, 0.8), + fontSize: 15, + lineHeight: 22, + }, + }; +}); + +export default injectIntl(FormattedMarkdownText); \ No newline at end of file