diff options
Diffstat (limited to 'web/react/components')
-rw-r--r-- | web/react/components/center_panel.jsx | 13 | ||||
-rw-r--r-- | web/react/components/channel_header.jsx | 15 | ||||
-rw-r--r-- | web/react/components/delete_post_modal.jsx | 4 | ||||
-rw-r--r-- | web/react/components/navbar.jsx | 2 | ||||
-rw-r--r-- | web/react/components/post.jsx | 54 | ||||
-rw-r--r-- | web/react/components/post_body.jsx | 22 | ||||
-rw-r--r-- | web/react/components/post_deleted_modal.jsx | 2 | ||||
-rw-r--r-- | web/react/components/post_header.jsx | 10 | ||||
-rw-r--r-- | web/react/components/posts_view.jsx | 14 | ||||
-rw-r--r-- | web/react/components/posts_view_container.jsx | 11 | ||||
-rw-r--r-- | web/react/components/rhs_comment.jsx | 30 | ||||
-rw-r--r-- | web/react/components/rhs_header_post.jsx | 4 | ||||
-rw-r--r-- | web/react/components/rhs_root_post.jsx | 24 | ||||
-rw-r--r-- | web/react/components/rhs_thread.jsx | 149 | ||||
-rw-r--r-- | web/react/components/search_bar.jsx | 2 | ||||
-rw-r--r-- | web/react/components/search_results_header.jsx | 2 | ||||
-rw-r--r-- | web/react/components/user_profile.jsx | 45 |
17 files changed, 191 insertions, 212 deletions
diff --git a/web/react/components/center_panel.jsx b/web/react/components/center_panel.jsx index 6cb749075..97c615768 100644 --- a/web/react/components/center_panel.jsx +++ b/web/react/components/center_panel.jsx @@ -27,20 +27,24 @@ export default class CenterPanel extends React.Component { this.onPreferenceChange = this.onPreferenceChange.bind(this); this.onChannelChange = this.onChannelChange.bind(this); + this.onUserChange = this.onUserChange.bind(this); const tutorialStep = PreferenceStore.getInt(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), 999); this.state = { showTutorialScreens: tutorialStep === TutorialSteps.INTRO_SCREENS, - showPostFocus: ChannelStore.getPostMode() === ChannelStore.POST_MODE_FOCUS + showPostFocus: ChannelStore.getPostMode() === ChannelStore.POST_MODE_FOCUS, + user: UserStore.getCurrentUser() }; } componentDidMount() { PreferenceStore.addChangeListener(this.onPreferenceChange); ChannelStore.addChangeListener(this.onChannelChange); + UserStore.addChangeListener(this.onUserChange); } componentWillUnmount() { PreferenceStore.removeChangeListener(this.onPreferenceChange); ChannelStore.removeChangeListener(this.onChannelChange); + UserStore.removeChangeListener(this.onUserChange); } onPreferenceChange() { const tutorialStep = PreferenceStore.getInt(Preferences.TUTORIAL_STEP, UserStore.getCurrentId(), 999); @@ -49,6 +53,9 @@ export default class CenterPanel extends React.Component { onChannelChange() { this.setState({showPostFocus: ChannelStore.getPostMode() === ChannelStore.POST_MODE_FOCUS}); } + onUserChange() { + this.setState({user: UserStore.getCurrentUser()}); + } render() { const channel = ChannelStore.getCurrent(); var handleClick = null; @@ -108,7 +115,9 @@ export default class CenterPanel extends React.Component { className='app__content' > <div id='channel-header'> - <ChannelHeader/> + <ChannelHeader + user={this.state.user} + /> </div> {postsContainer} {createPost} diff --git a/web/react/components/channel_header.jsx b/web/react/components/channel_header.jsx index 079a9451e..727f84e8e 100644 --- a/web/react/components/channel_header.jsx +++ b/web/react/components/channel_header.jsx @@ -55,7 +55,6 @@ export default class ChannelHeader extends React.Component { return { channel: ChannelStore.getCurrent(), memberChannel: ChannelStore.getCurrentMember(), - memberTeam: UserStore.getCurrentUser(), users: extraInfo.members, userCount: extraInfo.member_count, searchVisible: SearchStore.getSearchResults() !== null @@ -65,14 +64,12 @@ export default class ChannelHeader extends React.Component { ChannelStore.addChangeListener(this.onListenerChange); ChannelStore.addExtraInfoChangeListener(this.onListenerChange); SearchStore.addSearchChangeListener(this.onListenerChange); - UserStore.addChangeListener(this.onListenerChange); PreferenceStore.addChangeListener(this.onListenerChange); } componentWillUnmount() { ChannelStore.removeChangeListener(this.onListenerChange); ChannelStore.removeExtraInfoChangeListener(this.onListenerChange); SearchStore.removeSearchChangeListener(this.onListenerChange); - UserStore.removeChangeListener(this.onListenerChange); PreferenceStore.removeChangeListener(this.onListenerChange); } onListenerChange() { @@ -101,11 +98,11 @@ export default class ChannelHeader extends React.Component { searchMentions(e) { e.preventDefault(); - const user = UserStore.getCurrentUser(); + const user = this.props.user; let terms = ''; if (user.notify_props && user.notify_props.mention_keys) { - const termKeys = UserStore.getCurrentMentionKeys(); + const termKeys = UserStore.getMentionKeys(user.id); if (user.notify_props.all === 'true' && termKeys.indexOf('@all') !== -1) { termKeys.splice(termKeys.indexOf('@all'), 1); @@ -166,8 +163,8 @@ export default class ChannelHeader extends React.Component { </Popover> ); let channelTitle = channel.display_name; - const currentId = UserStore.getCurrentId(); - const isAdmin = Utils.isAdmin(this.state.memberChannel.roles) || Utils.isAdmin(this.state.memberTeam.roles); + const currentId = this.props.user.id; + const isAdmin = Utils.isAdmin(this.state.memberChannel.roles) || Utils.isAdmin(this.props.user.roles); const isDirect = (this.state.channel.type === 'D'); if (isDirect) { @@ -491,3 +488,7 @@ export default class ChannelHeader extends React.Component { ); } } + +ChannelHeader.propTypes = { + user: React.PropTypes.object.isRequired +}; diff --git a/web/react/components/delete_post_modal.jsx b/web/react/components/delete_post_modal.jsx index 773d0b420..f8e3e406a 100644 --- a/web/react/components/delete_post_modal.jsx +++ b/web/react/components/delete_post_modal.jsx @@ -68,7 +68,7 @@ export default class DeletePostModal extends React.Component { AppDispatcher.handleServerAction({ type: ActionTypes.RECEIVED_POST_SELECTED, - results: null + postId: null }); } else if (selectedPost.id === this.state.post.id && this.state.root_id) { if (selectedPost.root_id && selectedPost.root_id.length > 0 && selectedList.posts[selectedPost.root_id]) { @@ -77,7 +77,7 @@ export default class DeletePostModal extends React.Component { AppDispatcher.handleServerAction({ type: ActionTypes.RECEIVED_POST_SELECTED, - post_list: selectedList + postId: selectedPost.root_id }); AppDispatcher.handleServerAction({ diff --git a/web/react/components/navbar.jsx b/web/react/components/navbar.jsx index 85a6de6c8..93fe6c05a 100644 --- a/web/react/components/navbar.jsx +++ b/web/react/components/navbar.jsx @@ -93,7 +93,7 @@ export default class Navbar extends React.Component { AppDispatcher.handleServerAction({ type: ActionTypes.RECEIVED_POST_SELECTED, - results: null + postId: null }); if (e.target.className !== 'navbar-toggle' && e.target.className !== 'icon-bar') { diff --git a/web/react/components/post.jsx b/web/react/components/post.jsx index 3619a9f8f..889d4311e 100644 --- a/web/react/components/post.jsx +++ b/web/react/components/post.jsx @@ -3,15 +3,18 @@ import PostHeader from './post_header.jsx'; import PostBody from './post_body.jsx'; -import AppDispatcher from '../dispatcher/app_dispatcher.jsx'; -import Constants from '../utils/constants.jsx'; + import UserStore from '../stores/user_store.jsx'; import PostStore from '../stores/post_store.jsx'; import ChannelStore from '../stores/channel_store.jsx'; -import * as client from '../utils/client.jsx'; + +import Constants from '../utils/constants.jsx'; +const ActionTypes = Constants.ActionTypes; + +import * as Client from '../utils/client.jsx'; import * as AsyncClient from '../utils/async_client.jsx'; -var ActionTypes = Constants.ActionTypes; -import * as utils from '../utils/utils.jsx'; +import * as Utils from '../utils/utils.jsx'; +import AppDispatcher from '../dispatcher/app_dispatcher.jsx'; export default class Post extends React.Component { constructor(props) { @@ -26,13 +29,9 @@ export default class Post extends React.Component { handleCommentClick(e) { e.preventDefault(); - var data = {}; - data.order = [this.props.post.id]; - data.posts = this.props.posts; - AppDispatcher.handleServerAction({ type: ActionTypes.RECEIVED_POST_SELECTED, - post_list: data + postId: Utils.getRootId(this.props.post) }); AppDispatcher.handleServerAction({ @@ -48,14 +47,14 @@ export default class Post extends React.Component { e.preventDefault(); var post = this.props.post; - client.createPost(post, post.channel_id, + Client.createPost(post, post.channel_id, (data) => { AsyncClient.getPosts(); var channel = ChannelStore.get(post.channel_id); var member = ChannelStore.getMember(post.channel_id); member.msg_count = channel.total_msg_count; - member.last_viewed_at = utils.getTimestamp(); + member.last_viewed_at = Utils.getTimestamp(); ChannelStore.setChannelMember(member); AppDispatcher.handleServerAction({ @@ -75,7 +74,7 @@ export default class Post extends React.Component { this.forceUpdate(); } shouldComponentUpdate(nextProps) { - if (!utils.areObjectsEqual(nextProps.post, this.props.post)) { + if (!Utils.areObjectsEqual(nextProps.post, this.props.post)) { return true; } @@ -99,6 +98,10 @@ export default class Post extends React.Component { return true; } + if (nextProps.hasProfiles !== this.props.hasProfiles) { + return true; + } + return false; } getCommentCount(props) { @@ -125,6 +128,7 @@ export default class Post extends React.Component { const post = this.props.post; const parentPost = this.props.parentPost; const posts = this.props.posts; + const user = this.props.user || {}; if (!post.props) { post.props = {}; @@ -152,15 +156,13 @@ export default class Post extends React.Component { } let currentUserCss = ''; - if (UserStore.getCurrentId() === post.user_id && !post.props.from_webhook && !utils.isSystemMessage(post)) { + if (UserStore.getCurrentId() === post.user_id && !post.props.from_webhook && !Utils.isSystemMessage(post)) { currentUserCss = 'current--user'; } - const userProfile = UserStore.getProfile(post.user_id); - - let timestamp = UserStore.getCurrentUser().update_at; - if (userProfile) { - timestamp = userProfile.update_at; + let timestamp = user.update_at; + if (timestamp == null) { + timestamp = UserStore.getCurrentUser().update_at; } let sameUserClass = ''; @@ -174,18 +176,18 @@ export default class Post extends React.Component { } let systemMessageClass = ''; - if (utils.isSystemMessage(post)) { + if (Utils.isSystemMessage(post)) { systemMessageClass = 'post--system'; } let profilePic = null; if (!this.props.hideProfilePic) { - let src = '/api/v1/users/' + post.user_id + '/image?time=' + timestamp + '&' + utils.getSessionIndex(); + let src = '/api/v1/users/' + post.user_id + '/image?time=' + timestamp + '&' + Utils.getSessionIndex(); if (post.props && post.props.from_webhook && global.window.mm_config.EnablePostIconOverride === 'true') { if (post.props.override_icon_url) { src = post.props.override_icon_url; } - } else if (utils.isSystemMessage(post)) { + } else if (Utils.isSystemMessage(post)) { src = Constants.SYSTEM_MESSAGE_PROFILE_IMAGE; } @@ -215,6 +217,7 @@ export default class Post extends React.Component { handleCommentClick={this.handleCommentClick} isLastComment={this.props.isLastComment} sameUser={this.props.sameUser} + user={this.props.user} /> <PostBody post={post} @@ -223,6 +226,7 @@ export default class Post extends React.Component { posts={posts} handleCommentClick={this.handleCommentClick} retryPost={this.retryPost} + hasProfiles={this.props.hasProfiles} /> </div> </div> @@ -233,13 +237,15 @@ export default class Post extends React.Component { } Post.propTypes = { - post: React.PropTypes.object, + post: React.PropTypes.object.isRequired, posts: React.PropTypes.object, parentPost: React.PropTypes.object, + user: React.PropTypes.object, sameUser: React.PropTypes.bool, sameRoot: React.PropTypes.bool, hideProfilePic: React.PropTypes.bool, isLastComment: React.PropTypes.bool, shouldHighlight: React.PropTypes.bool, - displayNameType: React.PropTypes.string + displayNameType: React.PropTypes.string, + hasProfiles: React.PropTypes.bool }; diff --git a/web/react/components/post_body.jsx b/web/react/components/post_body.jsx index d71ac6ec7..b187acba3 100644 --- a/web/react/components/post_body.jsx +++ b/web/react/components/post_body.jsx @@ -33,19 +33,16 @@ class PostBody extends React.Component { this.isImgLoading = false; - this.handleUserChange = this.handleUserChange.bind(this); this.parseEmojis = this.parseEmojis.bind(this); this.createEmbed = this.createEmbed.bind(this); this.createImageEmbed = this.createImageEmbed.bind(this); this.loadImg = this.loadImg.bind(this); const linkData = Utils.extractLinks(this.props.post.message); - const profiles = UserStore.getProfiles(); this.state = { links: linkData.links, - post: this.props.post, - hasUserProfiles: profiles && Object.keys(profiles).length > 1 + post: this.props.post }; } @@ -80,26 +77,12 @@ class PostBody extends React.Component { componentDidMount() { this.parseEmojis(); - - UserStore.addChangeListener(this.handleUserChange); } componentDidUpdate() { this.parseEmojis(); } - componentWillUnmount() { - UserStore.removeChangeListener(this.handleUserChange); - } - - handleUserChange() { - if (!this.state.hasProfiles) { - const profiles = UserStore.getProfiles(); - - this.setState({hasProfiles: profiles && Object.keys(profiles).length > 1}); - } - } - componentWillReceiveProps(nextProps) { const linkData = Utils.extractLinks(nextProps.post.message); if (this.props.post.filenames.length === 0 && this.state.links && this.state.links.length > 0) { @@ -357,7 +340,8 @@ PostBody.propTypes = { post: React.PropTypes.object.isRequired, parentPost: React.PropTypes.object, retryPost: React.PropTypes.func.isRequired, - handleCommentClick: React.PropTypes.func.isRequired + handleCommentClick: React.PropTypes.func.isRequired, + hasProfiles: React.PropTypes.bool }; export default injectIntl(PostBody); diff --git a/web/react/components/post_deleted_modal.jsx b/web/react/components/post_deleted_modal.jsx index be22989a6..1f5c15aa9 100644 --- a/web/react/components/post_deleted_modal.jsx +++ b/web/react/components/post_deleted_modal.jsx @@ -38,7 +38,7 @@ export default class PostDeletedModal extends React.Component { AppDispatcher.handleServerAction({ type: ActionTypes.RECEIVED_POST_SELECTED, - results: null + postId: null }); this.props.onHide(); diff --git a/web/react/components/post_header.jsx b/web/react/components/post_header.jsx index c52391daa..2803fe387 100644 --- a/web/react/components/post_header.jsx +++ b/web/react/components/post_header.jsx @@ -13,16 +13,17 @@ export default class PostHeader extends React.Component { this.state = {}; } render() { - var post = this.props.post; + const post = this.props.post; + const user = this.props.user; - let userProfile = <UserProfile userId={post.user_id}/>; + let userProfile = <UserProfile user={user}/>; let botIndicator; if (post.props && post.props.from_webhook) { if (post.props.override_username && global.window.mm_config.EnablePostUsernameOverride === 'true') { userProfile = ( <UserProfile - userId={post.user_id} + user={user} overwriteName={post.props.override_username} disablePopover={true} /> @@ -33,7 +34,7 @@ export default class PostHeader extends React.Component { } else if (Utils.isSystemMessage(post)) { userProfile = ( <UserProfile - userId={''} + user={{}} overwriteName={Constants.SYSTEM_MESSAGE_PROFILE_NAME} overwriteImage={Constants.SYSTEM_MESSAGE_PROFILE_IMAGE} disablePopover={true} @@ -68,6 +69,7 @@ PostHeader.defaultProps = { }; PostHeader.propTypes = { post: React.PropTypes.object, + user: React.PropTypes.object, commentCount: React.PropTypes.number, isLastComment: React.PropTypes.bool, handleCommentClick: React.PropTypes.func, diff --git a/web/react/components/posts_view.jsx b/web/react/components/posts_view.jsx index 15810d9cf..3a84b51f0 100644 --- a/web/react/components/posts_view.jsx +++ b/web/react/components/posts_view.jsx @@ -145,6 +145,7 @@ export default class PostsView extends React.Component { const postCtls = []; let previousPostDay = new Date(0); const userId = UserStore.getCurrentId(); + const profiles = this.props.profiles; let renderedLastViewed = false; @@ -228,6 +229,13 @@ export default class PostsView extends React.Component { const shouldHighlight = this.props.postsToHighlight && this.props.postsToHighlight.hasOwnProperty(post.id); + let profile; + if (UserStore.getCurrentId() === post.user_id) { + profile = UserStore.getCurrentUser(); + } else { + profile = profiles[post.user_id]; + } + const postCtl = ( <Post key={keyPrefix + 'postKey'} @@ -242,6 +250,8 @@ export default class PostsView extends React.Component { shouldHighlight={shouldHighlight} onClick={() => EventHelpers.emitPostFocusEvent(post.id)} //eslint-disable-line no-loop-func displayNameType={this.state.displayNameType} + hasProfiles={profiles && Object.keys(profiles).length > 1} + user={profile} /> ); @@ -413,6 +423,9 @@ export default class PostsView extends React.Component { if (this.state.isScrolling !== nextState.isScrolling) { return true; } + if (!Utils.areObjectsEqual(this.props.profiles, nextProps.profiles)) { + return true; + } return false; } @@ -513,6 +526,7 @@ PostsView.defaultProps = { PostsView.propTypes = { isActive: React.PropTypes.bool, postList: React.PropTypes.object, + profiles: React.PropTypes.object, scrollPostId: React.PropTypes.string, scrollType: React.PropTypes.number, postViewScrolled: React.PropTypes.func.isRequired, diff --git a/web/react/components/posts_view_container.jsx b/web/react/components/posts_view_container.jsx index 972342021..1b14e8681 100644 --- a/web/react/components/posts_view_container.jsx +++ b/web/react/components/posts_view_container.jsx @@ -6,6 +6,7 @@ import LoadingScreen from './loading_screen.jsx'; import ChannelStore from '../stores/channel_store.jsx'; import PostStore from '../stores/post_store.jsx'; +import UserStore from '../stores/user_store.jsx'; import * as Utils from '../utils/utils.jsx'; import * as EventHelpers from '../dispatcher/event_helpers.jsx'; @@ -24,11 +25,13 @@ export default class PostsViewContainer extends React.Component { this.handlePostsViewScroll = this.handlePostsViewScroll.bind(this); this.loadMorePostsTop = this.loadMorePostsTop.bind(this); this.handlePostsViewJumpRequest = this.handlePostsViewJumpRequest.bind(this); + this.onUserChange = this.onUserChange.bind(this); const currentChannelId = ChannelStore.getCurrentId(); const state = { scrollType: PostsView.SCROLL_TYPE_BOTTOM, - scrollPost: null + scrollPost: null, + profiles: JSON.parse(JSON.stringify(UserStore.getProfiles())) }; if (currentChannelId) { Object.assign(state, { @@ -54,12 +57,14 @@ export default class PostsViewContainer extends React.Component { ChannelStore.addLeaveListener(this.onChannelLeave); PostStore.addChangeListener(this.onPostsChange); PostStore.addPostsViewJumpListener(this.handlePostsViewJumpRequest); + UserStore.addChangeListener(this.onUserChange); } componentWillUnmount() { ChannelStore.removeChangeListener(this.onChannelChange); ChannelStore.removeLeaveListener(this.onChannelLeave); PostStore.removeChangeListener(this.onPostsChange); PostStore.removePostsViewJumpListener(this.handlePostsViewJumpRequest); + UserStore.removeChangeListener(this.onUserChange); } handlePostsViewJumpRequest(type, post) { switch (type) { @@ -135,6 +140,9 @@ export default class PostsViewContainer extends React.Component { atTop[this.state.currentChannelIndex] = PostStore.getVisibilityAtTop(currentChannelId); this.setState({postLists, atTop}); } + onUserChange() { + this.setState({profiles: JSON.parse(JSON.stringify(UserStore.getProfiles()))}); + } getChannelPosts(id) { return PostStore.getVisiblePosts(id); } @@ -180,6 +188,7 @@ export default class PostsViewContainer extends React.Component { showMoreMessagesBottom={false} introText={channel ? createChannelIntroMessage(channel) : null} messageSeparatorTime={this.state.currentLastViewed} + profiles={this.state.profiles} /> ); if (!postLists[i] && isActive) { diff --git a/web/react/components/rhs_comment.jsx b/web/react/components/rhs_comment.jsx index 2ebca9b8d..9588809eb 100644 --- a/web/react/components/rhs_comment.jsx +++ b/web/react/components/rhs_comment.jsx @@ -194,8 +194,16 @@ class RhsComment extends React.Component { var timestamp = UserStore.getCurrentUser().update_at; - var loading; - var postClass = ''; + let loading; + let postClass = ''; + let message = ( + <div + ref='message_holder' + onClick={TextFormatting.handleClick} + dangerouslySetInnerHTML={{__html: TextFormatting.formatText(post.message)}} + /> + ); + if (post.state === Constants.POST_FAILED) { postClass += ' post-fail'; loading = ( @@ -218,6 +226,13 @@ class RhsComment extends React.Component { src='/static/images/load.gif' /> ); + } else if (this.props.post.state === Constants.POST_DELETED) { + message = ( + <FormattedMessage + id='post_body.deleted' + defaultMessage='(message deleted)' + /> + ); } var dropdown = this.createDropdown(); @@ -246,7 +261,7 @@ class RhsComment extends React.Component { <div> <ul className='post__header'> <li className='col__name'> - <strong><UserProfile userId={post.user_id}/></strong> + <strong><UserProfile user={this.props.user}/></strong> </li> <li className='col'> <time className='post__time'> @@ -268,11 +283,7 @@ class RhsComment extends React.Component { <div className='post__body'> <div className={postClass}> {loading} - <div - ref='message_holder' - onClick={TextFormatting.handleClick} - dangerouslySetInnerHTML={{__html: TextFormatting.formatText(post.message)}} - /> + {message} </div> {fileAttachment} </div> @@ -288,7 +299,8 @@ RhsComment.defaultProps = { }; RhsComment.propTypes = { intl: intlShape.isRequired, - post: React.PropTypes.object + post: React.PropTypes.object, + user: React.PropTypes.object }; export default injectIntl(RhsComment); diff --git a/web/react/components/rhs_header_post.jsx b/web/react/components/rhs_header_post.jsx index cd310df56..4c9f6f3f6 100644 --- a/web/react/components/rhs_header_post.jsx +++ b/web/react/components/rhs_header_post.jsx @@ -27,7 +27,7 @@ export default class RhsHeaderPost extends React.Component { AppDispatcher.handleServerAction({ type: ActionTypes.RECEIVED_POST_SELECTED, - results: null + postId: null }); } handleBack(e) { @@ -42,7 +42,7 @@ export default class RhsHeaderPost extends React.Component { AppDispatcher.handleServerAction({ type: ActionTypes.RECEIVED_POST_SELECTED, - results: null + postId: null }); } render() { diff --git a/web/react/components/rhs_root_post.jsx b/web/react/components/rhs_root_post.jsx index 32946ef23..023f3dd2d 100644 --- a/web/react/components/rhs_root_post.jsx +++ b/web/react/components/rhs_root_post.jsx @@ -50,10 +50,10 @@ export default class RhsRootPost extends React.Component { this.parseEmojis(); } render() { - var post = this.props.post; - var currentUser = UserStore.getCurrentUser(); - var isOwner = currentUser.id === post.user_id; - var isAdmin = Utils.isAdmin(currentUser.roles); + const post = this.props.post; + const user = this.props.user; + var isOwner = user.id === post.user_id; + var isAdmin = Utils.isAdmin(user.roles); var timestamp = UserStore.getProfile(post.user_id).update_at; var channel = ChannelStore.get(post.channel_id); @@ -62,9 +62,9 @@ export default class RhsRootPost extends React.Component { type = 'Comment'; } - var currentUserCss = ''; + var userCss = ''; if (UserStore.getCurrentId() === post.user_id) { - currentUserCss = 'current--user'; + userCss = 'current--user'; } var systemMessageClass = ''; @@ -185,14 +185,14 @@ export default class RhsRootPost extends React.Component { ); } - let userProfile = <UserProfile userId={post.user_id}/>; + let userProfile = <UserProfile user={user}/>; let botIndicator; if (post.props && post.props.from_webhook) { if (post.props.override_username && global.window.mm_config.EnablePostUsernameOverride === 'true') { userProfile = ( <UserProfile - userId={post.user_id} + user={user} overwriteName={post.props.override_username} disablePopover={true} /> @@ -203,7 +203,7 @@ export default class RhsRootPost extends React.Component { } else if (Utils.isSystemMessage(post)) { userProfile = ( <UserProfile - userId={''} + user={{}} overwriteName={Constants.SYSTEM_MESSAGE_PROFILE_NAME} overwriteImage={Constants.SYSTEM_MESSAGE_PROFILE_IMAGE} disablePopover={true} @@ -230,7 +230,7 @@ export default class RhsRootPost extends React.Component { ); return ( - <div className={'post post--root ' + currentUserCss + ' ' + systemMessageClass}> + <div className={'post post--root ' + userCss + ' ' + systemMessageClass}> <div className='post-right-channel__name'>{channelName}</div> <div className='post__content'> <div className='post__img'> @@ -278,10 +278,10 @@ export default class RhsRootPost extends React.Component { } RhsRootPost.defaultProps = { - post: null, commentCount: 0 }; RhsRootPost.propTypes = { - post: React.PropTypes.object, + post: React.PropTypes.object.isRequired, + user: React.PropTypes.object.isRequired, commentCount: React.PropTypes.number }; diff --git a/web/react/components/rhs_thread.jsx b/web/react/components/rhs_thread.jsx index 667030b3a..975a39171 100644 --- a/web/react/components/rhs_thread.jsx +++ b/web/react/components/rhs_thread.jsx @@ -19,39 +19,25 @@ export default class RhsThread extends React.Component { this.mounted = false; - this.onChange = this.onChange.bind(this); - this.onChangeAll = this.onChangeAll.bind(this); + this.onPostChange = this.onPostChange.bind(this); + this.onUserChange = this.onUserChange.bind(this); this.forceUpdateInfo = this.forceUpdateInfo.bind(this); this.handleResize = this.handleResize.bind(this); - const state = this.getStateFromStores(); + const state = {}; state.windowWidth = Utils.windowWidth(); state.windowHeight = Utils.windowHeight(); - this.state = state; - } - getStateFromStores() { - var postList = PostStore.getSelectedPost(); - if (!postList || postList.order.length < 1 || !postList.posts[postList.order[0]]) { - return {postList: {}}; - } - - var channelId = postList.posts[postList.order[0]].channel_id; - var pendingPostsList = PostStore.getPendingPosts(channelId); - - if (pendingPostsList) { - for (var pid in pendingPostsList.posts) { - if (pendingPostsList.posts.hasOwnProperty(pid)) { - postList.posts[pid] = pendingPostsList.posts[pid]; - } - } - } + state.selected = PostStore.getSelectedPost(); + state.posts = PostStore.getSelectedPostThread(); + state.profiles = JSON.parse(JSON.stringify(UserStore.getProfiles())); - return {postList: postList}; + this.state = state; } componentDidMount() { - PostStore.addSelectedPostChangeListener(this.onChange); - PostStore.addChangeListener(this.onChangeAll); + PostStore.addSelectedPostChangeListener(this.onPostChange); + PostStore.addChangeListener(this.onPostChange); PreferenceStore.addChangeListener(this.forceUpdateInfo); + UserStore.addChangeListener(this.onUserChange); this.resize(); window.addEventListener('resize', this.handleResize); @@ -65,14 +51,30 @@ export default class RhsThread extends React.Component { this.resize(); } componentWillUnmount() { - PostStore.removeSelectedPostChangeListener(this.onChange); - PostStore.removeChangeListener(this.onChangeAll); + PostStore.removeSelectedPostChangeListener(this.onPostChange); + PostStore.removeChangeListener(this.onPostChange); PreferenceStore.removeChangeListener(this.forceUpdateInfo); + UserStore.removeChangeListener(this.onUserChange); window.removeEventListener('resize', this.handleResize); this.mounted = false; } + shouldComponentUpdate(nextProps, nextState) { + if (!Utils.areObjectsEqual(nextState.posts, this.state.posts)) { + return true; + } + + if (!Utils.areObjectsEqual(nextState.selected, this.state.selected)) { + return true; + } + + if (!Utils.areObjectsEqual(nextState.profiles, this.state.profiles)) { + return true; + } + + return false; + } forceUpdateInfo() { if (this.state.postList) { for (var postId in this.state.postList.posts) { @@ -88,49 +90,14 @@ export default class RhsThread extends React.Component { windowHeight: Utils.windowHeight() }); } - onChange() { - var newState = this.getStateFromStores(); - if (this.mounted && !Utils.areObjectsEqual(newState, this.state)) { - this.setState(newState); - } + onPostChange() { + const selected = PostStore.getSelectedPost(); + const posts = PostStore.getSelectedPostThread(); + this.setState({posts, selected}); } - onChangeAll() { - // if something was changed in the channel like adding a - // comment or post then lets refresh the sidebar list - var currentSelected = PostStore.getSelectedPost(); - if (!currentSelected || currentSelected.order.length === 0 || !currentSelected.posts[currentSelected.order[0]]) { - return; - } - - var currentPosts = PostStore.getVisiblePosts(currentSelected.posts[currentSelected.order[0]].channel_id); - - if (!currentPosts || currentPosts.order.length === 0) { - return; - } - - if (currentPosts.posts[currentPosts.order[0]].channel_id === currentSelected.posts[currentSelected.order[0]].channel_id) { - for (var key in currentSelected.posts) { - if (currentSelected.posts.hasOwnProperty(key)) { - var post = currentSelected.posts[key]; - if (post.pending_post_id) { - Reflect.deleteProperty(currentSelected.posts, key); - } - } - } - - for (var postId in currentPosts.posts) { - if (currentPosts.posts.hasOwnProperty(postId)) { - currentSelected.posts[postId] = currentPosts.posts[postId]; - } - } - - PostStore.storeSelectedPost(currentSelected); - } - - var newState = this.getStateFromStores(); - if (this.mounted && !Utils.areObjectsEqual(newState, this.state)) { - this.setState(newState); - } + onUserChange() { + const profiles = JSON.parse(JSON.stringify(UserStore.getProfiles())); + this.setState({profiles}); } resize() { $('.post-right__scroll').scrollTop(100000); @@ -140,29 +107,21 @@ export default class RhsThread extends React.Component { } } render() { - var postList = this.state.postList; + const posts = this.state.posts; + const selected = this.state.selected; - if (postList == null || !postList.order) { + if (posts == null || selected == null) { return ( <div></div> ); } - var selectedPost = postList.posts[postList.order[0]]; - var rootPost = null; - - if (selectedPost.root_id === '') { - rootPost = selectedPost; - } else { - rootPost = postList.posts[selectedPost.root_id]; - } - var postsArray = []; - for (var postId in postList.posts) { - if (postList.posts.hasOwnProperty(postId)) { - var cpost = postList.posts[postId]; - if (cpost.root_id === rootPost.id) { + for (const id in posts) { + if (posts.hasOwnProperty(id)) { + const cpost = posts[id]; + if (cpost.root_id === selected.id) { postsArray.push(cpost); } } @@ -199,6 +158,13 @@ export default class RhsThread extends React.Component { searchForm = <SearchBox/>; } + let profile; + if (UserStore.getCurrentId() === selected.user_id) { + profile = UserStore.getCurrentUser(); + } else { + profile = this.state.profiles[selected.user_id]; + } + return ( <div className='post-right__container'> <FileUploadOverlay overlayType='right'/> @@ -210,26 +176,33 @@ export default class RhsThread extends React.Component { /> <div className='post-right__scroll'> <RootPost - ref={rootPost.id} - post={rootPost} + ref={selected.id} + post={selected} commentCount={postsArray.length} + user={profile} /> <div className='post-right-comments-container'> {postsArray.map(function mapPosts(comPost) { + let p; + if (UserStore.getCurrentId() === selected.user_id) { + p = UserStore.getCurrentUser(); + } else { + p = this.state.profiles[selected.user_id]; + } return ( <Comment ref={comPost.id} key={comPost.id + 'commentKey'} post={comPost} - selected={(comPost.id === selectedPost.id)} + user={p} /> ); })} </div> <div className='post-create__container'> <CreateComment - channelId={rootPost.channel_id} - rootId={rootPost.id} + channelId={selected.channel_id} + rootId={selected.id} /> </div> </div> diff --git a/web/react/components/search_bar.jsx b/web/react/components/search_bar.jsx index 1cdd09cc8..eaf8b5069 100644 --- a/web/react/components/search_bar.jsx +++ b/web/react/components/search_bar.jsx @@ -87,7 +87,7 @@ class SearchBar extends React.Component { AppDispatcher.handleServerAction({ type: ActionTypes.RECEIVED_POST_SELECTED, - results: null + postId: null }); } handleUserInput(text) { diff --git a/web/react/components/search_results_header.jsx b/web/react/components/search_results_header.jsx index 7f88eb2c7..20fe342dc 100644 --- a/web/react/components/search_results_header.jsx +++ b/web/react/components/search_results_header.jsx @@ -32,7 +32,7 @@ export default class SearchResultsHeader extends React.Component { AppDispatcher.handleServerAction({ type: ActionTypes.RECEIVED_POST_SELECTED, - results: null + postId: null }); } diff --git a/web/react/components/user_profile.jsx b/web/react/components/user_profile.jsx index 1e393cfe9..31b2b9907 100644 --- a/web/react/components/user_profile.jsx +++ b/web/react/components/user_profile.jsx @@ -2,7 +2,6 @@ // See License.txt for license information. import * as Utils from '../utils/utils.jsx'; -import UserStore from '../stores/user_store.jsx'; import {FormattedMessage} from 'mm-intl'; @@ -19,45 +18,15 @@ function nextId() { export default class UserProfile extends React.Component { constructor(props) { super(props); - this.uniqueId = nextId(); - this.onChange = this.onChange.bind(this); - - this.state = this.getStateFromStores(this.props.userId); - } - getStateFromStores(userId) { - var profile = UserStore.getProfile(userId); - - if (profile == null) { - return {profile: {id: '0', username: '...'}}; - } - - return {profile}; } componentDidMount() { - UserStore.addChangeListener(this.onChange); if (!this.props.disablePopover) { $('body').tooltip({selector: '[data-toggle=tooltip]', trigger: 'hover click'}); } } - componentWillUnmount() { - UserStore.removeChangeListener(this.onChange); - } - onChange(userId) { - if (!userId || userId === this.props.userId) { - var newState = this.getStateFromStores(this.props.userId); - if (!Utils.areObjectsEqual(newState, this.state)) { - this.setState(newState); - } - } - } - componentWillReceiveProps(nextProps) { - if (this.props.userId !== nextProps.userId) { - this.setState(this.getStateFromStores(nextProps.userId)); - } - } render() { - var name = Utils.displayUsername(this.state.profile.id); + var name = Utils.displayUsername(this.props.user.id); if (this.props.overwriteName) { name = this.props.overwriteName; } else if (!name) { @@ -68,7 +37,7 @@ export default class UserProfile extends React.Component { return <div>{name}</div>; } - var profileImg = '/api/v1/users/' + this.state.profile.id + '/image?time=' + this.state.profile.update_at + '&' + Utils.getSessionIndex(); + var profileImg = '/api/v1/users/' + this.props.user.id + '/image?time=' + this.props.user.update_at + '&' + Utils.getSessionIndex(); if (this.props.overwriteImage) { profileImg = this.props.overwriteImage; } @@ -100,14 +69,14 @@ export default class UserProfile extends React.Component { dataContent.push( <div data-toggle='tooltip' - title={this.state.profile.email} + title={this.props.user.email} key='user-popover-email' > <a - href={'mailto:' + this.state.profile.email} + href={'mailto:' + this.props.user.email} className='text-nowrap text-lowercase user-popover__email' > - {this.state.profile.email} + {this.props.user.email} </a> </div> ); @@ -139,13 +108,13 @@ export default class UserProfile extends React.Component { } UserProfile.defaultProps = { - userId: '', + user: {}, overwriteName: '', overwriteImage: '', disablePopover: false }; UserProfile.propTypes = { - userId: React.PropTypes.string, + user: React.PropTypes.object.isRequired, overwriteName: React.PropTypes.string, overwriteImage: React.PropTypes.string, disablePopover: React.PropTypes.bool |