From dad764088e4696edc180443e610287a20aaaba04 Mon Sep 17 00:00:00 2001 From: Joram Wilander Date: Fri, 19 Aug 2016 10:06:16 -0400 Subject: PLT-1831 Add statuses to centre channel profile pictures (#3826) * Created profile picture componenet and added statuses to pictures in center channel * PLT-3899 - Updating UI for status indicators (#3823) * PLT-3899 - Updating UI for status indicators * Updating position of timestamps for compact layout --- webapp/components/post_view/components/post.jsx | 13 ++--- .../components/post_view/components/post_list.jsx | 9 +++- .../post_view/post_focus_view_controller.jsx | 21 +++++++++ .../components/post_view/post_view_controller.jsx | 24 ++++++++++ webapp/components/profile_picture.jsx | 55 ++++++++++++++++++++++ webapp/components/user_list_row.jsx | 29 +++++++----- 6 files changed, 132 insertions(+), 19 deletions(-) create mode 100644 webapp/components/profile_picture.jsx (limited to 'webapp/components') diff --git a/webapp/components/post_view/components/post.jsx b/webapp/components/post_view/components/post.jsx index b372d2548..1b94f717d 100644 --- a/webapp/components/post_view/components/post.jsx +++ b/webapp/components/post_view/components/post.jsx @@ -3,6 +3,7 @@ import PostHeader from './post_header.jsx'; import PostBody from './post_body.jsx'; +import ProfilePicture from 'components/profile_picture.jsx'; import Constants from 'utils/constants.jsx'; const ActionTypes = Constants.ActionTypes; @@ -105,11 +106,11 @@ export default class Post extends React.Component { return true; } - if (!Utils.areObjectsEqual(nextProps.user, this.props.user)) { + if (nextProps.status !== this.props.status) { return true; } - if (nextProps.emojis !== this.props.emojis) { + if (!Utils.areObjectsEqual(nextProps.user, this.props.user)) { return true; } @@ -192,10 +193,9 @@ export default class Post extends React.Component { } let profilePic = ( - ); @@ -288,5 +288,6 @@ Post.propTypes = { isCommentMention: React.PropTypes.bool, useMilitaryTime: React.PropTypes.bool.isRequired, emojis: React.PropTypes.object.isRequired, - isFlagged: React.PropTypes.bool + isFlagged: React.PropTypes.bool, + status: React.PropTypes.string }; diff --git a/webapp/components/post_view/components/post_list.jsx b/webapp/components/post_view/components/post_list.jsx index 95b30a9d7..a1fd7a317 100644 --- a/webapp/components/post_view/components/post_list.jsx +++ b/webapp/components/post_view/components/post_list.jsx @@ -289,6 +289,11 @@ export default class PostList extends React.Component { isFlagged = this.props.flaggedPosts.get(post.id) === 'true'; } + let status = ''; + if (this.props.statuses) { + status = this.props.statuses[profile.id] || 'offline'; + } + const postCtl = ( ); @@ -579,5 +585,6 @@ PostList.propTypes = { useMilitaryTime: React.PropTypes.bool.isRequired, isFocusPost: React.PropTypes.bool, emojis: React.PropTypes.object.isRequired, - flaggedPosts: React.PropTypes.object + flaggedPosts: React.PropTypes.object, + statuses: React.PropTypes.object }; diff --git a/webapp/components/post_view/post_focus_view_controller.jsx b/webapp/components/post_view/post_focus_view_controller.jsx index a1c474184..7903087e9 100644 --- a/webapp/components/post_view/post_focus_view_controller.jsx +++ b/webapp/components/post_view/post_focus_view_controller.jsx @@ -23,6 +23,7 @@ export default class PostFocusView extends React.Component { this.onPostsChange = this.onPostsChange.bind(this); this.onUserChange = this.onUserChange.bind(this); this.onEmojiChange = this.onEmojiChange.bind(this); + this.onStatusChange = this.onStatusChange.bind(this); this.onPreferenceChange = this.onPreferenceChange.bind(this); this.onPostListScroll = this.onPostListScroll.bind(this); @@ -36,10 +37,16 @@ export default class PostFocusView extends React.Component { const joinLeaveEnabled = PreferenceStore.getBool(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, 'join_leave', true); + let statuses; + if (channel && channel.type !== Constants.DM_CHANNEL) { + statuses = Object.assign({}, UserStore.getStatuses()); + } + this.state = { postList: PostStore.filterPosts(focusedPostId, joinLeaveEnabled), currentUser: UserStore.getCurrentUser(), profiles, + statuses, scrollType: ScrollTypes.POST, currentChannel: ChannelStore.getCurrentId().slice(), scrollPostId: focusedPostId, @@ -54,6 +61,7 @@ export default class PostFocusView extends React.Component { ChannelStore.addChangeListener(this.onChannelChange); PostStore.addChangeListener(this.onPostsChange); UserStore.addChangeListener(this.onUserChange); + UserStore.addStatusesChangeListener(this.onStatusChange); EmojiStore.addChangeListener(this.onEmojiChange); PreferenceStore.addChangeListener(this.onPreferenceChange); } @@ -62,7 +70,9 @@ export default class PostFocusView extends React.Component { ChannelStore.removeChangeListener(this.onChannelChange); PostStore.removeChangeListener(this.onPostsChange); UserStore.removeChangeListener(this.onUserChange); + UserStore.removeStatusesChangeListener(this.onStatusChange); EmojiStore.removeChangeListener(this.onEmojiChange); + PreferenceStore.removeChangeListener(this.onPreferenceChange); } onChannelChange() { @@ -100,6 +110,16 @@ export default class PostFocusView extends React.Component { this.setState({currentUser: UserStore.getCurrentUser(), profiles: JSON.parse(JSON.stringify(profiles))}); } + onStatusChange() { + const channel = ChannelStore.getCurrent(); + let statuses; + if (channel && channel.type !== Constants.DM_CHANNEL) { + statuses = Object.assign({}, UserStore.getStatuses()); + } + + this.setState({statuses}); + } + onEmojiChange() { this.setState({ emojis: EmojiStore.getEmojis() @@ -151,6 +171,7 @@ export default class PostFocusView extends React.Component { isFocusPost={true} emojis={this.state.emojis} flaggedPosts={this.state.flaggedPosts} + statuses={this.state.statuses} /> ); } diff --git a/webapp/components/post_view/post_view_controller.jsx b/webapp/components/post_view/post_view_controller.jsx index 2451dfc8d..7e30818fb 100644 --- a/webapp/components/post_view/post_view_controller.jsx +++ b/webapp/components/post_view/post_view_controller.jsx @@ -26,6 +26,7 @@ export default class PostViewController extends React.Component { this.onUserChange = this.onUserChange.bind(this); this.onPostsChange = this.onPostsChange.bind(this); this.onEmojisChange = this.onEmojisChange.bind(this); + this.onStatusChange = this.onStatusChange.bind(this); this.onPostsViewJumpRequest = this.onPostsViewJumpRequest.bind(this); this.onSetNewMessageIndicator = this.onSetNewMessageIndicator.bind(this); this.onPostListScroll = this.onPostListScroll.bind(this); @@ -46,11 +47,17 @@ export default class PostViewController extends React.Component { const joinLeaveEnabled = PreferenceStore.getBool(Constants.Preferences.CATEGORY_ADVANCED_SETTINGS, 'join_leave', true); + let statuses; + if (channel && channel.type !== Constants.DM_CHANNEL) { + statuses = Object.assign({}, UserStore.getStatuses()); + } + this.state = { channel, postList: PostStore.filterPosts(channel.id, joinLeaveEnabled), currentUser: UserStore.getCurrentUser(), profiles, + statuses, atTop: PostStore.getVisibilityAtTop(channel.id), lastViewed, ownNewMessage: false, @@ -122,9 +129,20 @@ export default class PostViewController extends React.Component { }); } + onStatusChange() { + const channel = this.state.channel; + let statuses; + if (channel && channel.type !== Constants.DM_CHANNEL) { + statuses = Object.assign({}, UserStore.getStatuses()); + } + + this.setState({statuses}); + } + onActivate() { PreferenceStore.addChangeListener(this.onPreferenceChange); UserStore.addChangeListener(this.onUserChange); + UserStore.addStatusesChangeListener(this.onStatusChange); PostStore.addChangeListener(this.onPostsChange); PostStore.addPostsViewJumpListener(this.onPostsViewJumpRequest); EmojiStore.addChangeListener(this.onEmojisChange); @@ -134,6 +152,7 @@ export default class PostViewController extends React.Component { onDeactivate() { PreferenceStore.removeChangeListener(this.onPreferenceChange); UserStore.removeChangeListener(this.onUserChange); + UserStore.removeStatusesChangeListener(this.onStatusChange); PostStore.removeChangeListener(this.onPostsChange); PostStore.removePostsViewJumpListener(this.onPostsViewJumpRequest); EmojiStore.removeChangeListener(this.onEmojisChange); @@ -267,6 +286,10 @@ export default class PostViewController extends React.Component { return true; } + if (!Utils.areObjectsEqual(nextState.statuses, this.state.statuses)) { + return true; + } + if (!Utils.areObjectsEqual(nextState.postList, this.state.postList)) { return true; } @@ -311,6 +334,7 @@ export default class PostViewController extends React.Component { lastViewed={this.state.lastViewed} emojis={this.state.emojis} ownNewMessage={this.state.ownNewMessage} + statuses={this.state.statuses} /> ); } diff --git a/webapp/components/profile_picture.jsx b/webapp/components/profile_picture.jsx new file mode 100644 index 000000000..5443b74d6 --- /dev/null +++ b/webapp/components/profile_picture.jsx @@ -0,0 +1,55 @@ +// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import React from 'react'; + +export default class ProfilePicture extends React.Component { + shouldComponentUpdate(nextProps) { + if (nextProps.src !== this.props.src) { + return true; + } + + if (nextProps.status !== this.props.status) { + return true; + } + + if (nextProps.width !== this.props.width) { + return true; + } + + if (nextProps.height !== this.props.height) { + return true; + } + + return false; + } + + render() { + let statusClass = ''; + if (this.props.status) { + statusClass = 'status-' + this.props.status; + } + + return ( + + + + ); + } +} + +ProfilePicture.defaultProps = { + width: '36', + height: '36' +}; +ProfilePicture.propTypes = { + src: React.PropTypes.string.isRequired, + status: React.PropTypes.string, + width: React.PropTypes.string, + height: React.PropTypes.string +}; diff --git a/webapp/components/user_list_row.jsx b/webapp/components/user_list_row.jsx index d5d123ab7..9f80d4caa 100644 --- a/webapp/components/user_list_row.jsx +++ b/webapp/components/user_list_row.jsx @@ -1,11 +1,15 @@ // Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. +import ProfilePicture from 'components/profile_picture.jsx'; + import UserStore from 'stores/user_store.jsx'; -import Constants from 'utils/constants.jsx'; import PreferenceStore from 'stores/preference_store.jsx'; + +import Constants from 'utils/constants.jsx'; import * as Utils from 'utils/utils.jsx'; import Client from 'client/web_client.jsx'; + import React from 'react'; export default function UserListRow({user, teamMember, actions, actionProps}) { @@ -32,23 +36,24 @@ export default function UserListRow({user, teamMember, actions, actionProps}) { }); } - if (!user.status) { - var status = UserStore.getStatus(user.id); - user.status = status ? 'status-' + status : ''; + let status; + if (user.status) { + status = user.status; + } else { + status = UserStore.getStatus(user.id); } + return (
- - - +
-- cgit v1.2.3-1-g7c22