Files
mattermost/webapp/components/rhs_thread.jsx
George Goldberg a857cf18f4 PLT-4860 Make ProfilePopover into it's own component and use it consistently everywhere (#4701)
* PLT-4860 - Use same User Popover everywhere.

Refactor out the ProfilePopover into it's own component and give it the
union of all the features of the previous two implementations, and make
sure all the necessary data for it to work consistently everywhere is
provided through the props wherever it is used.

* Don't show popover for webhook posts in main view.

* No popover on RHS when it's a webhook post.

* Fix style.

* Don't send in user when it's a system message.

* Fix some duplication of code.
2016-12-22 14:19:19 -03:00

397 lines
14 KiB
JavaScript

// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import SearchBox from './search_bar.jsx';
import CreateComment from './create_comment.jsx';
import RhsHeaderPost from './rhs_header_post.jsx';
import RootPost from './rhs_root_post.jsx';
import Comment from './rhs_comment.jsx';
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';
import Constants from 'utils/constants.jsx';
const Preferences = Constants.Preferences;
import $ from 'jquery';
import React from 'react';
import Scrollbars from 'react-custom-scrollbars';
export function renderView(props) {
return (
<div
{...props}
className='scrollbar--view'
/>);
}
export function renderThumbHorizontal(props) {
return (
<div
{...props}
className='scrollbar--horizontal'
/>);
}
export function renderThumbVertical(props) {
return (
<div
{...props}
className='scrollbar--vertical'
/>);
}
export default class RhsThread extends React.Component {
constructor(props) {
super(props);
this.mounted = false;
this.onPostChange = this.onPostChange.bind(this);
this.onUserChange = this.onUserChange.bind(this);
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();
state.windowWidth = Utils.windowWidth();
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.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;
}
componentDidMount() {
PostStore.addSelectedPostChangeListener(this.onPostChange);
PostStore.addChangeListener(this.onPostChange);
PreferenceStore.addChangeListener(this.onPreferenceChange);
UserStore.addChangeListener(this.onUserChange);
UserStore.addStatusesChangeListener(this.onStatusChange);
WebrtcStore.addBusyListener(this.onBusy);
this.scrollToBottom();
window.addEventListener('resize', this.handleResize);
this.mounted = true;
}
componentWillUnmount() {
PostStore.removeSelectedPostChangeListener(this.onPostChange);
PostStore.removeChangeListener(this.onPostChange);
PreferenceStore.removeChangeListener(this.onPreferenceChange);
UserStore.removeChangeListener(this.onUserChange);
UserStore.removeStatusesChangeListener(this.onStatusChange);
WebrtcStore.removeBusyListener(this.onBusy);
window.removeEventListener('resize', this.handleResize);
this.mounted = false;
}
componentDidUpdate(prevProps, prevState) {
const prevPostsArray = prevState.postsArray || [];
const curPostsArray = this.state.postsArray || [];
if (prevPostsArray.length >= curPostsArray.length) {
return;
}
const curLastPost = curPostsArray[curPostsArray.length - 1];
if (curLastPost.user_id === UserStore.getCurrentId()) {
this.scrollToBottom();
}
}
shouldComponentUpdate(nextProps, nextState) {
if (!Utils.areObjectsEqual(nextState.statuses, this.state.statuses)) {
return true;
}
if (!Utils.areObjectsEqual(nextState.postsArray, this.state.postsArray)) {
return true;
}
if (!Utils.areObjectsEqual(nextState.selected, this.state.selected)) {
return true;
}
if (nextState.compactDisplay !== this.state.compactDisplay) {
return true;
}
if (nextProps.useMilitaryTime !== this.props.useMilitaryTime) {
return true;
}
if (nextState.previewsCollapsed !== this.state.previewsCollapsed) {
return true;
}
if (!Utils.areObjectsEqual(nextState.flaggedPosts, this.state.flaggedPosts)) {
return true;
}
if (!Utils.areObjectsEqual(nextState.profiles, this.state.profiles)) {
return true;
}
if (!Utils.areObjectsEqual(nextProps.currentUser, this.props.currentUser)) {
return true;
}
if (nextState.isBusy !== this.state.isBusy) {
return true;
}
return false;
}
forceUpdateInfo() {
if (this.state.postList) {
for (var postId in this.state.postList.posts) {
if (this.refs[postId]) {
this.refs[postId].forceUpdate();
}
}
}
}
handleResize() {
this.setState({
windowWidth: Utils.windowWidth(),
windowHeight: Utils.windowHeight()
});
}
onPreferenceChange(category) {
let previewSuffix = '';
if (category === Preferences.CATEGORY_DISPLAY_SETTINGS) {
previewSuffix = '_' + Utils.generateId();
}
this.setState({
compactDisplay: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.MESSAGE_DISPLAY, Preferences.MESSAGE_DISPLAY_DEFAULT) === Preferences.MESSAGE_DISPLAY_COMPACT,
flaggedPosts: PreferenceStore.getCategory(Constants.Preferences.CATEGORY_FLAGGED_POST),
previewsCollapsed: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.COLLAPSE_DISPLAY, 'false') + previewSuffix
});
this.forceUpdateInfo();
}
onPostChange() {
if (this.mounted) {
this.setState(this.getPosts());
}
}
onStatusChange() {
this.setState({statuses: Object.assign({}, UserStore.getStatuses())});
}
onBusy(isBusy) {
this.setState({isBusy});
}
getPosts() {
const selected = PostStore.getSelectedPost();
const posts = PostStore.getSelectedPostThread();
const postsArray = [];
for (const id in posts) {
if (posts.hasOwnProperty(id)) {
const cpost = posts[id];
if (cpost.root_id === selected.id) {
postsArray.push(cpost);
}
}
}
// sort failed posts to bottom, followed by pending, and then regular posts
postsArray.sort((a, b) => {
if ((a.state === Constants.POST_LOADING || a.state === Constants.POST_FAILED) && (b.state !== Constants.POST_LOADING && b.state !== Constants.POST_FAILED)) {
return 1;
}
if ((a.state !== Constants.POST_LOADING && a.state !== Constants.POST_FAILED) && (b.state === Constants.POST_LOADING || b.state === Constants.POST_FAILED)) {
return -1;
}
if (a.state === Constants.POST_LOADING && b.state === Constants.POST_FAILED) {
return -1;
}
if (a.state === Constants.POST_FAILED && b.state === Constants.POST_LOADING) {
return 1;
}
if (a.create_at < b.create_at) {
return -1;
}
if (a.create_at > b.create_at) {
return 1;
}
return 0;
});
return {postsArray, selected};
}
onUserChange() {
const profiles = JSON.parse(JSON.stringify(UserStore.getProfiles()));
this.setState({profiles});
}
scrollToBottom() {
if ($('.post-right__scroll')[0]) {
$('.post-right__scroll').parent().scrollTop($('.post-right__scroll')[0].scrollHeight);
}
}
render() {
const postsArray = this.state.postsArray;
const selected = this.state.selected;
const profiles = this.state.profiles || {};
if (postsArray == null || selected == null) {
return (
<div/>
);
}
var currentId = UserStore.getCurrentId();
var searchForm;
if (currentId != null) {
searchForm = <SearchBox/>;
}
let profile;
if (UserStore.getCurrentId() === selected.user_id) {
profile = this.props.currentUser;
} else {
profile = profiles[selected.user_id];
}
let isRootFlagged = false;
if (this.state.flaggedPosts) {
isRootFlagged = this.state.flaggedPosts.get(selected.id) === 'true';
}
let rootStatus = 'offline';
if (this.state.statuses) {
rootStatus = this.state.statuses[selected.user_id] || 'offline';
}
return (
<div className='post-right__container'>
<FileUploadOverlay overlayType='right'/>
<div className='search-bar__container sidebar--right__search-header'>{searchForm}</div>
<div className='sidebar-right__body'>
<RhsHeaderPost
fromFlaggedPosts={this.props.fromFlaggedPosts}
fromSearch={this.props.fromSearch}
isWebrtc={this.props.isWebrtc}
isMentionSearch={this.props.isMentionSearch}
toggleSize={this.props.toggleSize}
shrink={this.props.shrink}
/>
<Scrollbars
autoHide={true}
autoHideTimeout={500}
autoHideDuration={500}
renderThumbHorizontal={renderThumbHorizontal}
renderThumbVertical={renderThumbVertical}
renderView={renderView}
>
<div className='post-right__scroll'>
<RootPost
ref={selected.id}
post={selected}
commentCount={postsArray.length}
user={profile}
currentUser={this.props.currentUser}
compactDisplay={this.state.compactDisplay}
useMilitaryTime={this.props.useMilitaryTime}
isFlagged={isRootFlagged}
status={rootStatus}
previewCollapsed={this.state.previewsCollapsed}
isBusy={this.state.isBusy}
/>
<div className='post-right-comments-container'>
{postsArray.map((comPost) => {
let p;
if (UserStore.getCurrentId() === comPost.user_id) {
p = UserStore.getCurrentUser();
} else {
p = profiles[comPost.user_id];
}
let isFlagged = false;
if (this.state.flaggedPosts) {
isFlagged = this.state.flaggedPosts.get(comPost.id) === 'true';
}
let status = 'offline';
if (this.state.statuses) {
status = this.state.statuses[p.id] || 'offline';
}
const keyPrefix = comPost.id ? comPost.id : comPost.pending_post_id;
return (
<Comment
ref={comPost.id}
key={keyPrefix + 'commentKey'}
post={comPost}
user={p}
currentUser={this.props.currentUser}
compactDisplay={this.state.compactDisplay}
useMilitaryTime={this.props.useMilitaryTime}
isFlagged={isFlagged}
status={status}
isBusy={this.state.isBusy}
/>
);
})}
</div>
<div className='post-create__container'>
<CreateComment
channelId={selected.channel_id}
rootId={selected.id}
latestPostId={postsArray.length > 0 ? postsArray[postsArray.length - 1].id : selected.id}
/>
</div>
</div>
</Scrollbars>
</div>
</div>
);
}
}
RhsThread.defaultProps = {
fromSearch: '',
isMentionSearch: false
};
RhsThread.propTypes = {
fromSearch: React.PropTypes.string,
fromFlaggedPosts: React.PropTypes.bool,
isWebrtc: React.PropTypes.bool,
isMentionSearch: React.PropTypes.bool,
currentUser: React.PropTypes.object.isRequired,
useMilitaryTime: React.PropTypes.bool.isRequired,
toggleSize: React.PropTypes.func,
shrink: React.PropTypes.func
};