summaryrefslogtreecommitdiffstats
path: root/webapp/components/post_view
diff options
context:
space:
mode:
authorHarrison Healey <harrisonmhealey@gmail.com>2016-08-29 09:50:00 -0400
committerChristopher Speller <crspeller@gmail.com>2016-08-29 09:50:00 -0400
commit167dd22eefeeeb9c1eaebd990a4f5902bd366302 (patch)
tree6ddb15a80b2a608d42e20df72b98c0ae72821671 /webapp/components/post_view
parent55342e8fe16613f06528ed1aa726231e9b597d26 (diff)
downloadchat-167dd22eefeeeb9c1eaebd990a4f5902bd366302.tar.gz
chat-167dd22eefeeeb9c1eaebd990a4f5902bd366302.tar.bz2
chat-167dd22eefeeeb9c1eaebd990a4f5902bd366302.zip
PLT-1752/PLT-3567/PLT-3998 Highlighting links in search, unit tests for autolinking (#3865)
* Added highlighting to links when their URL includes the search term * Decoupling UserStore from react-router to allow for unit tests involving it * PLT-3998 Added SiteURL as an option to be passed into the text formatting code * Removed reference to PreferenceStore and window from TextFormatting * Refactored TextFormatting to remove remaining browser-only code * Updated ChannelHeader and MessageWrapper to match the changes to TextFormatting * Increased max listeners for Preference and Emoji stores * PLT-3832 Added automated unit tests for autolinking * PLT-3567 Rerender posts when mention keywords change * Updated RHS and search to match the changes to TextFormatting * Broke TextFormatting's dependency on the UserStore
Diffstat (limited to 'webapp/components/post_view')
-rw-r--r--webapp/components/post_view/components/post.jsx7
-rw-r--r--webapp/components/post_view/components/post_body.jsx14
-rw-r--r--webapp/components/post_view/components/post_list.jsx2
-rw-r--r--webapp/components/post_view/components/post_message_container.jsx87
-rw-r--r--webapp/components/post_view/components/post_message_view.jsx66
-rw-r--r--webapp/components/post_view/post_view_controller.jsx16
6 files changed, 156 insertions, 36 deletions
diff --git a/webapp/components/post_view/components/post.jsx b/webapp/components/post_view/components/post.jsx
index 1b94f717d..e9019bf38 100644
--- a/webapp/components/post_view/components/post.jsx
+++ b/webapp/components/post_view/components/post.jsx
@@ -114,10 +114,6 @@ export default class Post extends React.Component {
return true;
}
- if (nextProps.emojis !== this.props.emojis) {
- return true;
- }
-
if (nextState.dropdownOpened !== this.state.dropdownOpened) {
return true;
}
@@ -259,7 +255,6 @@ export default class Post extends React.Component {
handleCommentClick={this.handleCommentClick}
compactDisplay={this.props.compactDisplay}
previewCollapsed={this.props.previewCollapsed}
- emojis={this.props.emojis}
/>
</div>
</div>
@@ -279,7 +274,6 @@ Post.propTypes = {
isLastComment: React.PropTypes.bool,
shouldHighlight: React.PropTypes.bool,
displayNameType: React.PropTypes.string,
- hasProfiles: React.PropTypes.bool,
currentUser: React.PropTypes.object.isRequired,
center: React.PropTypes.bool,
compactDisplay: React.PropTypes.bool,
@@ -287,7 +281,6 @@ Post.propTypes = {
commentCount: React.PropTypes.number,
isCommentMention: React.PropTypes.bool,
useMilitaryTime: React.PropTypes.bool.isRequired,
- emojis: React.PropTypes.object.isRequired,
isFlagged: React.PropTypes.bool,
status: React.PropTypes.string
};
diff --git a/webapp/components/post_view/components/post_body.jsx b/webapp/components/post_view/components/post_body.jsx
index 348e7fc93..8c3b3009f 100644
--- a/webapp/components/post_view/components/post_body.jsx
+++ b/webapp/components/post_view/components/post_body.jsx
@@ -6,8 +6,8 @@ import UserStore from 'stores/user_store.jsx';
import * as Utils from 'utils/utils.jsx';
import * as GlobalActions from 'actions/global_actions.jsx';
import Constants from 'utils/constants.jsx';
-import * as TextFormatting from 'utils/text_formatting.jsx';
import PostBodyAdditionalContent from './post_body_additional_content.jsx';
+import PostMessageContainer from './post_message_container.jsx';
import PendingPostOptions from './pending_post_options.jsx';
import {FormattedMessage} from 'react-intl';
@@ -43,10 +43,6 @@ export default class PostBody extends React.Component {
return true;
}
- if (nextProps.emojis !== this.props.emojis) {
- return true;
- }
-
return false;
}
@@ -165,10 +161,7 @@ export default class PostBody extends React.Component {
);
} else {
message = (
- <span
- onClick={TextFormatting.handleClick}
- dangerouslySetInnerHTML={{__html: TextFormatting.formatText(this.props.post.message, {emojis: this.props.emojis})}}
- />
+ <PostMessageContainer post={this.props.post}/>
);
}
@@ -215,6 +208,5 @@ PostBody.propTypes = {
retryPost: React.PropTypes.func.isRequired,
handleCommentClick: React.PropTypes.func.isRequired,
compactDisplay: React.PropTypes.bool,
- previewCollapsed: React.PropTypes.string,
- emojis: React.PropTypes.object.isRequired
+ previewCollapsed: React.PropTypes.string
};
diff --git a/webapp/components/post_view/components/post_list.jsx b/webapp/components/post_view/components/post_list.jsx
index fc532c373..f891afdb3 100644
--- a/webapp/components/post_view/components/post_list.jsx
+++ b/webapp/components/post_view/components/post_list.jsx
@@ -314,7 +314,6 @@ export default class PostList extends React.Component {
compactDisplay={this.props.compactDisplay}
previewCollapsed={this.props.previewsCollapsed}
useMilitaryTime={this.props.useMilitaryTime}
- emojis={this.props.emojis}
isFlagged={isFlagged}
status={status}
/>
@@ -584,7 +583,6 @@ PostList.propTypes = {
previewsCollapsed: React.PropTypes.string,
useMilitaryTime: React.PropTypes.bool.isRequired,
isFocusPost: React.PropTypes.bool,
- emojis: React.PropTypes.object.isRequired,
flaggedPosts: React.PropTypes.object,
statuses: React.PropTypes.object
};
diff --git a/webapp/components/post_view/components/post_message_container.jsx b/webapp/components/post_view/components/post_message_container.jsx
new file mode 100644
index 000000000..4ab556fca
--- /dev/null
+++ b/webapp/components/post_view/components/post_message_container.jsx
@@ -0,0 +1,87 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import React from 'react';
+
+import EmojiStore from 'stores/emoji_store.jsx';
+import PreferenceStore from 'stores/preference_store.jsx';
+import {Preferences} from 'utils/constants.jsx';
+import UserStore from 'stores/user_store.jsx';
+
+import PostMessageView from './post_message_view.jsx';
+
+export default class PostMessageContainer extends React.Component {
+ static propTypes = {
+ post: React.PropTypes.object.isRequired,
+ options: React.PropTypes.object
+ };
+
+ static defaultProps = {
+ options: {}
+ };
+
+ constructor(props) {
+ super(props);
+
+ this.onEmojiChange = this.onEmojiChange.bind(this);
+ this.onPreferenceChange = this.onPreferenceChange.bind(this);
+ this.onUserChange = this.onUserChange.bind(this);
+
+ const mentionKeys = UserStore.getCurrentMentionKeys();
+ mentionKeys.push('@here');
+
+ this.state = {
+ emojis: EmojiStore.getEmojis(),
+ enableFormatting: PreferenceStore.getBool(Preferences.CATEGORY_ADVANCED_SETTINGS, 'formatting', true),
+ mentionKeys,
+ usernameMap: UserStore.getProfilesUsernameMap()
+ };
+ }
+
+ componentDidMount() {
+ EmojiStore.addChangeListener(this.onEmojiChange);
+ PreferenceStore.addChangeListener(this.onPreferenceChange);
+ UserStore.addChangeListener(this.onUserChange);
+ }
+
+ componentWillUnmount() {
+ EmojiStore.removeChangeListener(this.onEmojiChange);
+ PreferenceStore.removeChangeListener(this.onPreferenceChange);
+ UserStore.removeChangeListener(this.onUserChange);
+ }
+
+ onEmojiChange() {
+ this.setState({
+ emojis: EmojiStore.getEmojis()
+ });
+ }
+
+ onPreferenceChange() {
+ this.setState({
+ enableFormatting: PreferenceStore.getBool(Preferences.CATEGORY_ADVANCED_SETTINGS, 'formatting', true)
+ });
+ }
+
+ onUserChange() {
+ const mentionKeys = UserStore.getCurrentMentionKeys();
+ mentionKeys.push('@here');
+
+ this.setState({
+ mentionKeys,
+ usernameMap: UserStore.getProfilesUsernameMap()
+ });
+ }
+
+ render() {
+ return (
+ <PostMessageView
+ options={this.props.options}
+ message={this.props.post.message}
+ emojis={this.state.emojis}
+ enableFormatting={this.state.enableFormatting}
+ mentionKeys={this.state.mentionKeys}
+ usernameMap={this.state.usernameMap}
+ />
+ );
+ }
+} \ No newline at end of file
diff --git a/webapp/components/post_view/components/post_message_view.jsx b/webapp/components/post_view/components/post_message_view.jsx
new file mode 100644
index 000000000..99589c973
--- /dev/null
+++ b/webapp/components/post_view/components/post_message_view.jsx
@@ -0,0 +1,66 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import React from 'react';
+
+import * as TextFormatting from 'utils/text_formatting.jsx';
+import * as Utils from 'utils/utils.jsx';
+
+export default class PostMessageView extends React.Component {
+ static propTypes = {
+ options: React.PropTypes.object.isRequired,
+ message: React.PropTypes.string.isRequired,
+ emojis: React.PropTypes.object.isRequired,
+ enableFormatting: React.PropTypes.bool.isRequired,
+ mentionKeys: React.PropTypes.arrayOf(React.PropTypes.string).isRequired,
+ usernameMap: React.PropTypes.object.isRequired
+ };
+
+ shouldComponentUpdate(nextProps) {
+ if (!Utils.areObjectsEqual(nextProps.options, this.props.options)) {
+ return true;
+ }
+
+ if (nextProps.message !== this.props.message) {
+ return true;
+ }
+
+ // emojis are immutable
+ if (nextProps.emojis !== this.props.emojis) {
+ return true;
+ }
+
+ if (nextProps.enableFormatting !== this.props.enableFormatting) {
+ return true;
+ }
+
+ if (!Utils.areObjectsEqual(nextProps.mentionKeys, this.props.mentionKeys)) {
+ return true;
+ }
+
+ // Don't check if props.usernameMap changes since it is very large and inefficient to do so.
+ // This mimics previous behaviour, but could be changed if we decide it's worth it.
+
+ return false;
+ }
+
+ render() {
+ if (!this.props.enableFormatting) {
+ return <span>{this.props.message}</span>;
+ }
+
+ const options = Object.assign({}, this.props.options, {
+ emojis: this.props.emojis,
+ siteURL: Utils.getSiteURL(),
+ mentionKeys: this.props.mentionKeys,
+ usernameMap: this.props.usernameMap
+ });
+
+ return (
+ <span
+ onClick={Utils.handleFormattedTextClick}
+ dangerouslySetInnerHTML={{__html: TextFormatting.formatText(this.props.message, options)}}
+ />
+ );
+ }
+} \ No newline at end of file
diff --git a/webapp/components/post_view/post_view_controller.jsx b/webapp/components/post_view/post_view_controller.jsx
index 7e30818fb..58f8eff74 100644
--- a/webapp/components/post_view/post_view_controller.jsx
+++ b/webapp/components/post_view/post_view_controller.jsx
@@ -4,7 +4,6 @@
import PostList from './components/post_list.jsx';
import LoadingScreen from 'components/loading_screen.jsx';
-import EmojiStore from 'stores/emoji_store.jsx';
import PreferenceStore from 'stores/preference_store.jsx';
import UserStore from 'stores/user_store.jsx';
import PostStore from 'stores/post_store.jsx';
@@ -25,7 +24,6 @@ export default class PostViewController extends React.Component {
this.onPreferenceChange = this.onPreferenceChange.bind(this);
this.onUserChange = this.onUserChange.bind(this);
this.onPostsChange = this.onPostsChange.bind(this);
- this.onEmojisChange = this.onEmojisChange.bind(this);
this.onStatusChange = this.onStatusChange.bind(this);
this.onPostsViewJumpRequest = this.onPostsViewJumpRequest.bind(this);
this.onSetNewMessageIndicator = this.onSetNewMessageIndicator.bind(this);
@@ -67,7 +65,6 @@ export default class PostViewController extends React.Component {
compactDisplay: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.MESSAGE_DISPLAY, Preferences.MESSAGE_DISPLAY_DEFAULT) === Preferences.MESSAGE_DISPLAY_COMPACT,
previewsCollapsed: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.COLLAPSE_DISPLAY, 'false'),
useMilitaryTime: PreferenceStore.getBool(Constants.Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.USE_MILITARY_TIME, false),
- emojis: EmojiStore.getEmojis(),
flaggedPosts: PreferenceStore.getCategory(Constants.Preferences.CATEGORY_FLAGGED_POST)
};
}
@@ -123,12 +120,6 @@ export default class PostViewController extends React.Component {
});
}
- onEmojisChange() {
- this.setState({
- emojis: EmojiStore.getEmojis()
- });
- }
-
onStatusChange() {
const channel = this.state.channel;
let statuses;
@@ -145,7 +136,6 @@ export default class PostViewController extends React.Component {
UserStore.addStatusesChangeListener(this.onStatusChange);
PostStore.addChangeListener(this.onPostsChange);
PostStore.addPostsViewJumpListener(this.onPostsViewJumpRequest);
- EmojiStore.addChangeListener(this.onEmojisChange);
ChannelStore.addLastViewedListener(this.onSetNewMessageIndicator);
}
@@ -155,7 +145,6 @@ export default class PostViewController extends React.Component {
UserStore.removeStatusesChangeListener(this.onStatusChange);
PostStore.removeChangeListener(this.onPostsChange);
PostStore.removePostsViewJumpListener(this.onPostsViewJumpRequest);
- EmojiStore.removeChangeListener(this.onEmojisChange);
ChannelStore.removeLastViewedListener(this.onSetNewMessageIndicator);
}
@@ -298,10 +287,6 @@ export default class PostViewController extends React.Component {
return true;
}
- if (nextState.emojis !== this.state.emojis) {
- return true;
- }
-
return false;
}
@@ -332,7 +317,6 @@ export default class PostViewController extends React.Component {
useMilitaryTime={this.state.useMilitaryTime}
flaggedPosts={this.state.flaggedPosts}
lastViewed={this.state.lastViewed}
- emojis={this.state.emojis}
ownNewMessage={this.state.ownNewMessage}
statuses={this.state.statuses}
/>