mattermost-mobile/app/components/formatted_markdown_text.js
Elias Nahum ae629a455c
Update UX for post long press menu (#2325)
* Update UX for post long press menu

* Post menu feedback review
2018-11-14 17:29:09 -03:00

147 lines
4.9 KiB
JavaScript

// 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 {concatStyles, changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import {getMarkdownTextStyles} from 'app/utils/markdown';
import CustomPropTypes from 'app/constants/custom_prop_types';
import AtMention from 'app/components/at_mention';
import MarkdownLink from 'app/components/markdown/markdown_link';
const TARGET_BLANK_URL_PREFIX = '!';
/*
* Translations component with a similar API to <FormattedText> 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:
* <FormattedMarkdownText id='my.example' defaultMessage={'first line\nsecond line'} theme={theme} />
*/
class FormattedMarkdownText extends React.PureComponent {
static propTypes = {
baseTextStyle: CustomPropTypes.Style,
defaultMessage: PropTypes.string.isRequired,
id: PropTypes.string.isRequired,
navigator: PropTypes.object.isRequired,
onPostPress: PropTypes.func,
style: CustomPropTypes.Style,
textStyles: PropTypes.object,
theme: PropTypes.object.isRequired,
values: PropTypes.object,
};
static contextTypes = {
intl: intlShape,
};
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,
html_inline: this.renderHTML,
html_block: this.renderHTML,
atMention: this.renderAtMention,
},
});
}
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 <Text style={style}>{literal}</Text>;
}
renderCodeSpan = ({context, literal}) => {
const style = this.computeTextStyle([this.baseTextStyle, this.textStyles.code], context);
return <Text style={style}>{literal}</Text>;
}
renderLink = ({children, href}) => {
var url = href[0] === TARGET_BLANK_URL_PREFIX ? href.substring(1, href.length) : href;
return <MarkdownLink href={url}>{children}</MarkdownLink>;
}
renderBreak = () => {
return <Text>{'\n'}</Text>;
}
renderParagraph = ({children}) => {
return <Text>{children}</Text>;
}
renderHTML = (props) => {
console.warn(`HTML used in FormattedMarkdownText component with id ${this.props.id}`); // eslint-disable-line no-console
return this.renderText(props);
}
renderAtMention = ({context, mentionName}) => {
return (
<AtMention
mentionStyle={this.props.textStyles.mention}
mentionName={mentionName}
navigator={this.props.navigator}
onPostPress={this.props.onPostPress}
textStyle={this.computeTextStyle(this.props.baseTextStyle, context)}
/>
);
}
render() {
const {id, defaultMessage, values, theme} = this.props;
const messageDescriptor = {id, defaultMessage};
const {formatMessage} = this.context.intl;
const message = 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);