Files
mattermost/webapp/components/rhs_comment.jsx
David Meza f740698dbe PLT-6486 Add an @username button to the profile popover, that puts the username in the post when clicked (#6349)
* PLT-6486 Add an `@username` button to the profile popover, that puts the username in the post when clicked

* PLT-6486 Display `@username` mention on the right text area on center or RHS.

* Disable @mentions from profile popover on searches, mentions and pinned posts. Fix js errors.

* Control undefined post in SearchStore that causes an exception.
2017-07-31 08:24:13 -04:00

471 lines
14 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import UserProfile from './user_profile.jsx';
import FileAttachmentListContainer from 'components/file_attachment_list';
import PostMessageContainer from 'components/post_view/post_message_view';
import ProfilePicture from 'components/profile_picture.jsx';
import ReactionListContainer from 'components/post_view/reaction_list';
import PostFlagIcon from 'components/post_view/post_flag_icon.jsx';
import FailedPostOptions from 'components/post_view/failed_post_options';
import DotMenu from 'components/dot_menu';
import EmojiPickerOverlay from 'components/emoji_picker/emoji_picker_overlay.jsx';
import {addReaction} from 'actions/post_actions.jsx';
import TeamStore from 'stores/team_store.jsx';
import * as Utils from 'utils/utils.jsx';
import * as PostUtils from 'utils/post_utils.jsx';
import Constants from 'utils/constants.jsx';
import React from 'react';
import PropTypes from 'prop-types';
import {Link} from 'react-router/es6';
import {FormattedMessage} from 'react-intl';
export default class RhsComment extends React.Component {
static propTypes = {
post: PropTypes.object,
lastPostCount: PropTypes.number,
user: PropTypes.object.isRequired,
currentUser: PropTypes.object.isRequired,
compactDisplay: PropTypes.bool,
useMilitaryTime: PropTypes.bool.isRequired,
isFlagged: PropTypes.bool,
status: PropTypes.string,
isBusy: PropTypes.bool,
removePost: PropTypes.func.isRequired
};
constructor(props) {
super(props);
this.removePost = this.removePost.bind(this);
this.reactEmojiClick = this.reactEmojiClick.bind(this);
this.handleDropdownOpened = this.handleDropdownOpened.bind(this);
this.state = {
currentTeamDisplayName: TeamStore.getCurrent().name,
width: '',
height: '',
showEmojiPicker: false,
dropdownOpened: false
};
}
componentDidMount() {
window.addEventListener('resize', () => {
Utils.updateWindowDimensions(this);
});
}
componentWillUnmount() {
window.removeEventListener('resize', () => {
Utils.updateWindowDimensions(this);
});
}
removePost() {
this.props.removePost(this.props.post);
}
createRemovePostButton() {
return (
<a
href='#'
className='post__remove theme'
type='button'
onClick={this.removePost}
>
{'×'}
</a>
);
}
shouldComponentUpdate(nextProps, nextState) {
if (nextProps.status !== this.props.status) {
return true;
}
if (nextProps.isBusy !== this.props.isBusy) {
return true;
}
if (nextProps.compactDisplay !== this.props.compactDisplay) {
return true;
}
if (nextProps.useMilitaryTime !== this.props.useMilitaryTime) {
return true;
}
if (nextProps.isFlagged !== this.props.isFlagged) {
return true;
}
if (!Utils.areObjectsEqual(nextProps.post, this.props.post)) {
return true;
}
if (!Utils.areObjectsEqual(nextProps.currentUser, this.props.currentUser)) {
return true;
}
if (this.state.showEmojiPicker !== nextState.showEmojiPicker) {
return true;
}
if (nextProps.lastPostCount !== this.props.lastPostCount) {
return true;
}
if (this.state.dropdownOpened !== nextState.dropdownOpened) {
return true;
}
return false;
}
timeTag(post, timeOptions) {
return (
<time
className='post__time'
dateTime={Utils.getDateForUnixTicks(post.create_at).toISOString()}
>
{Utils.getDateForUnixTicks(post.create_at).toLocaleString('en', timeOptions)}
</time>
);
}
renderTimeTag(post, timeOptions) {
return Utils.isMobile() ?
this.timeTag(post, timeOptions) :
(
<Link
to={`/${this.state.currentTeamDisplayName}/pl/${post.id}`}
target='_blank'
className='post__permalink'
>
{this.timeTag(post, timeOptions)}
</Link>
);
}
toggleEmojiPicker = () => {
const showEmojiPicker = !this.state.showEmojiPicker;
this.setState({
showEmojiPicker,
dropdownOpened: showEmojiPicker
});
}
reactEmojiClick(emoji) {
this.setState({showEmojiPicker: false});
const emojiName = emoji.name || emoji.aliases[0];
addReaction(this.props.post.channel_id, this.props.post.id, emojiName);
}
getClassName = (post, isSystemMessage) => {
let className = 'post post--thread';
if (this.props.currentUser.id === post.user_id) {
className += ' current--user';
}
if (isSystemMessage) {
className += ' post--system';
}
if (this.props.compactDisplay) {
className += ' post--compact';
}
if (post.is_pinned) {
className += ' post--pinned';
}
if (this.state.dropdownOpened) {
className += ' post--hovered';
}
return className;
}
handleDropdownOpened(isOpened) {
this.setState({
dropdownOpened: isOpened
});
}
render() {
const post = this.props.post;
const mattermostLogo = Constants.MATTERMOST_ICON_SVG;
let idCount = -1;
if (this.props.lastPostCount >= 0 && this.props.lastPostCount < Constants.TEST_ID_COUNT) {
idCount = this.props.lastPostCount;
}
const isEphemeral = Utils.isPostEphemeral(post);
const isSystemMessage = PostUtils.isSystemMessage(post);
let status = this.props.status;
if (post.props && post.props.from_webhook === 'true') {
status = null;
}
let botIndicator;
let userProfile = (
<UserProfile
user={this.props.user}
status={status}
isBusy={this.props.isBusy}
isRHS={true}
hasMention={true}
/>
);
let visibleMessage;
if (post.props && post.props.from_webhook) {
if (post.props.override_username && global.window.mm_config.EnablePostUsernameOverride === 'true') {
userProfile = (
<UserProfile
user={this.props.user}
overwriteName={post.props.override_username}
disablePopover={true}
/>
);
} else {
userProfile = (
<UserProfile
user={this.props.user}
disablePopover={true}
/>
);
}
botIndicator = <div className='col col__name bot-indicator'>{'BOT'}</div>;
} else if (isSystemMessage) {
userProfile = (
<UserProfile
user={{}}
overwriteName={
<FormattedMessage
id='post_info.system'
defaultMessage='System'
/>
}
overwriteImage={Constants.SYSTEM_MESSAGE_PROFILE_IMAGE}
disablePopover={true}
/>
);
visibleMessage = (
<span className='post__visibility'>
<FormattedMessage
id='post_info.message.visible'
defaultMessage='(Only visible to you)'
/>
</span>
);
}
let failedPostOptions;
let postClass = '';
if (post.failed) {
postClass += ' post-failed';
failedPostOptions = <FailedPostOptions post={this.props.post}/>;
}
if (PostUtils.isEdited(this.props.post)) {
postClass += ' post--edited';
}
let profilePic = (
<ProfilePicture
src={PostUtils.getProfilePicSrcForPost(post, this.props.user)}
status={status}
width='36'
height='36'
user={this.props.user}
isBusy={this.props.isBusy}
isRHS={true}
hasMention={true}
/>
);
if (post.props && post.props.from_webhook) {
profilePic = (
<ProfilePicture
src={PostUtils.getProfilePicSrcForPost(post, this.props.user)}
width='36'
height='36'
/>
);
}
if (isSystemMessage) {
profilePic = (
<span
className='icon'
dangerouslySetInnerHTML={{__html: mattermostLogo}}
/>
);
}
if (this.props.compactDisplay) {
if (post.props && post.props.from_webhook) {
profilePic = (
<ProfilePicture
src=''
/>
);
} else {
profilePic = (
<ProfilePicture
src=''
status={status}
user={this.props.user}
isBusy={this.props.isBusy}
isRHS={true}
hasMention={true}
/>
);
}
}
const profilePicContainer = (<div className='post__img'>{profilePic}</div>);
let fileAttachment = null;
if (post.file_ids && post.file_ids.length > 0) {
fileAttachment = (
<FileAttachmentListContainer
post={post}
compactDisplay={this.props.compactDisplay}
/>
);
}
let react;
if (!isEphemeral && !post.failed && !isSystemMessage && window.mm_config.EnableEmojiPicker === 'true') {
react = (
<span>
<EmojiPickerOverlay
show={this.state.showEmojiPicker}
onHide={this.toggleEmojiPicker}
target={() => this.refs.dotMenu}
onEmojiClick={this.reactEmojiClick}
rightOffset={15}
spaceRequiredAbove={342}
spaceRequiredBelow={342}
/>
<a
href='#'
className='reacticon__container reaction'
onClick={this.toggleEmojiPicker}
ref={'rhs_reacticon_' + post.id}
><i className='fa fa-smile-o'/>
</a>
</span>
);
}
let options;
if (isEphemeral) {
options = (
<div className='col col__remove'>
{this.createRemovePostButton()}
</div>
);
} else if (!isSystemMessage) {
const dotMenu = (
<DotMenu
idPrefix={Constants.RHS}
idCount={idCount}
post={this.props.post}
isFlagged={this.props.isFlagged}
handleDropdownOpened={this.handleDropdownOpened}
/>
);
options = (
<div
ref='dotMenu'
className='col col__reply'
>
{dotMenu}
{react}
</div>
);
}
let pinnedBadge;
if (post.is_pinned) {
pinnedBadge = (
<span className='post__pinned-badge'>
<FormattedMessage
id='post_info.pinned'
defaultMessage='Pinned'
/>
</span>
);
}
const timeOptions = {
hour: '2-digit',
minute: '2-digit',
hour12: !this.props.useMilitaryTime
};
return (
<div
ref={'post_body_' + post.id}
className={this.getClassName(post, isSystemMessage)}
>
<div className='post__content'>
{profilePicContainer}
<div>
<div className='post__header'>
<div className='col col__name'>
<strong>{userProfile}</strong>
</div>
{botIndicator}
<div className='col'>
{this.renderTimeTag(post, timeOptions)}
{pinnedBadge}
<PostFlagIcon
idPrefix={'rhsCommentFlag'}
idCount={idCount}
postId={post.id}
isFlagged={this.props.isFlagged}
isEphemeral={isEphemeral}
/>
{visibleMessage}
</div>
{options}
</div>
<div className='post__body' >
<div className={postClass}>
{failedPostOptions}
<PostMessageContainer
post={post}
isRHS={true}
hasMention={true}
/>
</div>
{fileAttachment}
<ReactionListContainer post={post}/>
</div>
</div>
</div>
</div>
);
}
}