summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--webapp/components/post_view/components/post.jsx32
-rw-r--r--webapp/components/post_view/components/post_header.jsx8
-rw-r--r--webapp/components/profile_picture.jsx72
-rw-r--r--webapp/components/profile_popover.jsx224
-rw-r--r--webapp/components/rhs_comment.jsx72
-rw-r--r--webapp/components/rhs_root_post.jsx56
-rw-r--r--webapp/components/rhs_thread.jsx15
-rw-r--r--webapp/components/search_results.jsx32
-rw-r--r--webapp/components/search_results_item.jsx8
-rw-r--r--webapp/components/user_profile.jsx174
10 files changed, 440 insertions, 253 deletions
diff --git a/webapp/components/post_view/components/post.jsx b/webapp/components/post_view/components/post.jsx
index ce6ce7d2a..f052ac4ae 100644
--- a/webapp/components/post_view/components/post.jsx
+++ b/webapp/components/post_view/components/post.jsx
@@ -194,9 +194,18 @@ export default class Post extends React.Component {
src={PostUtils.getProfilePicSrcForPost(post, timestamp)}
status={status}
user={this.props.user}
+ isBusy={this.props.isBusy}
/>
);
+ if (post.props && post.props.from_webhook) {
+ profilePic = (
+ <ProfilePicture
+ src={PostUtils.getProfilePicSrcForPost(post, timestamp)}
+ />
+ );
+ }
+
if (PostUtils.isSystemMessage(post)) {
profilePic = (
<span
@@ -215,13 +224,22 @@ export default class Post extends React.Component {
if (this.props.compactDisplay) {
compactClass = 'post--compact';
- profilePic = (
- <ProfilePicture
- src=''
- status={status}
- user={this.props.user}
- />
- );
+ if (post.props && post.props.from_webhook) {
+ profilePic = (
+ <ProfilePicture
+ src=''
+ status={status}
+ isBusy={this.props.isBusy}
+ user={this.props.user}
+ />
+ );
+ } else {
+ profilePic = (
+ <ProfilePicture
+ src=''
+ />
+ );
+ }
}
const profilePicContainer = (<div className='post__img'>{profilePic}</div>);
diff --git a/webapp/components/post_view/components/post_header.jsx b/webapp/components/post_view/components/post_header.jsx
index ff691c12b..4043460a3 100644
--- a/webapp/components/post_view/components/post_header.jsx
+++ b/webapp/components/post_view/components/post_header.jsx
@@ -40,6 +40,14 @@ export default class PostHeader extends React.Component {
disablePopover={true}
/>
);
+ } else {
+ userProfile = (
+ <UserProfile
+ user={this.props.user}
+ displayNameType={this.props.displayNameType}
+ disablePopover={true}
+ />
+ );
}
botIndicator = <li className='bot-indicator'>{Constants.BOT_NAME}</li>;
diff --git a/webapp/components/profile_picture.jsx b/webapp/components/profile_picture.jsx
index 8e14fa5fa..17a4ddc65 100644
--- a/webapp/components/profile_picture.jsx
+++ b/webapp/components/profile_picture.jsx
@@ -1,12 +1,17 @@
// Copyright (c) 2016 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 UserStore from 'stores/user_store.jsx';
+
import React from 'react';
-import {Popover, OverlayTrigger} from 'react-bootstrap';
+import {OverlayTrigger} from 'react-bootstrap';
export default class ProfilePicture extends React.Component {
shouldComponentUpdate(nextProps) {
+ if (!Utils.areObjectsEqual(nextProps.user, this.props.user)) {
+ return true;
+ }
+
if (nextProps.src !== this.props.src) {
return true;
}
@@ -23,71 +28,31 @@ export default class ProfilePicture extends React.Component {
return true;
}
+ if (nextProps.isBusy !== this.props.isBusy) {
+ return true;
+ }
+
return false;
}
render() {
- let email = '';
let statusClass = '';
if (this.props.status) {
statusClass = 'status-' + this.props.status;
}
if (this.props.user) {
- email = this.props.user.email;
- var dataContent = [];
- dataContent.push(
- <img
- className='user-popover__image'
- src={this.props.src}
- height='128'
- width='128'
- key='user-popover-image'
- />
- );
- const fullname = Utils.getFullName(this.props.user);
- if (fullname) {
- dataContent.push(
- <div
- data-toggle='tooltip'
- title={fullname}
- key='user-popover-fullname'
- >
- <p
- className='text-nowrap'
- >
- {fullname}
- </p>
- </div>
- );
- }
- if (global.window.mm_config.ShowEmailAddress === 'true' || UserStore.isSystemAdminForCurrentUser() || this.props.user.id === UserStore.getCurrentId()) {
- dataContent.push(
- <div
- data-toggle='tooltip'
- title={email}
- key='user-popover-email'
- >
- <a
- href={'mailto:' + email}
- className='text-nowrap text-lowercase user-popover__email'
- >
- {email}
- </a>
- </div>
- );
- }
return (
<OverlayTrigger
trigger='click'
placement='right'
rootClose={true}
overlay={
- <Popover
- title={'@' + this.props.user.username}
- id='user-profile-popover'
- >
- {dataContent}
- </Popover>
+ <ProfilePopover
+ user={this.props.user}
+ src={this.props.src}
+ status={this.props.status}
+ isBusy={this.props.isBusy}
+ />
}
>
<span className={`status-wrapper ${statusClass}`}>
@@ -123,5 +88,6 @@ ProfilePicture.propTypes = {
status: React.PropTypes.string,
width: React.PropTypes.string,
height: React.PropTypes.string,
- user: React.PropTypes.object
+ user: React.PropTypes.object,
+ isBusy: React.PropTypes.bool
};
diff --git a/webapp/components/profile_popover.jsx b/webapp/components/profile_popover.jsx
new file mode 100644
index 000000000..a342f312f
--- /dev/null
+++ b/webapp/components/profile_popover.jsx
@@ -0,0 +1,224 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import * as Utils from 'utils/utils.jsx';
+import UserStore from 'stores/user_store.jsx';
+import WebrtcStore from 'stores/webrtc_store.jsx';
+import * as GlobalActions from 'actions/global_actions.jsx';
+import * as WebrtcActions from 'actions/webrtc_actions.jsx';
+import Constants from 'utils/constants.jsx';
+const UserStatuses = Constants.UserStatuses;
+const PreReleaseFeatures = Constants.PRE_RELEASE_FEATURES;
+
+import {Popover, OverlayTrigger, Tooltip} from 'react-bootstrap';
+import {FormattedMessage} from 'react-intl';
+import React from 'react';
+
+export default class ProfilePopover extends React.Component {
+ constructor(props) {
+ super(props);
+
+ this.initWebrtc = this.initWebrtc.bind(this);
+ this.state = {
+ currentUserId: UserStore.getCurrentId()
+ };
+ }
+ shouldComponentUpdate(nextProps) {
+ if (!Utils.areObjectsEqual(nextProps.user, this.props.user)) {
+ return true;
+ }
+
+ if (nextProps.src !== this.props.src) {
+ return true;
+ }
+
+ if (nextProps.status !== this.props.status) {
+ return true;
+ }
+
+ if (nextProps.isBusy !== this.props.isBusy) {
+ return true;
+ }
+
+ // React-Bootstrap Forwarded Props from OverlayTrigger to Popover
+ if (nextProps.arrowOffsetLeft !== this.props.arrowOffsetLeft) {
+ return true;
+ }
+
+ if (nextProps.arrowOffsetTop !== this.props.arrowOffsetTop) {
+ return true;
+ }
+
+ if (nextProps.positionLeft !== this.props.positionLeft) {
+ return true;
+ }
+
+ if (nextProps.positionTop !== this.props.positionTop) {
+ return true;
+ }
+
+ return false;
+ }
+
+ initWebrtc() {
+ if (this.props.status !== UserStatuses.OFFLINE && !WebrtcStore.isBusy()) {
+ GlobalActions.emitCloseRightHandSide();
+ WebrtcActions.initWebrtc(this.props.user.id, true);
+ }
+ }
+
+ render() {
+ const popoverProps = Object.assign({}, this.props);
+ delete popoverProps.user;
+ delete popoverProps.src;
+ delete popoverProps.status;
+ delete popoverProps.isBusy;
+
+ let webrtc;
+ const userMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
+
+ const webrtcEnabled = global.mm_config.EnableWebrtc === 'true' && userMedia && Utils.isFeatureEnabled(PreReleaseFeatures.WEBRTC_PREVIEW);
+
+ if (webrtcEnabled && this.props.user.id !== this.state.currentUserId) {
+ const isOnline = this.props.status !== UserStatuses.OFFLINE;
+ let webrtcMessage;
+ let circleClass = 'offline';
+ if (isOnline && !this.props.isBusy) {
+ circleClass = '';
+ webrtcMessage = (
+ <FormattedMessage
+ id='user_profile.webrtc.call'
+ defaultMessage='Start Video Call'
+ />
+ );
+ } else if (this.props.isBusy) {
+ webrtcMessage = (
+ <FormattedMessage
+ id='user_profile.webrtc.unavailable'
+ defaultMessage='New call unavailable until your existing call ends'
+ />
+ );
+ } else {
+ webrtcMessage = (
+ <FormattedMessage
+ id='user_profile.webrtc.offline'
+ defaultMessage='The user is offline'
+ />
+ );
+ }
+
+ const webrtcTooltip = (
+ <Tooltip id='webrtcTooltip'>{webrtcMessage}</Tooltip>
+ );
+
+ webrtc = (
+ <div
+ className='webrtc__user-profile'
+ key='makeCall'
+ >
+ <a
+ href='#'
+ onClick={() => this.initWebrtc()}
+ disabled={!isOnline}
+ >
+ <OverlayTrigger
+ delayShow={Constants.WEBRTC_TIME_DELAY}
+ placement='top'
+ overlay={webrtcTooltip}
+ >
+ <div
+ id='webrtc-btn'
+ className={'webrtc__button ' + circleClass}
+ >
+ <span dangerouslySetInnerHTML={{__html: Constants.VIDEO_ICON}}/>
+ </div>
+ </OverlayTrigger>
+ </a>
+ </div>
+ );
+ }
+
+ var dataContent = [];
+ dataContent.push(
+ <img
+ className='user-popover__image'
+ src={this.props.src}
+ height='128'
+ width='128'
+ key='user-popover-image'
+ />
+ );
+
+ const fullname = Utils.getFullName(this.props.user);
+ if (fullname) {
+ dataContent.push(
+ <div
+ data-toggle='tooltip'
+ title={fullname}
+ key='user-popover-fullname'
+ >
+ <p
+ className='text-nowrap'
+ >
+ {fullname}
+ </p>
+ </div>
+ );
+ }
+
+ if (this.props.user.position) {
+ const position = this.props.user.position.substring(0, Constants.MAX_POSITION_LENGTH);
+ dataContent.push(
+ <div
+ data-toggle='tooltip'
+ title={position}
+ key='user-popover-position'
+ >
+ <p
+ className='text-nowrap'
+ >
+ {position}
+ </p>
+ </div>
+ );
+ }
+
+ dataContent.push(webrtc);
+
+ const email = this.props.user.email;
+ if (global.window.mm_config.ShowEmailAddress === 'true' || UserStore.isSystemAdminForCurrentUser() || this.props.user === UserStore.getCurrentUser()) {
+ dataContent.push(
+ <div
+ data-toggle='tooltip'
+ title={email}
+ key='user-popover-email'
+ >
+ <a
+ href={'mailto:' + email}
+ className='text-nowrap text-lowercase user-popover__email'
+ >
+ {email}
+ </a>
+ </div>
+ );
+ }
+
+ return (
+ <Popover
+ {...popoverProps}
+ title={'@' + this.props.user.username}
+ id='user-profile-popover'
+ >
+ {dataContent}
+ </Popover>
+ );
+ }
+}
+
+ProfilePopover.propTypes = Object.assign({
+ src: React.PropTypes.string.isRequired,
+ user: React.PropTypes.object.isRequired,
+ status: React.PropTypes.string.isRequired,
+ isBusy: React.PropTypes.bool.isRequired
+}, Popover.propTypes);
+delete ProfilePopover.propTypes.id;
diff --git a/webapp/components/rhs_comment.jsx b/webapp/components/rhs_comment.jsx
index c699fb353..8b7642fd8 100644
--- a/webapp/components/rhs_comment.jsx
+++ b/webapp/components/rhs_comment.jsx
@@ -66,6 +66,10 @@ export default class RhsComment extends React.Component {
return true;
}
+ if (nextProps.isBusy !== this.props.isBusy) {
+ return true;
+ }
+
if (nextProps.compactDisplay !== this.props.compactDisplay) {
return true;
}
@@ -237,10 +241,20 @@ export default class RhsComment extends React.Component {
var timestamp = this.props.currentUser.update_at;
+ let status = this.props.status;
+ if (post.props && post.props.from_webhook === 'true') {
+ status = null;
+ }
+
let botIndicator;
let userProfile = (
- <UserProfile user={this.props.user}/>
+ <UserProfile
+ user={this.props.user}
+ status={status}
+ isBusy={this.props.isBusy}
+ />
);
+
if (post.props && post.props.from_webhook) {
if (post.props.override_username && global.window.mm_config.EnablePostUsernameOverride === 'true') {
userProfile = (
@@ -250,9 +264,17 @@ export default class RhsComment extends React.Component {
disablePopover={true}
/>
);
+ } else {
+ userProfile = (
+ <UserProfile
+ user={this.props.user}
+ disablePopover={true}
+ />
+ );
}
- botIndicator = <li className='bot-indicator'>{Constants.BOT_NAME}</li>;
- } else if (isSystemMessage) {
+
+ botIndicator = <li className='col col__name bot-indicator'>{'BOT'}</li>;
+ } else if (PostUtils.isSystemMessage(post)) {
userProfile = (
<UserProfile
user={{}}
@@ -292,11 +314,6 @@ export default class RhsComment extends React.Component {
systemMessageClass = 'post--system';
}
- let status = this.props.status;
- if (post.props && post.props.from_webhook === 'true') {
- status = null;
- }
-
let profilePic = (
<ProfilePicture
src={PostUtils.getProfilePicSrcForPost(post, timestamp)}
@@ -304,10 +321,21 @@ export default class RhsComment extends React.Component {
width='36'
height='36'
user={this.props.user}
+ isBusy={this.props.isBusy}
/>
);
- if (isSystemMessage) {
+ if (post.props && post.props.from_webhook) {
+ profilePic = (
+ <ProfilePicture
+ src={PostUtils.getProfilePicSrcForPost(post, timestamp)}
+ width='36'
+ height='36'
+ />
+ );
+ }
+
+ if (PostUtils.isSystemMessage(post)) {
profilePic = (
<span
className='icon'
@@ -320,13 +348,22 @@ export default class RhsComment extends React.Component {
if (this.props.compactDisplay) {
compactClass = 'post--compact';
- profilePic = (
- <ProfilePicture
- src=''
- status={status}
- user={this.props.user}
- />
- );
+ if (post.props && post.props.from_webhook) {
+ profilePic = (
+ <ProfilePicture
+ src=''
+ />
+ );
+ } else {
+ profilePic = (
+ <ProfilePicture
+ src=''
+ status={status}
+ user={this.props.user}
+ isBusy={this.props.isBusy}
+ />
+ );
+ }
}
const profilePicContainer = (<div className='post__img'>{profilePic}</div>);
@@ -466,5 +503,6 @@ RhsComment.propTypes = {
compactDisplay: React.PropTypes.bool,
useMilitaryTime: React.PropTypes.bool.isRequired,
isFlagged: React.PropTypes.bool,
- status: React.PropTypes.string
+ status: React.PropTypes.string,
+ isBusy: React.PropTypes.bool
};
diff --git a/webapp/components/rhs_root_post.jsx b/webapp/components/rhs_root_post.jsx
index 4a15127a0..95f5fc1ac 100644
--- a/webapp/components/rhs_root_post.jsx
+++ b/webapp/components/rhs_root_post.jsx
@@ -47,6 +47,10 @@ export default class RhsRootPost extends React.Component {
return true;
}
+ if (nextProps.isBusy !== this.props.isBusy) {
+ return true;
+ }
+
if (nextProps.compactDisplay !== this.props.compactDisplay) {
return true;
}
@@ -248,7 +252,13 @@ export default class RhsRootPost extends React.Component {
);
}
- let userProfile = <UserProfile user={user}/>;
+ let userProfile = (
+ <UserProfile
+ user={user}
+ status={this.props.status}
+ isBusy={this.props.isBusy}
+ />
+ );
let botIndicator;
if (post.props && post.props.from_webhook) {
@@ -260,6 +270,13 @@ export default class RhsRootPost extends React.Component {
disablePopover={true}
/>
);
+ } else {
+ userProfile = (
+ <UserProfile
+ user={user}
+ disablePopover={true}
+ />
+ );
}
botIndicator = <li className='col col__name bot-indicator'>{'BOT'}</li>;
@@ -286,9 +303,20 @@ export default class RhsRootPost extends React.Component {
width='36'
height='36'
user={this.props.user}
+ isBusy={this.props.isBusy}
/>
);
+ if (post.props && post.props.from_webhook) {
+ profilePic = (
+ <ProfilePicture
+ src={PostUtils.getProfilePicSrcForPost(post, timestamp)}
+ width='36'
+ height='36'
+ />
+ );
+ }
+
if (PostUtils.isSystemMessage(post)) {
profilePic = (
<span
@@ -302,13 +330,22 @@ export default class RhsRootPost extends React.Component {
if (this.props.compactDisplay) {
compactClass = 'post--compact';
- profilePic = (
- <ProfilePicture
- src=''
- status={status}
- user={this.props.user}
- />
- );
+ if (post.props && post.props.from_webhook) {
+ profilePic = (
+ <ProfilePicture
+ src=''
+ />
+ );
+ } else {
+ profilePic = (
+ <ProfilePicture
+ src=''
+ status={status}
+ user={this.props.user}
+ isBusy={this.props.isBusy}
+ />
+ );
+ }
}
const profilePicContainer = (<div className='post__img'>{profilePic}</div>);
@@ -424,5 +461,6 @@ RhsRootPost.propTypes = {
useMilitaryTime: React.PropTypes.bool.isRequired,
isFlagged: React.PropTypes.bool,
status: React.PropTypes.string,
- previewCollapsed: React.PropTypes.string
+ previewCollapsed: React.PropTypes.string,
+ isBusy: React.PropTypes.bool
};
diff --git a/webapp/components/rhs_thread.jsx b/webapp/components/rhs_thread.jsx
index 8fd49dd25..5040f83fc 100644
--- a/webapp/components/rhs_thread.jsx
+++ b/webapp/components/rhs_thread.jsx
@@ -11,6 +11,7 @@ import FileUploadOverlay from './file_upload_overlay.jsx';
import PostStore from 'stores/post_store.jsx';
import UserStore from 'stores/user_store.jsx';
import PreferenceStore from 'stores/preference_store.jsx';
+import WebrtcStore from 'stores/webrtc_store.jsx';
import * as Utils from 'utils/utils.jsx';
@@ -56,6 +57,7 @@ export default class RhsThread extends React.Component {
this.forceUpdateInfo = this.forceUpdateInfo.bind(this);
this.onPreferenceChange = this.onPreferenceChange.bind(this);
this.onStatusChange = this.onStatusChange.bind(this);
+ this.onBusy = this.onBusy.bind(this);
this.handleResize = this.handleResize.bind(this);
const state = this.getPosts();
@@ -66,6 +68,7 @@ export default class RhsThread extends React.Component {
state.flaggedPosts = PreferenceStore.getCategory(Constants.Preferences.CATEGORY_FLAGGED_POST);
state.statuses = Object.assign({}, UserStore.getStatuses());
state.previewsCollapsed = PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.COLLAPSE_DISPLAY, 'false');
+ state.isBusy = WebrtcStore.isBusy();
this.state = state;
}
@@ -76,6 +79,7 @@ export default class RhsThread extends React.Component {
PreferenceStore.addChangeListener(this.onPreferenceChange);
UserStore.addChangeListener(this.onUserChange);
UserStore.addStatusesChangeListener(this.onStatusChange);
+ WebrtcStore.addBusyListener(this.onBusy);
this.scrollToBottom();
window.addEventListener('resize', this.handleResize);
@@ -89,6 +93,7 @@ export default class RhsThread extends React.Component {
PreferenceStore.removeChangeListener(this.onPreferenceChange);
UserStore.removeChangeListener(this.onUserChange);
UserStore.removeStatusesChangeListener(this.onStatusChange);
+ WebrtcStore.removeBusyListener(this.onBusy);
window.removeEventListener('resize', this.handleResize);
@@ -147,6 +152,10 @@ export default class RhsThread extends React.Component {
return true;
}
+ if (nextState.isBusy !== this.state.isBusy) {
+ return true;
+ }
+
return false;
}
@@ -191,6 +200,10 @@ export default class RhsThread extends React.Component {
this.setState({statuses: Object.assign({}, UserStore.getStatuses())});
}
+ onBusy(isBusy) {
+ this.setState({isBusy});
+ }
+
getPosts() {
const selected = PostStore.getSelectedPost();
const posts = PostStore.getSelectedPostThread();
@@ -312,6 +325,7 @@ export default class RhsThread extends React.Component {
isFlagged={isRootFlagged}
status={rootStatus}
previewCollapsed={this.state.previewsCollapsed}
+ isBusy={this.state.isBusy}
/>
<div className='post-right-comments-container'>
{postsArray.map((comPost) => {
@@ -345,6 +359,7 @@ export default class RhsThread extends React.Component {
useMilitaryTime={this.props.useMilitaryTime}
isFlagged={isFlagged}
status={status}
+ isBusy={this.state.isBusy}
/>
);
})}
diff --git a/webapp/components/search_results.jsx b/webapp/components/search_results.jsx
index 6396150e7..466665c2c 100644
--- a/webapp/components/search_results.jsx
+++ b/webapp/components/search_results.jsx
@@ -9,6 +9,7 @@ import ChannelStore from 'stores/channel_store.jsx';
import SearchStore from 'stores/search_store.jsx';
import UserStore from 'stores/user_store.jsx';
import PreferenceStore from 'stores/preference_store.jsx';
+import WebrtcStore from 'stores/webrtc_store.jsx';
import * as Utils from 'utils/utils.jsx';
import Constants from 'utils/constants.jsx';
@@ -51,8 +52,10 @@ export default class SearchResults extends React.Component {
this.onChange = this.onChange.bind(this);
this.onUserChange = this.onUserChange.bind(this);
this.onPreferenceChange = this.onPreferenceChange.bind(this);
+ this.onBusy = this.onBusy.bind(this);
this.resize = this.resize.bind(this);
this.onPreferenceChange = this.onPreferenceChange.bind(this);
+ this.onStatusChange = this.onStatusChange.bind(this);
this.handleResize = this.handleResize.bind(this);
const state = getStateFromStores();
@@ -60,6 +63,8 @@ export default class SearchResults extends React.Component {
state.windowHeight = Utils.windowHeight();
state.profiles = JSON.parse(JSON.stringify(UserStore.getProfiles()));
state.compactDisplay = PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.MESSAGE_DISPLAY, Preferences.MESSAGE_DISPLAY_DEFAULT) === Preferences.MESSAGE_DISPLAY_COMPACT;
+ state.isBusy = WebrtcStore.isBusy();
+ state.statuses = Object.assign({}, UserStore.getStatuses());
this.state = state;
}
@@ -70,7 +75,9 @@ export default class SearchResults extends React.Component {
ChannelStore.addChangeListener(this.onChange);
PreferenceStore.addChangeListener(this.onPreferenceChange);
UserStore.addChangeListener(this.onUserChange);
+ UserStore.addStatusesChangeListener(this.onStatusChange);
PreferenceStore.addChangeListener(this.onPreferenceChange);
+ WebrtcStore.addBusyListener(this.onBusy);
this.resize();
window.addEventListener('resize', this.handleResize);
@@ -80,6 +87,10 @@ export default class SearchResults extends React.Component {
}
shouldComponentUpdate(nextProps, nextState) {
+ if (!Utils.areObjectsEqual(nextState.statuses, this.state.statuses)) {
+ return true;
+ }
+
if (!Utils.areObjectsEqual(this.props, nextProps)) {
return true;
}
@@ -92,6 +103,10 @@ export default class SearchResults extends React.Component {
return true;
}
+ if (nextState.isBusy !== this.state.isBusy) {
+ return true;
+ }
+
return false;
}
@@ -106,7 +121,9 @@ export default class SearchResults extends React.Component {
ChannelStore.removeChangeListener(this.onChange);
PreferenceStore.removeChangeListener(this.onPreferenceChange);
UserStore.removeChangeListener(this.onUserChange);
+ UserStore.removeStatusesChangeListener(this.onStatusChange);
PreferenceStore.removeChangeListener(this.onPreferenceChange);
+ WebrtcStore.removeBusyListener(this.onBusy);
window.removeEventListener('resize', this.handleResize);
}
@@ -135,6 +152,14 @@ export default class SearchResults extends React.Component {
this.setState({profiles: JSON.parse(JSON.stringify(UserStore.getProfiles()))});
}
+ onBusy(isBusy) {
+ this.setState({isBusy});
+ }
+
+ onStatusChange() {
+ this.setState({statuses: Object.assign({}, UserStore.getStatuses())});
+ }
+
resize() {
$('#search-items-container').scrollTop(0);
}
@@ -224,6 +249,11 @@ export default class SearchResults extends React.Component {
profile = profiles[post.user_id];
}
+ let status = 'offline';
+ if (this.state.statuses) {
+ status = this.state.statuses[post.user_id] || 'offline';
+ }
+
let isFlagged = false;
if (this.state.flaggedPosts) {
isFlagged = this.state.flaggedPosts.get(post.id) === 'true';
@@ -241,6 +271,8 @@ export default class SearchResults extends React.Component {
useMilitaryTime={this.props.useMilitaryTime}
shrink={this.props.shrink}
isFlagged={isFlagged}
+ isBusy={this.state.isBusy}
+ status={status}
/>
);
}, this);
diff --git a/webapp/components/search_results_item.jsx b/webapp/components/search_results_item.jsx
index ba172b4d3..76681959e 100644
--- a/webapp/components/search_results_item.jsx
+++ b/webapp/components/search_results_item.jsx
@@ -101,6 +101,8 @@ export default class SearchResultsItem extends React.Component {
<ProfilePicture
src={PostUtils.getProfilePicSrcForPost(post, timestamp)}
user={this.props.user}
+ status={this.props.status}
+ isBusy={this.props.isBusy}
/>
);
@@ -265,6 +267,8 @@ export default class SearchResultsItem extends React.Component {
user={user}
overwriteName={overrideUsername}
disablePopover={disableProfilePopover}
+ status={this.props.status}
+ isBusy={this.props.isBusy}
/>
</strong></li>
{botIndicator}
@@ -302,5 +306,7 @@ SearchResultsItem.propTypes = {
term: React.PropTypes.string,
useMilitaryTime: React.PropTypes.bool.isRequired,
shrink: React.PropTypes.func,
- isFlagged: React.PropTypes.bool
+ isFlagged: React.PropTypes.bool,
+ isBusy: React.PropTypes.bool,
+ status: React.PropTypes.string
};
diff --git a/webapp/components/user_profile.jsx b/webapp/components/user_profile.jsx
index 051b8d263..807c5f023 100644
--- a/webapp/components/user_profile.jsx
+++ b/webapp/components/user_profile.jsx
@@ -1,38 +1,15 @@
// Copyright (c) 2015 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 UserStore from 'stores/user_store.jsx';
-import WebrtcStore from 'stores/webrtc_store.jsx';
-import * as GlobalActions from 'actions/global_actions.jsx';
-import * as WebrtcActions from 'actions/webrtc_actions.jsx';
-import Constants from 'utils/constants.jsx';
-const UserStatuses = Constants.UserStatuses;
-const PreReleaseFeatures = Constants.PRE_RELEASE_FEATURES;
-import {Popover, OverlayTrigger, Tooltip} from 'react-bootstrap';
-import {FormattedMessage} from 'react-intl';
+import {OverlayTrigger} from 'react-bootstrap';
import React from 'react';
export default class UserProfile extends React.Component {
- constructor(props) {
- super(props);
-
- this.initWebrtc = this.initWebrtc.bind(this);
- this.state = {
- currentUserId: UserStore.getCurrentId()
- };
- }
-
- initWebrtc() {
- if (this.props.status !== UserStatuses.OFFLINE && !WebrtcStore.isBusy()) {
- GlobalActions.emitCloseRightHandSide();
- WebrtcActions.initWebrtc(this.props.user.id, true);
- }
- }
-
shouldComponentUpdate(nextProps) {
if (!Utils.areObjectsEqual(nextProps.user, this.props.user)) {
return true;
@@ -67,11 +44,9 @@ export default class UserProfile extends React.Component {
render() {
let name = '...';
- let email = '';
let profileImg = '';
if (this.props.user) {
name = Utils.displayUsername(this.props.user.id);
- email = this.props.user.email;
profileImg = Client.getUsersRoute() + '/' + this.props.user.id + '/image?time=' + this.props.user.update_at;
}
@@ -79,155 +54,22 @@ export default class UserProfile extends React.Component {
name = this.props.overwriteName;
}
- if (this.props.overwriteImage) {
- profileImg = this.props.overwriteImage;
- }
-
if (this.props.disablePopover) {
return <div className='user-popover'>{name}</div>;
}
- let webrtc;
- const userMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
-
- const webrtcEnabled = global.mm_config.EnableWebrtc === 'true' && userMedia && Utils.isFeatureEnabled(PreReleaseFeatures.WEBRTC_PREVIEW);
-
- if (webrtcEnabled && this.props.user.id !== this.state.currentUserId) {
- const isOnline = this.props.status !== UserStatuses.OFFLINE;
- let webrtcMessage;
- let circleClass = 'offline';
- if (isOnline && !this.props.isBusy) {
- circleClass = '';
- webrtcMessage = (
- <FormattedMessage
- id='user_profile.webrtc.call'
- defaultMessage='Start Video Call'
- />
- );
- } else if (this.props.isBusy) {
- webrtcMessage = (
- <FormattedMessage
- id='user_profile.webrtc.unavailable'
- defaultMessage='New call unavailable until your existing call ends'
- />
- );
- } else {
- webrtcMessage = (
- <FormattedMessage
- id='user_profile.webrtc.offline'
- defaultMessage='The user is offline'
- />
- );
- }
-
- const webrtcTooltip = (
- <Tooltip id='webrtcTooltip'>{webrtcMessage}</Tooltip>
- );
-
- webrtc = (
- <div
- className='webrtc__user-profile'
- key='makeCall'
- >
- <a
- href='#'
- onClick={() => this.initWebrtc()}
- disabled={!isOnline}
- >
- <OverlayTrigger
- delayShow={Constants.WEBRTC_TIME_DELAY}
- placement='top'
- overlay={webrtcTooltip}
- >
- <div
- id='webrtc-btn'
- className={'webrtc__button ' + circleClass}
- >
- <span dangerouslySetInnerHTML={{__html: Constants.VIDEO_ICON}}/>
- </div>
- </OverlayTrigger>
- </a>
- </div>
- );
- }
-
- var dataContent = [];
- dataContent.push(
- <img
- className='user-popover__image'
- src={profileImg}
- height='128'
- width='128'
- key='user-popover-image'
- />
- );
-
- const fullname = Utils.getFullName(this.props.user);
- if (fullname) {
- dataContent.push(
- <div
- data-toggle='tooltip'
- title={fullname}
- key='user-popover-fullname'
- >
-
- <p
- className='text-nowrap'
- >
- {fullname}
- </p>
- </div>
- );
- }
-
- dataContent.push(webrtc);
-
- if (this.props.user.position) {
- const position = this.props.user.position.substring(0, Constants.MAX_POSITION_LENGTH);
- dataContent.push(
- <div
- data-toggle='tooltip'
- title={position}
- key='user-popover-position'
- >
- <p
- className='text-nowrap'
- >
- {position}
- </p>
- </div>
- );
- }
-
- if (global.window.mm_config.ShowEmailAddress === 'true' || UserStore.isSystemAdminForCurrentUser() || this.props.user === UserStore.getCurrentUser()) {
- dataContent.push(
- <div
- data-toggle='tooltip'
- title={email}
- key='user-popover-email'
- >
- <a
- href={'mailto:' + email}
- className='text-nowrap text-lowercase user-popover__email'
- >
- {email}
- </a>
- </div>
- );
- }
-
return (
<OverlayTrigger
trigger='click'
placement='right'
rootClose={true}
overlay={
- <Popover
- title={'@' + this.props.user.username}
- id='user-profile-popover'
- >
- {dataContent}
- </Popover>
+ <ProfilePopover
+ user={this.props.user}
+ src={profileImg}
+ status={this.props.status}
+ isBusy={this.props.isBusy}
+ />
}
>
<div