diff --git a/app/components/at_mention/at_mention.js b/app/components/at_mention/at_mention.js index eca3a4c76..ae2a87e5b 100644 --- a/app/components/at_mention/at_mention.js +++ b/app/components/at_mention/at_mention.js @@ -1,13 +1,13 @@ // Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. // See License.txt for license information. -import React, {PureComponent} from 'react'; +import React from 'react'; import PropTypes from 'prop-types'; import {Text} from 'react-native'; import CustomPropTypes from 'app/constants/custom_prop_types'; -export default class AtMention extends PureComponent { +export default class AtMention extends React.PureComponent { static propTypes = { mentionName: PropTypes.string.isRequired, mentionStyle: CustomPropTypes.Style, @@ -15,12 +15,28 @@ export default class AtMention extends PureComponent { usersByUsername: PropTypes.object.isRequired }; - getUsernameFromMentionName = () => { - let mentionName = this.props.mentionName; + constructor(props) { + super(props); + + this.state = { + username: this.getUsernameFromMentionName(props) + }; + } + + componentWillReceiveProps(nextProps) { + if (nextProps.mentionName !== this.props.mentionName || nextProps.usersByUsername !== this.props.usersByUsername) { + this.setState({ + username: this.getUsernameFromMentionName(nextProps) + }); + } + } + + getUsernameFromMentionName(props) { + let mentionName = props.mentionName; while (mentionName.length > 0) { - if (this.props.usersByUsername[mentionName]) { - return this.props.usersByUsername[mentionName].username; + if (props.usersByUsername[mentionName]) { + return props.usersByUsername[mentionName].username; } // Repeatedly trim off trailing punctuation in case this is at the end of a sentence @@ -31,12 +47,16 @@ export default class AtMention extends PureComponent { } } - // Just assume this is a mention for a user that we don't have - return mentionName; + return ''; } render() { - const username = this.getUsernameFromMentionName(); + const username = this.state.username; + + if (!username) { + return {'@' + this.props.mentionName}; + } + const suffix = this.props.mentionName.substring(username.length); return ( diff --git a/app/components/channel_link/channel_link.js b/app/components/channel_link/channel_link.js new file mode 100644 index 000000000..3a346601e --- /dev/null +++ b/app/components/channel_link/channel_link.js @@ -0,0 +1,83 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import React from 'react'; +import PropTypes from 'prop-types'; +import {Text} from 'react-native'; + +import CustomPropTypes from 'app/constants/custom_prop_types'; + +export default class ChannelLink extends React.PureComponent { + static propTypes = { + channelName: PropTypes.string.isRequired, + linkStyle: CustomPropTypes.Style, + textStyle: CustomPropTypes.Style, + channelsByName: PropTypes.object.isRequired, + actions: PropTypes.shape({ + handleSelectChannel: PropTypes.func.isRequired, + setChannelDisplayName: PropTypes.func.isRequired + }).isRequired + }; + + constructor(props) { + super(props); + + this.state = { + channel: this.getChannelFromChannelName(props) + }; + } + + componentWillReceiveProps(nextProps) { + if (nextProps.channelName !== this.props.channelName || nextProps.channelsByName !== this.props.channelsByName) { + this.setState({ + channel: this.getChannelFromChannelName(nextProps) + }); + } + } + + getChannelFromChannelName(props) { + let channelName = props.channelName; + + while (channelName.length > 0) { + if (props.channelsByName[channelName]) { + return props.channelsByName[channelName]; + } + + // Repeatedly trim off trailing punctuation in case this is at the end of a sentence + if ((/[_-]$/).test(channelName)) { + channelName = channelName.substring(0, channelName.length - 1); + } else { + break; + } + } + + return null; + } + + handlePress = () => { + this.props.actions.setChannelDisplayName(this.state.channel.display_name); + this.props.actions.handleSelectChannel(this.state.channel.id); + } + + render() { + const channel = this.state.channel; + + if (!channel) { + return {'~' + this.props.channelName}; + } + + const suffix = this.props.channelName.substring(channel.name.length); + + return ( + + + {channel.display_name} + + {suffix} + + ); + } +} diff --git a/app/components/channel_link/index.js b/app/components/channel_link/index.js new file mode 100644 index 000000000..9d6c9d053 --- /dev/null +++ b/app/components/channel_link/index.js @@ -0,0 +1,29 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import {connect} from 'react-redux'; +import {bindActionCreators} from 'redux'; + +import {getChannelsNameMapInCurrentTeam} from 'mattermost-redux/selectors/entities/channels'; + +import {handleSelectChannel, setChannelDisplayName} from 'app/actions/views/channel'; + +import ChannelLink from './channel_link'; + +function mapStateToProps(state, ownProps) { + return { + channelsByName: getChannelsNameMapInCurrentTeam(state), + ...ownProps + }; +} + +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + handleSelectChannel, + setChannelDisplayName + }, dispatch) + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(ChannelLink); diff --git a/app/components/markdown/index.js b/app/components/markdown/index.js index 82212b969..00cff842a 100644 --- a/app/components/markdown/index.js +++ b/app/components/markdown/index.js @@ -13,6 +13,7 @@ import { } from 'react-native'; import AtMention from 'app/components/at_mention'; +import ChannelLink from 'app/components/channel_link'; import Emoji from 'app/components/emoji'; import CustomPropTypes from 'app/constants/custom_prop_types'; import {concatStyles} from 'app/utils/theme'; @@ -77,6 +78,7 @@ export default class Markdown extends React.PureComponent { link: MarkdownLink, image: this.renderImage, atMention: this.renderAtMention, + channelLink: this.renderChannelLink, emoji: this.renderEmoji, paragraph: this.renderParagraph, @@ -134,6 +136,16 @@ export default class Markdown extends React.PureComponent { ); } + renderChannelLink = ({context, channelName}) => { + return ( + + ); + } + renderEmoji = ({context, emojiName, literal}) => { let size; const headingType = context.find((type) => type.startsWith('heading')); diff --git a/package.json b/package.json index a8f64f4ce..6664c68f4 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,8 @@ "private": true, "dependencies": { "babel-polyfill": "6.23.0", - "commonmark": "hmhealey/commonmark.js#0e1c57aff4e0729e12ae1453fadc6c8bfd86cf96", - "commonmark-react-renderer": "hmhealey/commonmark-react-renderer#1cec47814455f2eb3858864334eb4300b5103523", + "commonmark": "hmhealey/commonmark.js#10bc18c0c8054088c0ec4737a8766c6b5794a323", + "commonmark-react-renderer": "hmhealey/commonmark-react-renderer#7be9f10cdb3408a64366a20e60975edff0b5c710", "deep-equal": "1.0.1", "intl": "1.2.5", "mattermost-redux": "mattermost/mattermost-redux#master", diff --git a/yarn.lock b/yarn.lock index 5811899e9..1fb0f70fb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1512,9 +1512,9 @@ commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" -commonmark-react-renderer@hmhealey/commonmark-react-renderer#1cec47814455f2eb3858864334eb4300b5103523: +commonmark-react-renderer@hmhealey/commonmark-react-renderer#7be9f10cdb3408a64366a20e60975edff0b5c710: version "4.3.2" - resolved "https://codeload.github.com/hmhealey/commonmark-react-renderer/tar.gz/1cec47814455f2eb3858864334eb4300b5103523" + resolved "https://codeload.github.com/hmhealey/commonmark-react-renderer/tar.gz/7be9f10cdb3408a64366a20e60975edff0b5c710" dependencies: in-publish "^2.0.0" lodash.assign "^4.2.0" @@ -1522,9 +1522,9 @@ commonmark-react-renderer@hmhealey/commonmark-react-renderer#1cec47814455f2eb385 pascalcase "^0.1.1" xss-filters "^1.2.6" -commonmark@hmhealey/commonmark.js#0e1c57aff4e0729e12ae1453fadc6c8bfd86cf96: +commonmark@hmhealey/commonmark.js#10bc18c0c8054088c0ec4737a8766c6b5794a323: version "0.27.0" - resolved "https://codeload.github.com/hmhealey/commonmark.js/tar.gz/0e1c57aff4e0729e12ae1453fadc6c8bfd86cf96" + resolved "https://codeload.github.com/hmhealey/commonmark.js/tar.gz/10bc18c0c8054088c0ec4737a8766c6b5794a323" dependencies: entities "~ 1.1.1" mdurl "~ 1.0.1"