summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarrison Healey <harrisonmhealey@gmail.com>2017-01-30 11:49:00 -0500
committerGitHub <noreply@github.com>2017-01-30 11:49:00 -0500
commit39ee5737b7aa84833a1dc5b03c492b46e22209bd (patch)
tree61736473b9d62cd9aa9a4575198022b063711c55
parent63d68b3e36c2deaaeb7174edc1950fab3752d887 (diff)
downloadchat-39ee5737b7aa84833a1dc5b03c492b46e22209bd.tar.gz
chat-39ee5737b7aa84833a1dc5b03c492b46e22209bd.tar.bz2
chat-39ee5737b7aa84833a1dc5b03c492b46e22209bd.zip
PLT-2555/PLT-5009/PLT-5225 Changed system messages to be rendered by the client (#5209)
* Moved rendering of (message deleted) into PostMessageView * Added additional post types to constants on client * Changed system messages to be rendered in the client's language * Updated new system messages to have relevant usernames highlighted and have markdown rendered in header change messages
-rw-r--r--webapp/actions/global_actions.jsx2
-rw-r--r--webapp/components/post_view/components/post_body.jsx18
-rw-r--r--webapp/components/post_view/components/post_message_view.jsx57
-rw-r--r--webapp/components/post_view/components/system_message_helpers.jsx228
-rw-r--r--webapp/components/rhs_comment.jsx10
-rw-r--r--webapp/components/rhs_root_post.jsx3
-rw-r--r--webapp/i18n/en.json12
-rw-r--r--webapp/i18n/fr.json12
-rw-r--r--webapp/stores/notification_store.jsx3
-rw-r--r--webapp/stores/post_store.jsx6
-rw-r--r--webapp/utils/constants.jsx17
-rw-r--r--webapp/utils/post_utils.jsx2
-rw-r--r--webapp/utils/utils.jsx2
13 files changed, 321 insertions, 51 deletions
diff --git a/webapp/actions/global_actions.jsx b/webapp/actions/global_actions.jsx
index 23e19f22f..c8da43f24 100644
--- a/webapp/actions/global_actions.jsx
+++ b/webapp/actions/global_actions.jsx
@@ -397,7 +397,7 @@ export function sendEphemeralPost(message, channelId) {
user_id: '0',
channel_id: channelId || ChannelStore.getCurrentId(),
message,
- type: Constants.POST_TYPE_EPHEMERAL,
+ type: Constants.PostTypes.EPHEMERAL,
create_at: timestamp,
update_at: timestamp,
props: {}
diff --git a/webapp/components/post_view/components/post_body.jsx b/webapp/components/post_view/components/post_body.jsx
index e690b3702..8b650423f 100644
--- a/webapp/components/post_view/components/post_body.jsx
+++ b/webapp/components/post_view/components/post_body.jsx
@@ -156,22 +156,6 @@ export default class PostBody extends React.Component {
);
}
- let message;
- if (this.props.post.state === Constants.POST_DELETED) {
- message = (
- <p>
- <FormattedMessage
- id='post_body.deleted'
- defaultMessage='(message deleted)'
- />
- </p>
- );
- } else {
- message = (
- <PostMessageContainer post={this.props.post}/>
- );
- }
-
const messageWrapper = (
<div
key={`${post.id}_message`}
@@ -179,7 +163,7 @@ export default class PostBody extends React.Component {
className={postClass}
>
{loading}
- {message}
+ <PostMessageContainer post={this.props.post}/>
</div>
);
diff --git a/webapp/components/post_view/components/post_message_view.jsx b/webapp/components/post_view/components/post_message_view.jsx
index eff791aec..371dd64eb 100644
--- a/webapp/components/post_view/components/post_message_view.jsx
+++ b/webapp/components/post_view/components/post_message_view.jsx
@@ -4,9 +4,12 @@
import React from 'react';
import {FormattedMessage} from 'react-intl';
+import Constants from 'utils/constants.jsx';
+import * as PostUtils from 'utils/post_utils.jsx';
import * as TextFormatting from 'utils/text_formatting.jsx';
import * as Utils from 'utils/utils.jsx';
-import * as PostUtils from 'utils/post_utils.jsx';
+
+import {renderSystemMessage} from './system_message_helpers.jsx';
export default class PostMessageView extends React.Component {
static propTypes = {
@@ -29,6 +32,14 @@ export default class PostMessageView extends React.Component {
return true;
}
+ if (nextProps.post.state !== this.props.post.state) {
+ return true;
+ }
+
+ if (nextProps.post.type !== this.props.post.type) {
+ return true;
+ }
+
// emojis are immutable
if (nextProps.emojis !== this.props.emojis) {
return true;
@@ -49,26 +60,43 @@ export default class PostMessageView extends React.Component {
return false;
}
- editedIndicator() {
+ renderDeletedPost() {
return (
- PostUtils.isEdited(this.props.post) ?
- <span className='edited'>
- <FormattedMessage
- id='post_message_view.edited'
- defaultMessage='(edited)'
- />
- </span> :
- ''
+ <p>
+ <FormattedMessage
+ id='post_body.deleted'
+ defaultMessage='(message deleted)'
+ />
+ </p>
+ );
+ }
+
+ renderEditedIndicator() {
+ if (!PostUtils.isEdited(this.props.post)) {
+ return null;
+ }
+
+ return (
+ <span className='edited'>
+ <FormattedMessage
+ id='post_message_view.edited'
+ defaultMessage='(edited)'
+ />
+ </span>
);
}
render() {
+ if (this.props.post.state === Constants.POST_DELETED) {
+ return this.renderDeletedPost();
+ }
+
if (!this.props.enableFormatting) {
return (
<span>
{this.props.post.message}
&nbsp;
- {this.editedIndicator()}
+ {this.renderEditedIndicator()}
</span>
);
}
@@ -82,13 +110,18 @@ export default class PostMessageView extends React.Component {
team: this.props.team
});
+ const renderedSystemMessage = renderSystemMessage(this.props.post, options);
+ if (renderedSystemMessage) {
+ return <div>{renderedSystemMessage}</div>;
+ }
+
return (
<div>
<span
onClick={Utils.handleFormattedTextClick}
dangerouslySetInnerHTML={{__html: TextFormatting.formatText(this.props.post.message, options)}}
/>
- {this.editedIndicator()}
+ {this.renderEditedIndicator()}
</div>
);
}
diff --git a/webapp/components/post_view/components/system_message_helpers.jsx b/webapp/components/post_view/components/system_message_helpers.jsx
new file mode 100644
index 000000000..6f6454599
--- /dev/null
+++ b/webapp/components/post_view/components/system_message_helpers.jsx
@@ -0,0 +1,228 @@
+// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import React from 'react';
+import {FormattedMessage} from 'react-intl';
+
+import {PostTypes} from 'utils/constants.jsx';
+import {formatText} from 'utils/text_formatting.jsx';
+
+function renderFormattedText(value, options) {
+ return <span dangerouslySetInnerHTML={{__html: formatText(value, options)}}/>;
+}
+
+function renderJoinChannelMessage(post, options) {
+ const username = renderFormattedText(post.props.username, options);
+
+ return (
+ <FormattedMessage
+ id='api.channel.join_channel.post_and_forget'
+ defaultMessage='{username} has joined the channel.'
+ values={{username}}
+ />
+ );
+}
+
+function renderLeaveChannelMessage(post, options) {
+ const username = renderFormattedText(post.props.username, options);
+
+ return (
+ <FormattedMessage
+ id='api.channel.leave.left'
+ defaultMessage='{username} has left the channel.'
+ values={{username}}
+ />
+ );
+}
+
+function renderAddToChannelMessage(post, options) {
+ const username = renderFormattedText(post.props.username, options);
+ const addedUsername = renderFormattedText(post.props.addedUsername, options);
+
+ return (
+ <FormattedMessage
+ id='api.channel.add_member.added'
+ defaultMessage='{addedUsername} added to the channel by {username}'
+ values={{
+ username,
+ addedUsername
+ }}
+ />
+ );
+}
+
+function renderRemoveFromChannelMessage(post, options) {
+ const removedUsername = renderFormattedText(post.props.removedUsername, options);
+
+ return (
+ <FormattedMessage
+ id='api.channel.remove_member.removed'
+ defaultMessage='{removedUsername} was removed from the channel'
+ values={{
+ removedUsername
+ }}
+ />
+ );
+}
+
+function renderHeaderChangeMessage(post, options) {
+ if (!post.props.username) {
+ return null;
+ }
+
+ const headerOptions = {
+ ...options,
+ singleline: true
+ };
+
+ const username = renderFormattedText(post.props.username, options);
+ const oldHeader = post.props.old_header ? renderFormattedText(post.props.old_header, headerOptions) : null;
+ const newHeader = post.props.new_header ? renderFormattedText(post.props.new_header, headerOptions) : null;
+
+ if (post.props.new_header) {
+ if (post.props.old_header) {
+ return (
+ <FormattedMessage
+ id='api.channel.post_update_channel_header_message_and_forget.updated_from'
+ defaultMessage='{username} updated the channel header from: {old} to: {new}'
+ values={{
+ username,
+ old: oldHeader,
+ new: newHeader
+ }}
+ />
+ );
+ }
+
+ return (
+ <FormattedMessage
+ id='api.channel.post_update_channel_header_message_and_forget.updated_to'
+ defaultMessage='{username} updated the channel header to: {new}'
+ values={{
+ username,
+ new: newHeader
+ }}
+ />
+ );
+ } else if (post.props.old_header) {
+ return (
+ <FormattedMessage
+ id='api.channel.post_update_channel_header_message_and_forget.removed'
+ defaultMessage='{username} removed the channel header (was: {old})'
+ values={{
+ username,
+ old: oldHeader
+ }}
+ />
+ );
+ }
+
+ return null;
+}
+
+function renderDisplayNameChangeMessage(post, options) {
+ if (!(post.props.username && post.props.old_displayname && post.props.new_displayname)) {
+ return null;
+ }
+
+ const username = renderFormattedText(post.props.username, options);
+ const oldDisplayName = post.props.old_displayname;
+ const newDisplayName = post.props.new_displayname;
+
+ return (
+ <FormattedMessage
+ id='api.channel.post_update_channel_displayname_message_and_forget.updated_from'
+ defaultMessage='{username} updated the channel display name from: {old} to: {new}'
+ values={{
+ username,
+ old: oldDisplayName,
+ new: newDisplayName
+ }}
+ />
+ );
+}
+
+function renderPurposeChangeMessage(post, options) {
+ if (!post.props.username) {
+ return null;
+ }
+
+ const username = renderFormattedText(post.props.username, options);
+ const oldPurpose = post.props.old_purpose;
+ const newPurpose = post.props.new_purpose;
+
+ if (post.props.new_purpose) {
+ if (post.props.old_purpose) {
+ return (
+ <FormattedMessage
+ id='app.channel.post_update_channel_purpose_message.updated_from'
+ defaultMessage='{username} updated the channel purpose from: {old} to: {new}'
+ values={{
+ username,
+ old: oldPurpose,
+ new: newPurpose
+ }}
+ />
+ );
+ }
+
+ return (
+ <FormattedMessage
+ id='app.channel.post_update_channel_purpose_message.updated_to'
+ defaultMessage='{username} updated the channel purpose to: {new}'
+ values={{
+ username,
+ new: newPurpose
+ }}
+ />
+ );
+ } else if (post.props.old_purpose) {
+ return (
+ <FormattedMessage
+ id='app.channel.post_update_channel_purpose_message.removed'
+ defaultMessage='{username} removed the channel purpose (was: {old})'
+ values={{
+ username,
+ old: oldPurpose
+ }}
+ />
+ );
+ }
+
+ return null;
+}
+
+function renderChannelDeletedMessage(post, options) {
+ if (!post.props.username) {
+ return null;
+ }
+
+ const username = renderFormattedText(post.props.username, options);
+
+ return (
+ <FormattedMessage
+ id='api.channel.delete_channel.archived'
+ defaultMessage='{username} has archived the channel.'
+ values={{username}}
+ />
+ );
+}
+
+const systemMessageRenderers = {
+ [PostTypes.JOIN_CHANNEL]: renderJoinChannelMessage,
+ [PostTypes.LEAVE_CHANNEL]: renderLeaveChannelMessage,
+ [PostTypes.ADD_TO_CHANNEL]: renderAddToChannelMessage,
+ [PostTypes.REMOVE_FROM_CHANNEL]: renderRemoveFromChannelMessage,
+ [PostTypes.HEADER_CHANGE]: renderHeaderChangeMessage,
+ [PostTypes.DISPLAYNAME_CHANGE]: renderDisplayNameChangeMessage,
+ [PostTypes.PURPOSE_CHANGE]: renderPurposeChangeMessage,
+ [PostTypes.CHANNEL_DELETED]: renderChannelDeletedMessage
+};
+
+export function renderSystemMessage(post, options) {
+ if (!systemMessageRenderers[post.type]) {
+ return null;
+ }
+
+ return systemMessageRenderers[post.type](post, options);
+}
diff --git a/webapp/components/rhs_comment.jsx b/webapp/components/rhs_comment.jsx
index 26659c7a1..0eb7717b3 100644
--- a/webapp/components/rhs_comment.jsx
+++ b/webapp/components/rhs_comment.jsx
@@ -294,7 +294,6 @@ export default class RhsComment extends React.Component {
let loading;
let postClass = '';
- let message = <PostMessageContainer post={post}/>;
if (post.state === Constants.POST_FAILED) {
postClass += ' post-fail';
@@ -307,13 +306,6 @@ export default class RhsComment extends React.Component {
src={loadingGif}
/>
);
- } else if (this.props.post.state === Constants.POST_DELETED) {
- message = (
- <FormattedMessage
- id='post_body.deleted'
- defaultMessage='(message deleted)'
- />
- );
}
let systemMessageClass = '';
@@ -491,7 +483,7 @@ export default class RhsComment extends React.Component {
<div className='post__body'>
<div className={postClass}>
{loading}
- {message}
+ <PostMessageContainer post={post}/>
</div>
{fileAttachment}
<ReactionListContainer
diff --git a/webapp/components/rhs_root_post.jsx b/webapp/components/rhs_root_post.jsx
index 7d00e2322..a114c7385 100644
--- a/webapp/components/rhs_root_post.jsx
+++ b/webapp/components/rhs_root_post.jsx
@@ -358,7 +358,6 @@ export default class RhsRootPost extends React.Component {
}
const profilePicContainer = (<div className='post__img'>{profilePic}</div>);
- const messageWrapper = <PostMessageContainer post={post}/>;
let flag;
let flagFunc;
@@ -445,7 +444,7 @@ export default class RhsRootPost extends React.Component {
<div className='post__body'>
<PostBodyAdditionalContent
post={post}
- message={messageWrapper}
+ message={<PostMessageContainer post={post}/>}
previewCollapsed={this.props.previewCollapsed}
/>
{fileAttachment}
diff --git a/webapp/i18n/en.json b/webapp/i18n/en.json
index 6aa994bf3..fd2a34f36 100644
--- a/webapp/i18n/en.json
+++ b/webapp/i18n/en.json
@@ -968,6 +968,18 @@
"analytics.team.title": "Team Statistics for {team}",
"analytics.team.totalPosts": "Total Posts",
"analytics.team.totalUsers": "Total Users",
+ "api.channel.add_member.added": "{addedUsername} added to the channel by {username}",
+ "api.channel.delete_channel.archived": "{username} has archived the channel.",
+ "api.channel.join_channel.post_and_forget": "{username} has joined the channel.",
+ "api.channel.leave.left": "{username} has left the channel.",
+ "api.channel.post_update_channel_displayname_message_and_forget.updated_from": "{username} updated the channel display name from: {old} to: {new}",
+ "api.channel.post_update_channel_header_message_and_forget.removed": "{username} removed the channel header (was: {old})",
+ "api.channel.post_update_channel_header_message_and_forget.updated_from": "{username} updated the channel header from: {old} to: {new}",
+ "api.channel.post_update_channel_header_message_and_forget.updated_to": "{username} updated the channel header to: {new}",
+ "api.channel.remove_member.removed": "{removedUsername} was removed from the channel",
+ "app.channel.post_update_channel_purpose_message.removed": "{username} removed the channel purpose (was: {old})",
+ "app.channel.post_update_channel_purpose_message.updated_from": "{username} updated the channel purpose from: {old} to: {new}",
+ "app.channel.post_update_channel_purpose_message.updated_to": "{username} updated the channel purpose to: {new}",
"audit_table.accountActive": "Account made active",
"audit_table.accountInactive": "Account made inactive",
"audit_table.action": "Action",
diff --git a/webapp/i18n/fr.json b/webapp/i18n/fr.json
index b464930b9..80df30f58 100644
--- a/webapp/i18n/fr.json
+++ b/webapp/i18n/fr.json
@@ -968,6 +968,18 @@
"analytics.team.title": "Statistiques d'équipe pour {team}",
"analytics.team.totalPosts": "Nombre de messages",
"analytics.team.totalUsers": "Nombre d'utilisateurs",
+ "api.channel.add_member.added": "{addedUsername} a été ajouté au canal par {username}",
+ "api.channel.delete_channel.archived": "{username} a archivé le canal.",
+ "api.channel.join_channel.post_and_forget": "{username} a rejoint le canal.",
+ "api.channel.leave.left": "{username} a quitté le canal.",
+ "api.channel.post_update_channel_displayname_message_and_forget.updated_from": "{username} a mis à jour l'en-tête du canal de : {old} en : {new}",
+ "api.channel.post_update_channel_header_message_and_forget.removed": "{username} a supprimé le titre du canal (était : {old})",
+ "api.channel.post_update_channel_header_message_and_forget.updated_from": "{username} a mis à jour l'en-tête du canal de : {old} en : {new}",
+ "api.channel.post_update_channel_header_message_and_forget.updated_to": "{username} a mis à jour l'en-tête du canal en : {new}",
+ "api.channel.remove_member.removed": "{removedUsername} a été retiré du canal.",
+ "app.channel.post_update_channel_purpose_message.removed": "{username} a supprimé le titre du canal (était : {old})",
+ "app.channel.post_update_channel_purpose_message.updated_from": "{username} a mis à jour l'en-tête du canal de : {old} en : {new}",
+ "app.channel.post_update_channel_purpose_message.updated_to": "{username} a mis à jour l'en-tête du canal en : {new}",
"audit_table.accountActive": "Compte activé",
"audit_table.accountInactive": "Compte désactivé",
"audit_table.action": "Action",
diff --git a/webapp/stores/notification_store.jsx b/webapp/stores/notification_store.jsx
index 4c89fe480..93d544cd1 100644
--- a/webapp/stores/notification_store.jsx
+++ b/webapp/stores/notification_store.jsx
@@ -6,7 +6,6 @@ import EventEmitter from 'events';
import Constants from 'utils/constants.jsx';
import UserStore from './user_store.jsx';
import ChannelStore from './channel_store.jsx';
-import PreferenceStore from './preference_store.jsx';
import * as UserAgent from 'utils/user_agent.jsx';
import * as Utils from 'utils/utils.jsx';
import * as PostUtils from 'utils/post_utils.jsx';
@@ -35,8 +34,6 @@ class NotificationStoreClass extends EventEmitter {
if ((UserStore.getCurrentId() !== post.user_id || post.props.from_webhook === 'true')) {
if (PostUtils.isSystemMessage(post)) {
return;
- } else if (!PreferenceStore.getBool(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, 'join_leave', true) && post.type === Constants.POST_TYPE_JOIN_LEAVE) {
- return;
}
let mentions = [];
diff --git a/webapp/stores/post_store.jsx b/webapp/stores/post_store.jsx
index 411eaf724..360b7cd72 100644
--- a/webapp/stores/post_store.jsx
+++ b/webapp/stores/post_store.jsx
@@ -8,7 +8,7 @@ import ChannelStore from 'stores/channel_store.jsx';
import BrowserStore from 'stores/browser_store.jsx';
import UserStore from 'stores/user_store.jsx';
-import Constants from 'utils/constants.jsx';
+import {Constants, PostTypes} from 'utils/constants.jsx';
const ActionTypes = Constants.ActionTypes;
const CHANGE_EVENT = 'change';
@@ -616,7 +616,9 @@ class PostStoreClass extends EventEmitter {
if (!joinLeave && postsList) {
postsList.order = postsList.order.filter((id) => {
- if (postsList.posts[id].type === Constants.POST_TYPE_JOIN_LEAVE) {
+ const post = postsList.posts[id];
+
+ if (post.type === PostTypes.JOIN_LEAVE || post.type === PostTypes.JOIN_CHANNEL || post.type === PostTypes.LEAVE_CHANNEL) {
Reflect.deleteProperty(postsList.posts, id);
return false;
diff --git a/webapp/utils/constants.jsx b/webapp/utils/constants.jsx
index 646adbd15..dd9a4486e 100644
--- a/webapp/utils/constants.jsx
+++ b/webapp/utils/constants.jsx
@@ -228,6 +228,19 @@ export const TutorialSteps = {
MENU_POPOVER: 3
};
+export const PostTypes = {
+ JOIN_LEAVE: 'system_join_leave',
+ JOIN_CHANNEL: 'system_join_channel',
+ LEAVE_CHANNEL: 'system_leave_channel',
+ ADD_TO_CHANNEL: 'system_add_to_channel',
+ REMOVE_FROM_CHANNEL: 'system_remove_from_channel',
+ HEADER_CHANGE: 'system_header_change',
+ DISPLAYNAME_CHANGE: 'system_displayname_change',
+ PURPOSE_CHANGE: 'system_purpose_change',
+ CHANNEL_DELETED: 'system_channel_deleted',
+ EPHEMERAL: 'system_ephemeral'
+};
+
export const Constants = {
Preferences,
SocketEvents,
@@ -236,6 +249,7 @@ export const Constants = {
UserStatuses,
UserSearchOptions,
TutorialSteps,
+ PostTypes,
PayloadSources: keyMirror({
SERVER_ACTION: null,
@@ -349,9 +363,6 @@ export const Constants = {
POST_LOADING: 'loading',
POST_FAILED: 'failed',
POST_DELETED: 'deleted',
- POST_TYPE_EPHEMERAL: 'system_ephemeral',
- POST_TYPE_JOIN_LEAVE: 'system_join_leave',
- POST_TYPE_ATTACHMENT: 'slack_attachment',
SYSTEM_MESSAGE_PREFIX: 'system_',
SYSTEM_MESSAGE_PROFILE_NAME: 'System',
SYSTEM_MESSAGE_PROFILE_IMAGE: logoImage,
diff --git a/webapp/utils/post_utils.jsx b/webapp/utils/post_utils.jsx
index 20993b95c..0b908c55b 100644
--- a/webapp/utils/post_utils.jsx
+++ b/webapp/utils/post_utils.jsx
@@ -73,4 +73,4 @@ export function canEditPost(post, editDisableAction) {
}
}
return canEdit;
-} \ No newline at end of file
+}
diff --git a/webapp/utils/utils.jsx b/webapp/utils/utils.jsx
index 815e78e53..4e8a05075 100644
--- a/webapp/utils/utils.jsx
+++ b/webapp/utils/utils.jsx
@@ -1170,7 +1170,7 @@ export function clearFileInput(elm) {
}
export function isPostEphemeral(post) {
- return post.type === Constants.POST_TYPE_EPHEMERAL || post.state === Constants.POST_DELETED;
+ return post.type === Constants.PostTypes.EPHEMERAL || post.state === Constants.POST_DELETED;
}
export function getRootId(post) {