diff --git a/app/components/markdown/index.js b/app/components/markdown/index.js
index d4d8e33f2..dcbd63e96 100644
--- a/app/components/markdown/index.js
+++ b/app/components/markdown/index.js
@@ -1,411 +1,16 @@
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import {Parser, Node} from 'commonmark';
-import Renderer from 'commonmark-react-renderer';
-import React, {PureComponent} from 'react';
-import PropTypes from 'prop-types';
-import {
- Platform,
- Text,
- View
-} from 'react-native';
+import {connect} from 'react-redux';
-import AtMention from 'app/components/at_mention';
-import ChannelLink from 'app/components/channel_link';
-import Emoji from 'app/components/emoji';
-import FormattedText from 'app/components/formatted_text';
-import CustomPropTypes from 'app/constants/custom_prop_types';
-import {blendColors, concatStyles, makeStyleSheetFromTheme} from 'app/utils/theme';
+import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
-import MarkdownBlockQuote from './markdown_block_quote';
-import MarkdownCodeBlock from './markdown_code_block';
-import MarkdownImage from './markdown_image';
-import MarkdownLink from './markdown_link';
-import MarkdownList from './markdown_list';
-import MarkdownListItem from './markdown_list_item';
-import {addListItemIndices, pullOutImages} from './transform';
+import Markdown from './markdown';
-export default class Markdown extends PureComponent {
- static propTypes = {
- baseTextStyle: CustomPropTypes.Style,
- blockStyles: PropTypes.object,
- emojiSizes: PropTypes.object,
- fontSizes: PropTypes.object,
- isEdited: PropTypes.bool,
- isSearchResult: PropTypes.bool,
- navigator: PropTypes.object.isRequired,
- onLongPress: PropTypes.func,
- onPostPress: PropTypes.func,
- textStyles: PropTypes.object,
- theme: PropTypes.object.isRequired,
- value: PropTypes.string.isRequired
+function mapStateToProps(state) {
+ return {
+ theme: getTheme(state)
};
-
- static defaultProps = {
- textStyles: {},
- blockStyles: {},
- emojiSizes: {
- ...Platform.select({
- ios: {
- heading1: 25,
- heading2: 25,
- heading3: 25,
- heading4: 25,
- heading5: 25,
- heading6: 25,
- text: 20
- },
- android: {
- heading1: 60,
- heading2: 60,
- heading3: 60,
- heading4: 60,
- heading5: 60,
- heading6: 60,
- text: 45
- }
- })
- },
- fontSizes: {
- heading1: 17,
- heading2: 17,
- heading3: 17,
- heading4: 17,
- heading5: 17,
- heading6: 17,
- text: 15
- },
- onLongPress: () => true
- };
-
- 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,
- del: Renderer.forwardChildren,
- code: this.renderCodeSpan,
- link: this.renderLink,
- image: this.renderImage,
- atMention: this.renderAtMention,
- channelLink: this.renderChannelLink,
- emoji: this.renderEmoji,
-
- 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,
-
- editedIndicator: this.renderEditedIndicator
- },
- renderParagraphsInLists: true,
- getExtraPropsForNode: this.getExtraPropsForNode
- });
- }
-
- getExtraPropsForNode = (node) => {
- const extraProps = {
- continue: node.continue,
- index: node.index
- };
-
- if (node.type === 'image') {
- extraProps.reactChildren = node.react.children;
- extraProps.linkDestination = node.linkDestination;
- }
-
- return extraProps;
- }
-
- computeTextStyle = (baseStyle, context) => {
- return concatStyles(baseStyle, context.map((type) => this.props.textStyles[type]));
- }
-
- renderText = ({context, literal}) => {
- if (context.indexOf('image') !== -1) {
- // If this text is displayed, it will be styled by the image component
- return {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 = ({linkDestination, reactChildren, context, src}) => {
- return (
-
- {reactChildren}
-
- );
- }
-
- renderAtMention = ({context, mentionName}) => {
- return (
-
- );
- }
-
- renderChannelLink = ({context, channelName}) => {
- return (
-
- );
- }
-
- renderEmoji = ({context, emojiName, literal}) => {
- let size;
- let fontSize;
- const headingType = context.find((type) => type.startsWith('heading'));
- if (headingType) {
- size = this.props.emojiSizes[headingType];
- fontSize = this.props.fontSizes[headingType];
- } else {
- size = this.props.emojiSizes.text;
- fontSize = this.props.fontSizes.text;
- }
-
- return (
-
- );
- }
-
- renderParagraph = ({children, first}) => {
- if (!children || children.length === 0) {
- return null;
- }
-
- const style = getStyleSheet(this.props.theme);
- const blockStyle = [style.block];
- if (!first) {
- blockStyle.push(this.props.blockStyles.adjacentParagraph);
- }
-
- return (
-
-
- {children}
-
-
- );
- }
-
- renderHeading = ({children, level}) => {
- const style = getStyleSheet(this.props.theme);
-
- return (
-
-
- {children}
-
-
- );
- }
-
- renderCodeBlock = (props) => {
- // These sometimes include a trailing newline
- const content = props.literal.replace(/\n$/, '');
-
- return (
-
- );
- }
-
- renderBlockQuote = ({children, ...otherProps}) => {
- return (
-
- {children}
-
- );
- }
-
- renderList = ({children, tight, type}) => {
- return (
-
- {children}
-
- );
- }
-
- renderListItem = ({children, context, ...otherProps}) => {
- const level = context.filter((type) => type === 'list').length;
-
- return (
-
- {children}
-
- );
- }
-
- renderHardBreak = () => {
- return {'\n'};
- }
-
- renderThematicBreak = () => {
- return ;
- }
-
- renderSoftBreak = () => {
- return {'\n'};
- }
-
- renderHtml = (props) => {
- let rendered = this.renderText(props);
-
- if (props.isBlock) {
- const style = getStyleSheet(this.props.theme);
-
- rendered = (
-
- {rendered}
-
- );
- }
-
- return rendered;
- }
-
- renderLink = ({children, href}) => {
- return (
-
- {children}
-
- );
- }
-
- renderEditedIndicator = ({context}) => {
- let spacer = '';
- if (context[0] === 'paragraph') {
- spacer = ' ';
- }
-
- const style = getStyleSheet(this.props.theme);
- const styles = [
- this.props.baseTextStyle,
- style.editedIndicatorText
- ];
-
- return (
-
- {spacer}
-
-
- );
- }
-
- render() {
- let ast = this.parser.parse(this.props.value);
-
- ast = addListItemIndices(ast);
- ast = pullOutImages(ast);
-
- if (this.props.isEdited) {
- const editIndicatorNode = new Node('edited_indicator');
- if (['heading', 'paragraph'].includes(ast.lastChild.type)) {
- ast.lastChild.appendChild(editIndicatorNode);
- } else {
- const node = new Node('paragraph');
- node.appendChild(editIndicatorNode);
-
- ast.appendChild(node);
- }
- }
-
- return {this.renderer.render(ast)};
- }
}
-const getStyleSheet = makeStyleSheetFromTheme((theme) => {
- // Android has trouble giving text transparency depending on how it's nested,
- // so we calculate the resulting colour manually
- const editedOpacity = Platform.select({
- ios: 0.3,
- android: 1.0
- });
- const editedColor = Platform.select({
- ios: theme.centerChannelColor,
- android: blendColors(theme.centerChannelBg, theme.centerChannelColor, 0.3)
- });
-
- return {
- block: {
- alignItems: 'flex-start',
- flexDirection: 'row',
- flexWrap: 'wrap'
- },
- editedIndicatorText: {
- color: editedColor,
- fontSize: 14,
- opacity: editedOpacity
- }
- };
-});
+export default connect(mapStateToProps)(Markdown);
diff --git a/app/components/markdown/markdown.js b/app/components/markdown/markdown.js
new file mode 100644
index 000000000..d4d8e33f2
--- /dev/null
+++ b/app/components/markdown/markdown.js
@@ -0,0 +1,411 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import {Parser, Node} from 'commonmark';
+import Renderer from 'commonmark-react-renderer';
+import React, {PureComponent} from 'react';
+import PropTypes from 'prop-types';
+import {
+ Platform,
+ Text,
+ View
+} from 'react-native';
+
+import AtMention from 'app/components/at_mention';
+import ChannelLink from 'app/components/channel_link';
+import Emoji from 'app/components/emoji';
+import FormattedText from 'app/components/formatted_text';
+import CustomPropTypes from 'app/constants/custom_prop_types';
+import {blendColors, concatStyles, makeStyleSheetFromTheme} from 'app/utils/theme';
+
+import MarkdownBlockQuote from './markdown_block_quote';
+import MarkdownCodeBlock from './markdown_code_block';
+import MarkdownImage from './markdown_image';
+import MarkdownLink from './markdown_link';
+import MarkdownList from './markdown_list';
+import MarkdownListItem from './markdown_list_item';
+import {addListItemIndices, pullOutImages} from './transform';
+
+export default class Markdown extends PureComponent {
+ static propTypes = {
+ baseTextStyle: CustomPropTypes.Style,
+ blockStyles: PropTypes.object,
+ emojiSizes: PropTypes.object,
+ fontSizes: PropTypes.object,
+ isEdited: PropTypes.bool,
+ isSearchResult: PropTypes.bool,
+ navigator: PropTypes.object.isRequired,
+ onLongPress: PropTypes.func,
+ onPostPress: PropTypes.func,
+ textStyles: PropTypes.object,
+ theme: PropTypes.object.isRequired,
+ value: PropTypes.string.isRequired
+ };
+
+ static defaultProps = {
+ textStyles: {},
+ blockStyles: {},
+ emojiSizes: {
+ ...Platform.select({
+ ios: {
+ heading1: 25,
+ heading2: 25,
+ heading3: 25,
+ heading4: 25,
+ heading5: 25,
+ heading6: 25,
+ text: 20
+ },
+ android: {
+ heading1: 60,
+ heading2: 60,
+ heading3: 60,
+ heading4: 60,
+ heading5: 60,
+ heading6: 60,
+ text: 45
+ }
+ })
+ },
+ fontSizes: {
+ heading1: 17,
+ heading2: 17,
+ heading3: 17,
+ heading4: 17,
+ heading5: 17,
+ heading6: 17,
+ text: 15
+ },
+ onLongPress: () => true
+ };
+
+ 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,
+ del: Renderer.forwardChildren,
+ code: this.renderCodeSpan,
+ link: this.renderLink,
+ image: this.renderImage,
+ atMention: this.renderAtMention,
+ channelLink: this.renderChannelLink,
+ emoji: this.renderEmoji,
+
+ 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,
+
+ editedIndicator: this.renderEditedIndicator
+ },
+ renderParagraphsInLists: true,
+ getExtraPropsForNode: this.getExtraPropsForNode
+ });
+ }
+
+ getExtraPropsForNode = (node) => {
+ const extraProps = {
+ continue: node.continue,
+ index: node.index
+ };
+
+ if (node.type === 'image') {
+ extraProps.reactChildren = node.react.children;
+ extraProps.linkDestination = node.linkDestination;
+ }
+
+ return extraProps;
+ }
+
+ computeTextStyle = (baseStyle, context) => {
+ return concatStyles(baseStyle, context.map((type) => this.props.textStyles[type]));
+ }
+
+ renderText = ({context, literal}) => {
+ if (context.indexOf('image') !== -1) {
+ // If this text is displayed, it will be styled by the image component
+ return {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 = ({linkDestination, reactChildren, context, src}) => {
+ return (
+
+ {reactChildren}
+
+ );
+ }
+
+ renderAtMention = ({context, mentionName}) => {
+ return (
+
+ );
+ }
+
+ renderChannelLink = ({context, channelName}) => {
+ return (
+
+ );
+ }
+
+ renderEmoji = ({context, emojiName, literal}) => {
+ let size;
+ let fontSize;
+ const headingType = context.find((type) => type.startsWith('heading'));
+ if (headingType) {
+ size = this.props.emojiSizes[headingType];
+ fontSize = this.props.fontSizes[headingType];
+ } else {
+ size = this.props.emojiSizes.text;
+ fontSize = this.props.fontSizes.text;
+ }
+
+ return (
+
+ );
+ }
+
+ renderParagraph = ({children, first}) => {
+ if (!children || children.length === 0) {
+ return null;
+ }
+
+ const style = getStyleSheet(this.props.theme);
+ const blockStyle = [style.block];
+ if (!first) {
+ blockStyle.push(this.props.blockStyles.adjacentParagraph);
+ }
+
+ return (
+
+
+ {children}
+
+
+ );
+ }
+
+ renderHeading = ({children, level}) => {
+ const style = getStyleSheet(this.props.theme);
+
+ return (
+
+
+ {children}
+
+
+ );
+ }
+
+ renderCodeBlock = (props) => {
+ // These sometimes include a trailing newline
+ const content = props.literal.replace(/\n$/, '');
+
+ return (
+
+ );
+ }
+
+ renderBlockQuote = ({children, ...otherProps}) => {
+ return (
+
+ {children}
+
+ );
+ }
+
+ renderList = ({children, tight, type}) => {
+ return (
+
+ {children}
+
+ );
+ }
+
+ renderListItem = ({children, context, ...otherProps}) => {
+ const level = context.filter((type) => type === 'list').length;
+
+ return (
+
+ {children}
+
+ );
+ }
+
+ renderHardBreak = () => {
+ return {'\n'};
+ }
+
+ renderThematicBreak = () => {
+ return ;
+ }
+
+ renderSoftBreak = () => {
+ return {'\n'};
+ }
+
+ renderHtml = (props) => {
+ let rendered = this.renderText(props);
+
+ if (props.isBlock) {
+ const style = getStyleSheet(this.props.theme);
+
+ rendered = (
+
+ {rendered}
+
+ );
+ }
+
+ return rendered;
+ }
+
+ renderLink = ({children, href}) => {
+ return (
+
+ {children}
+
+ );
+ }
+
+ renderEditedIndicator = ({context}) => {
+ let spacer = '';
+ if (context[0] === 'paragraph') {
+ spacer = ' ';
+ }
+
+ const style = getStyleSheet(this.props.theme);
+ const styles = [
+ this.props.baseTextStyle,
+ style.editedIndicatorText
+ ];
+
+ return (
+
+ {spacer}
+
+
+ );
+ }
+
+ render() {
+ let ast = this.parser.parse(this.props.value);
+
+ ast = addListItemIndices(ast);
+ ast = pullOutImages(ast);
+
+ if (this.props.isEdited) {
+ const editIndicatorNode = new Node('edited_indicator');
+ if (['heading', 'paragraph'].includes(ast.lastChild.type)) {
+ ast.lastChild.appendChild(editIndicatorNode);
+ } else {
+ const node = new Node('paragraph');
+ node.appendChild(editIndicatorNode);
+
+ ast.appendChild(node);
+ }
+ }
+
+ return {this.renderer.render(ast)};
+ }
+}
+
+const getStyleSheet = makeStyleSheetFromTheme((theme) => {
+ // Android has trouble giving text transparency depending on how it's nested,
+ // so we calculate the resulting colour manually
+ const editedOpacity = Platform.select({
+ ios: 0.3,
+ android: 1.0
+ });
+ const editedColor = Platform.select({
+ ios: theme.centerChannelColor,
+ android: blendColors(theme.centerChannelBg, theme.centerChannelColor, 0.3)
+ });
+
+ return {
+ block: {
+ alignItems: 'flex-start',
+ flexDirection: 'row',
+ flexWrap: 'wrap'
+ },
+ editedIndicatorText: {
+ color: editedColor,
+ fontSize: 14,
+ opacity: editedOpacity
+ }
+ };
+});
diff --git a/app/components/post_body/post_body.js b/app/components/post_body/post_body.js
index 4478faa53..9fa6df11e 100644
--- a/app/components/post_body/post_body.js
+++ b/app/components/post_body/post_body.js
@@ -233,7 +233,6 @@ class PostBody extends PureComponent {
onLongPress={this.showOptionsContext}
onPostPress={onPress}
textStyles={textStyles}
- theme={theme}
value={message}
/>