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
Этот коммит содержится в:
Harrison Healey
2017-01-30 11:49:00 -05:00
коммит произвёл GitHub
родитель 63d68b3e36
Коммит 39ee5737b7
13 изменённых файлов: 321 добавлений и 51 удалений

Просмотреть файл

@@ -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>
);

Просмотреть файл

@@ -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>
);
}

Просмотреть файл

@@ -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);
}

Просмотреть файл

@@ -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

Просмотреть файл

@@ -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}