Skip to content

Commit

Permalink
post message at-mention profile pop-over
Browse files Browse the repository at this point in the history
  • Loading branch information
saturninoabril committed Apr 18, 2017
1 parent 8aab290 commit e71cb6f
Show file tree
Hide file tree
Showing 22 changed files with 302 additions and 87 deletions.
2 changes: 1 addition & 1 deletion webapp/components/add_users_to_team.jsx
Expand Up @@ -2,7 +2,7 @@
// See License.txt for license information.

import MultiSelect from 'components/multiselect/multiselect.jsx';
import ProfilePicture from 'components/profile_picture.jsx';
import ProfilePicture from 'components/profile_popover/picture_profile_popover.jsx';

import {addUsersToTeam} from 'actions/team_actions.jsx';
import {searchUsersNotInTeam} from 'actions/user_actions.jsx';
Expand Down
2 changes: 1 addition & 1 deletion webapp/components/more_direct_channels.jsx
Expand Up @@ -2,7 +2,7 @@
// See License.txt for license information.

import MultiSelect from 'components/multiselect/multiselect.jsx';
import ProfilePicture from 'components/profile_picture.jsx';
import ProfilePicture from 'components/profile_popover/picture_profile_popover.jsx';

import {searchUsers} from 'actions/user_actions.jsx';
import {openDirectChannelToUser, openGroupChannelToUsers} from 'actions/channel_actions.jsx';
Expand Down
2 changes: 1 addition & 1 deletion webapp/components/popover_list_members.jsx
@@ -1,7 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.

import ProfilePicture from 'components/profile_picture.jsx';
import ProfilePicture from 'components/profile_popover/picture_profile_popover.jsx';

import TeamStore from 'stores/team_store.jsx';
import UserStore from 'stores/user_store.jsx';
Expand Down
2 changes: 1 addition & 1 deletion webapp/components/post_view/components/post.jsx
Expand Up @@ -3,7 +3,7 @@

import PostHeader from './post_header.jsx';
import PostBody from './post_body.jsx';
import ProfilePicture from 'components/profile_picture.jsx';
import ProfilePicture from 'components/profile_popover/picture_profile_popover.jsx';

import Constants from 'utils/constants.jsx';
const ActionTypes = Constants.ActionTypes;
Expand Down
2 changes: 1 addition & 1 deletion webapp/components/post_view/components/post_header.jsx
@@ -1,7 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.

import UserProfile from 'components/user_profile.jsx';
import UserProfile from 'components/profile_popover/username_profile_popover.jsx';
import PostInfo from './post_info.jsx';
import {FormattedMessage} from 'react-intl';

Expand Down
Expand Up @@ -39,6 +39,7 @@ export default class PostMessageContainer extends React.Component {
enableFormatting: PreferenceStore.getBool(Preferences.CATEGORY_ADVANCED_SETTINGS, 'formatting', true),
mentionKeys,
usernameMap: UserStore.getProfilesUsernameMap(),
liteUsernameMap: UserStore.getLiteProfilesUsernameMap(),
channelNamesMap: ChannelStore.getChannelNamesMap(),
team: TeamStore.getCurrent()
};
Expand Down Expand Up @@ -76,7 +77,8 @@ export default class PostMessageContainer extends React.Component {

this.setState({
mentionKeys,
usernameMap: UserStore.getProfilesUsernameMap()
usernameMap: UserStore.getProfilesUsernameMap(),
liteUsernameMap: UserStore.getLiteProfilesUsernameMap()
});
}

Expand All @@ -96,6 +98,7 @@ export default class PostMessageContainer extends React.Component {
enableFormatting={this.state.enableFormatting}
mentionKeys={this.state.mentionKeys}
usernameMap={this.state.usernameMap}
liteUsernameMap={this.state.liteUsernameMap}
channelNamesMap={this.state.channelNamesMap}
team={this.state.team}
/>
Expand Down
12 changes: 11 additions & 1 deletion webapp/components/post_view/components/post_message_view.jsx
Expand Up @@ -4,6 +4,8 @@
import React from 'react';
import {FormattedMessage} from 'react-intl';

import AtMentionProfile from 'components/profile_popover/atmention_profile_popover.jsx';

import Constants from 'utils/constants.jsx';
import * as PostUtils from 'utils/post_utils.jsx';
import * as TextFormatting from 'utils/text_formatting.jsx';
Expand All @@ -20,6 +22,7 @@ export default class PostMessageView extends React.Component {
enableFormatting: React.PropTypes.bool.isRequired,
mentionKeys: React.PropTypes.arrayOf(React.PropTypes.string).isRequired,
usernameMap: React.PropTypes.object.isRequired,
liteUsernameMap: React.PropTypes.object.isRequired,
channelNamesMap: React.PropTypes.object.isRequired,
team: React.PropTypes.object.isRequired,
isLastPost: React.PropTypes.bool
Expand Down Expand Up @@ -55,6 +58,10 @@ export default class PostMessageView extends React.Component {
return true;
}

if (!Utils.areObjectsEqual(nextProps.liteUsernameMap, this.props.liteUsernameMap)) {
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.
// The same choice (and reasoning) is also applied to the this.props.channelNamesMap.
Expand Down Expand Up @@ -111,14 +118,17 @@ export default class PostMessageView extends React.Component {
return <div>{renderedSystemMessage}</div>;
}

const htmlFormattedText = TextFormatting.formatText(this.props.post.message, options);
const postMessageComponent = Utils.postMessageHtmlToComponent(htmlFormattedText, AtMentionProfile, this.props.liteUsernameMap);

return (
<div>
<span
id={this.props.isLastPost ? 'lastPostMessageText' : null}
className='post-message__text'
onClick={Utils.handleFormattedTextClick}
dangerouslySetInnerHTML={{__html: TextFormatting.formatText(this.props.post.message, options)}}
/>
{postMessageComponent}
{this.renderEditedIndicator()}
</div>
);
Expand Down
95 changes: 95 additions & 0 deletions webapp/components/profile_popover/atmention_profile_popover.jsx
@@ -0,0 +1,95 @@
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.

import ProfilePopover from './profile_popover.jsx';
import * as Utils from 'utils/utils.jsx';
import Client from 'client/web_client.jsx';

import {OverlayTrigger} from 'react-bootstrap';

import React from 'react';

export default class AtMentionProfile extends React.Component {
constructor(props) {
super(props);

this.hideProfilePopover = this.hideProfilePopover.bind(this);
}

shouldComponentUpdate(nextProps) {
if (!Utils.areObjectsEqual(nextProps.user, this.props.user)) {
return true;
}

if (nextProps.overwriteImage !== this.props.overwriteImage) {
return true;
}

if (nextProps.disablePopover !== this.props.disablePopover) {
return true;
}

if (nextProps.displayNameType !== this.props.displayNameType) {
return true;
}

if (nextProps.status !== this.props.status) {
return true;
}

if (nextProps.isBusy !== this.props.isBusy) {
return true;
}

return false;
}

hideProfilePopover() {
this.refs.overlay.hide();
}

render() {
let profileImg = '';
if (this.props.user) {
profileImg = Client.getUsersRoute() + '/' + this.props.user.id + '/image?time=' + this.props.user.last_picture_update;
}

if (this.props.disablePopover) {
return <a className='mention-link'>{'@' + this.props.username}</a>;
}

return (
<OverlayTrigger
ref='overlay'
trigger='click'
placement='right'
rootClose={true}
overlay={
<ProfilePopover
user={this.props.user}
src={profileImg}
status={this.props.status}
isBusy={this.props.isBusy}
hide={this.hideProfilePopover}
/>
}
>
<a className='mention-link'>{'@' + this.props.username}</a>
</OverlayTrigger>
);
}
}

AtMentionProfile.defaultProps = {
overwriteImage: '',
disablePopover: false
};
AtMentionProfile.propTypes = {
user: React.PropTypes.object.isRequired,
username: React.PropTypes.string.isRequired,
overwriteImage: React.PropTypes.string,
disablePopover: React.PropTypes.bool,
displayNameType: React.PropTypes.string,
status: React.PropTypes.string,
isBusy: React.PropTypes.bool
};
@@ -1,10 +1,11 @@
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.

import ProfilePopover from './profile_popover.jsx';
import * as Utils from 'utils/utils.jsx';

import React from 'react';
import StatusIcon from './status_icon.jsx';
import StatusIcon from 'components/status_icon.jsx';
import {OverlayTrigger} from 'react-bootstrap';

export default class ProfilePicture extends React.Component {
Expand Down

0 comments on commit e71cb6f

Please sign in to comment.