diff options
Diffstat (limited to 'webapp')
-rw-r--r-- | webapp/components/post_view/components/post.jsx | 32 | ||||
-rw-r--r-- | webapp/components/post_view/components/post_header.jsx | 8 | ||||
-rw-r--r-- | webapp/components/profile_picture.jsx | 72 | ||||
-rw-r--r-- | webapp/components/profile_popover.jsx | 224 | ||||
-rw-r--r-- | webapp/components/rhs_comment.jsx | 72 | ||||
-rw-r--r-- | webapp/components/rhs_root_post.jsx | 56 | ||||
-rw-r--r-- | webapp/components/rhs_thread.jsx | 15 | ||||
-rw-r--r-- | webapp/components/search_results.jsx | 32 | ||||
-rw-r--r-- | webapp/components/search_results_item.jsx | 8 | ||||
-rw-r--r-- | webapp/components/user_profile.jsx | 174 |
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 |