summaryrefslogtreecommitdiffstats
path: root/webapp
diff options
context:
space:
mode:
authorJoram Wilander <jwawilander@gmail.com>2016-08-19 10:06:16 -0400
committerGitHub <noreply@github.com>2016-08-19 10:06:16 -0400
commitdad764088e4696edc180443e610287a20aaaba04 (patch)
treee6eece623f17c36243ea179467b85b4c3cbef5c5 /webapp
parent8c2ea22892079cb7f72be96ae6ddff165cda6e43 (diff)
downloadchat-dad764088e4696edc180443e610287a20aaaba04.tar.gz
chat-dad764088e4696edc180443e610287a20aaaba04.tar.bz2
chat-dad764088e4696edc180443e610287a20aaaba04.zip
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
Diffstat (limited to 'webapp')
-rw-r--r--webapp/components/post_view/components/post.jsx13
-rw-r--r--webapp/components/post_view/components/post_list.jsx9
-rw-r--r--webapp/components/post_view/post_focus_view_controller.jsx21
-rw-r--r--webapp/components/post_view/post_view_controller.jsx24
-rw-r--r--webapp/components/profile_picture.jsx55
-rw-r--r--webapp/components/user_list_row.jsx29
-rw-r--r--webapp/sass/components/_modal.scss48
-rw-r--r--webapp/sass/layout/_post.scss4
-rw-r--r--webapp/sass/responsive/_tablet.scss4
-rw-r--r--webapp/utils/utils.jsx6
10 files changed, 170 insertions, 43 deletions
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 = (
- <img
+ <ProfilePicture
src={PostUtils.getProfilePicSrcForPost(post, timestamp)}
- height='36'
- width='36'
+ status={this.props.status}
/>
);
@@ -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 = (
<Post
key={keyPrefix + 'postKey'}
@@ -311,6 +316,7 @@ export default class PostList extends React.Component {
useMilitaryTime={this.props.useMilitaryTime}
emojis={this.props.emojis}
isFlagged={isFlagged}
+ status={status}
/>
);
@@ -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 (
+ <span className={`status-wrapper ${statusClass}`}>
+ <img
+ className='more-modal__image'
+ width={this.props.width}
+ height={this.props.width}
+ src={this.props.src}
+ />
+ </span>
+ );
+ }
+}
+
+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 (
<div
key={user.id}
className='more-modal__row'
>
- <span className={`more-modal__image-wrapper ${user.status}`}>
- <img
- className='more-modal__image'
- width='38'
- height='38'
- src={`${Client.getUsersRoute()}/${user.id}/image?time=${user.update_at}`}
- />
- </span>
+ <ProfilePicture
+ src={`${Client.getUsersRoute()}/${user.id}/image?time=${user.update_at}`}
+ status={status}
+ width='32'
+ height='32'
+ />
<div
className='more-modal__details'
>
diff --git a/webapp/sass/components/_modal.scss b/webapp/sass/components/_modal.scss
index dd62fc20c..a12856c63 100644
--- a/webapp/sass/components/_modal.scss
+++ b/webapp/sass/components/_modal.scss
@@ -483,6 +483,30 @@
}
+
+.status-wrapper {
+ display: inline-block;
+ margin-right: 3px;
+ position: relative;
+
+ &:after {
+ border-radius: 100%;
+ bottom: 4px;
+ content: '';
+ display: block;
+ height: 8px;
+ position: absolute;
+ right: 0;
+ width: 8px;
+ }
+
+ &.status-offline {
+ &:after {
+ background: #D3D3D3;
+ }
+ }
+}
+
.more-modal__list {
display: flex;
flex-direction: column;
@@ -504,26 +528,12 @@
@include border-radius(60px);
flex-grow: 0;
flex-shrink: 0;
- max-width: none;
-
- &-wrapper {
- position: relative;
- display: inline-block;
- margin-right: 8px;
+ margin-top: 2px;
+ max-width: none;
+ .status-wrapper {
&:after {
- content: "";
- right: 0;
- bottom: 0;
- width: 25%;
- height: 25%;
- display: block;
- position: absolute;
- border-radius: 100%;
- }
-
- &.status-offline:after {
- background: #D3D3D3;
+ bottom: 3px;
}
}
}
@@ -586,4 +596,4 @@
flex-grow: 1;
flex-shrink: 1;
}
-} \ No newline at end of file
+}
diff --git a/webapp/sass/layout/_post.scss b/webapp/sass/layout/_post.scss
index d894848c9..4437e4ce3 100644
--- a/webapp/sass/layout/_post.scss
+++ b/webapp/sass/layout/_post.scss
@@ -703,6 +703,10 @@ body.ios {
}
.post__img {
+ .status-wrapper {
+ display: none;
+ }
+
img {
display: none;
}
diff --git a/webapp/sass/responsive/_tablet.scss b/webapp/sass/responsive/_tablet.scss
index a74c18660..36b6af851 100644
--- a/webapp/sass/responsive/_tablet.scss
+++ b/webapp/sass/responsive/_tablet.scss
@@ -134,7 +134,7 @@
}
&:not(.post--thread) {
- padding: 5px .5em 0 72px;
+ padding: 5px .5em 0 77px;
.post__link {
margin: 4px 0 7px;
@@ -220,7 +220,7 @@
&.same--root {
&.same--user {
- padding-left: 72px;
+ padding-left: 77px;
padding-top: 0;
.flag-icon__container {
diff --git a/webapp/utils/utils.jsx b/webapp/utils/utils.jsx
index c394cdaf1..a8ad4d76a 100644
--- a/webapp/utils/utils.jsx
+++ b/webapp/utils/utils.jsx
@@ -525,14 +525,14 @@ export function applyTheme(theme) {
changeCss('.app__body .sidebar--left .status .online--icon', 'fill:' + theme.onlineIndicator, 1);
changeCss('.app__body .channel-header__info .status .online--icon', 'fill:' + theme.onlineIndicator, 1);
changeCss('.app__body .navbar .status .online--icon', 'fill:' + theme.onlineIndicator, 1);
- changeCss('.more-modal__list .more-modal__image-wrapper.status-online:after', 'background:' + theme.onlineIndicator, 1);
+ changeCss('.status-wrapper.status-online:after', 'background:' + theme.onlineIndicator, 1);
}
if (theme.awayIndicator) {
changeCss('.app__body .sidebar--left .status .away--icon', 'fill:' + theme.awayIndicator, 1);
changeCss('.app__body .channel-header__info .status .away--icon', 'fill:' + theme.awayIndicator, 1);
changeCss('.app__body .navbar .status .away--icon', 'fill:' + theme.awayIndicator, 1);
- changeCss('.more-modal__list .more-modal__image-wrapper.status-away:after', 'background:' + theme.awayIndicator, 1);
+ changeCss('.status-wrapper.status-away:after', 'background:' + theme.awayIndicator, 1);
}
if (theme.mentionBj) {
@@ -1341,4 +1341,4 @@ export function isValidPassword(password) {
export function getSiteURL() {
return global.mm_config.SiteURL || window.location.origin;
-} \ No newline at end of file
+}