// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. import {Parser} from 'commonmark'; import Renderer from 'commonmark-react-renderer'; import React, {PropTypes, PureComponent} from 'react'; import { StyleSheet, Text, View } from 'react-native'; import AtMention from 'app/components/at_mention'; import CustomPropTypes from 'app/constants/custom_prop_types'; import {concatStyles} from 'app/utils/theme'; import MarkdownBlockQuote from './markdown_block_quote'; import MarkdownCodeBlock from './markdown_code_block'; import MarkdownLink from './markdown_link'; import MarkdownList from './markdown_list'; import MarkdownListItem from './markdown_list_item'; export default class Markdown extends PureComponent { static propTypes = { baseTextStyle: CustomPropTypes.Style, textStyles: PropTypes.object, blockStyles: PropTypes.object, value: PropTypes.string.isRequired }; static defaultProps = { textStyles: {}, blockStyles: {} }; constructor(props) { super(props); this.parser = new Parser(); this.renderer = this.createRenderer(); } createRenderer = () => { return new Renderer({ renderers: { text: this.renderText, emph: Renderer.forwardChildren, strong: Renderer.forwardChildren, code: this.renderCodeSpan, link: MarkdownLink, image: this.renderImage, atMention: this.renderAtMention, paragraph: this.renderParagraph, heading: this.renderHeading, codeBlock: this.renderCodeBlock, blockQuote: this.renderBlockQuote, list: this.renderList, item: this.renderListItem, hardBreak: this.renderHardBreak, thematicBreak: this.renderThematicBreak, softBreak: this.renderSoftBreak, htmlBlock: this.renderHtml, htmlInline: this.renderHtml }, renderParagraphsInLists: true }); } computeTextStyle = (baseStyle, context) => { return concatStyles(baseStyle, context.map((type) => this.props.textStyles[type])); } renderText = ({context, literal}) => { // Construct the text style based off of the parents of this node since RN's inheritance is limited return {literal}; } renderCodeSpan = ({context, literal}) => { return {literal}; } renderImage = ({children, context, src}) => { // TODO This will be properly implemented for PLT-5736 return ( {'!['} {children} {']('} {src} {')'} ); } renderAtMention = ({context, mentionName}) => { return ( ); } renderParagraph = ({children}) => { return ( {children} ); } renderHeading = ({children, level}) => { return ( {children} ); } renderCodeBlock = (props) => { // These sometimes include a trailing newline const contents = props.literal.replace(/\n$/, ''); return ( {contents} ); } renderBlockQuote = ({children, ...otherProps}) => { return ( {children} ); } renderList = ({children, start, tight, type}) => { return ( {children} ); } renderListItem = ({children, ...otherProps}) => { return ( {children} ); } renderHardBreak = () => { return {'\n'}; } renderThematicBreak = () => { return ; } renderSoftBreak = () => { return {'\n'}; } renderHtml = (props) => { let rendered = this.renderText(props); if (props.isBlock) { rendered = ( {rendered} ); } return rendered; } render() { const ast = this.parser.parse(this.props.value); return {this.renderer.render(ast)}; } } const style = StyleSheet.create({ block: { alignItems: 'flex-start', flexDirection: 'row', flexWrap: 'wrap' } });