summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevyn Bruyere <6eme.hokage@gmail.com>2016-06-15 14:30:32 +0200
committerChristopher Speller <crspeller@gmail.com>2016-06-15 06:30:32 -0600
commitd6fdd936797890565dff4e6951a6792b7e09831c (patch)
treeca188cd891cb4930bae303f1d5f92250c75deba3
parentdbcf8572e5af1c0c244850627437d97616098740 (diff)
downloadchat-d6fdd936797890565dff4e6951a6792b7e09831c.tar.gz
chat-d6fdd936797890565dff4e6951a6792b7e09831c.tar.bz2
chat-d6fdd936797890565dff4e6951a6792b7e09831c.zip
PLT-946 Add status icon to the left of the username in DM channel (#3258)
Add a StatusIcon component to be able to display a status icon from anywhere
-rw-r--r--webapp/components/channel_header.jsx22
-rw-r--r--webapp/components/navbar.jsx20
-rw-r--r--webapp/components/sidebar.jsx24
-rw-r--r--webapp/components/status_icon.jsx37
-rw-r--r--webapp/sass/components/_status-icon.scss33
-rw-r--r--webapp/sass/layout/_headers.scss2
-rw-r--r--webapp/sass/layout/_sidebar-left.scss34
-rw-r--r--webapp/sass/responsive/_mobile.scss2
-rw-r--r--webapp/utils/utils.jsx8
9 files changed, 126 insertions, 56 deletions
diff --git a/webapp/components/channel_header.jsx b/webapp/components/channel_header.jsx
index 34ac53705..181d37ca2 100644
--- a/webapp/components/channel_header.jsx
+++ b/webapp/components/channel_header.jsx
@@ -15,6 +15,7 @@ import ChannelNotificationsModal from './channel_notifications_modal.jsx';
import DeleteChannelModal from './delete_channel_modal.jsx';
import RenameChannelModal from './rename_channel_modal.jsx';
import ToggleModalButton from './toggle_modal_button.jsx';
+import StatusIcon from './status_icon.jsx';
import ChannelStore from 'stores/channel_store.jsx';
import UserStore from 'stores/user_store.jsx';
@@ -82,6 +83,7 @@ export default class ChannelHeader extends React.Component {
SearchStore.addSearchChangeListener(this.onListenerChange);
PreferenceStore.addChangeListener(this.onListenerChange);
UserStore.addChangeListener(this.onListenerChange);
+ UserStore.addStatusesChangeListener(this.onListenerChange);
$('.sidebar--left .dropdown-menu').perfectScrollbar();
document.addEventListener('keydown', this.openRecentMentions);
}
@@ -91,6 +93,7 @@ export default class ChannelHeader extends React.Component {
SearchStore.removeSearchChangeListener(this.onListenerChange);
PreferenceStore.removeChangeListener(this.onListenerChange);
UserStore.removeChangeListener(this.onListenerChange);
+ UserStore.removeStatusesChangeListener(this.onListenerChange);
document.removeEventListener('keydown', this.openRecentMentions);
}
onListenerChange() {
@@ -157,6 +160,21 @@ export default class ChannelHeader extends React.Component {
showRenameChannelModal: false
});
}
+
+ getTeammateStatus() {
+ const channel = this.state.channel;
+
+ // get status for direct message channels
+ if (channel.type === 'D') {
+ const currentUserId = this.state.currentUser.id;
+ const teammate = this.state.users.find((user) => user.id !== currentUserId);
+ if (teammate) {
+ return UserStore.getStatus(teammate.id);
+ }
+ }
+ return null;
+ }
+
render() {
if (!this.validState()) {
return null;
@@ -173,7 +191,7 @@ export default class ChannelHeader extends React.Component {
);
const popoverContent = (
<Popover
- id='hader-popover'
+ id='header-popover'
bStyle='info'
bSize='large'
placement='bottom'
@@ -459,7 +477,7 @@ export default class ChannelHeader extends React.Component {
data-toggle='dropdown'
aria-expanded='true'
>
- <strong className='heading'>{channelTitle} </strong>
+ <strong className='heading'><StatusIcon status={this.getTeammateStatus()}/>{channelTitle} </strong>
<span className='glyphicon glyphicon-chevron-down header-dropdown__icon'/>
</a>
<ul
diff --git a/webapp/components/navbar.jsx b/webapp/components/navbar.jsx
index cfda38670..32682bc78 100644
--- a/webapp/components/navbar.jsx
+++ b/webapp/components/navbar.jsx
@@ -13,6 +13,7 @@ import ChannelNotificationsModal from './channel_notifications_modal.jsx';
import DeleteChannelModal from './delete_channel_modal.jsx';
import RenameChannelModal from './rename_channel_modal.jsx';
import ToggleModalButton from './toggle_modal_button.jsx';
+import StatusIcon from './status_icon.jsx';
import UserStore from 'stores/user_store.jsx';
import ChannelStore from 'stores/channel_store.jsx';
@@ -77,12 +78,14 @@ export default class Navbar extends React.Component {
componentDidMount() {
ChannelStore.addChangeListener(this.onChange);
ChannelStore.addExtraInfoChangeListener(this.onChange);
+ UserStore.addStatusesChangeListener(this.onChange);
$('.inner-wrap').click(this.hideSidebars);
document.addEventListener('keydown', this.showChannelSwitchModal);
}
componentWillUnmount() {
ChannelStore.removeChangeListener(this.onChange);
ChannelStore.removeExtraInfoChangeListener(this.onChange);
+ UserStore.removeStatusesChangeListener(this.onChange);
document.removeEventListener('keydown', this.showChannelSwitchModal);
}
handleSubmit(e) {
@@ -352,7 +355,7 @@ export default class Navbar extends React.Component {
data-toggle='dropdown'
aria-expanded='true'
>
- <span className='heading'>{channelTitle} </span>
+ <span className='heading'><StatusIcon status={this.getTeammateStatus()}/>{channelTitle} </span>
<span className='glyphicon glyphicon-chevron-down header-dropdown__icon'></span>
</a>
<ul
@@ -446,6 +449,21 @@ export default class Navbar extends React.Component {
return buttons;
}
+
+ getTeammateStatus() {
+ const channel = this.state.channel;
+
+ // get status for direct message channels
+ if (channel.type === 'D') {
+ const currentUserId = this.state.currentUser.id;
+ const teammate = this.state.users.find((user) => user.id !== currentUserId);
+ if (teammate) {
+ return UserStore.getStatus(teammate.id);
+ }
+ }
+ return null;
+ }
+
render() {
if (!this.isStateValid()) {
return null;
diff --git a/webapp/components/sidebar.jsx b/webapp/components/sidebar.jsx
index 1c02c9d98..f6c2a1a5a 100644
--- a/webapp/components/sidebar.jsx
+++ b/webapp/components/sidebar.jsx
@@ -8,6 +8,7 @@ import MoreDirectChannels from './more_direct_channels.jsx';
import SidebarHeader from './sidebar_header.jsx';
import UnreadChannelIndicator from './unread_channel_indicator.jsx';
import TutorialTip from './tutorial/tutorial_tip.jsx';
+import StatusIcon from './status_icon.jsx';
import ChannelStore from 'stores/channel_store.jsx';
import UserStore from 'stores/user_store.jsx';
@@ -503,30 +504,14 @@ export default class Sidebar extends React.Component {
rowClass += ' has-badge';
}
- // set up status icon for direct message channels
- var status = null;
- if (channel.type === 'D') {
- var statusIcon = '';
- if (channel.status === 'online') {
- statusIcon = Constants.ONLINE_ICON_SVG;
- } else if (channel.status === 'away') {
- statusIcon = Constants.AWAY_ICON_SVG;
- } else {
- statusIcon = Constants.OFFLINE_ICON_SVG;
- }
- status = (
- <span
- className='status'
- dangerouslySetInnerHTML={{__html: statusIcon}}
- />
- );
- }
-
var icon = null;
if (channel.type === 'O') {
icon = <div className='status'><i className='fa fa-globe'></i></div>;
} else if (channel.type === 'P') {
icon = <div className='status'><i className='fa fa-lock'></i></div>;
+ } else {
+ // set up status icon for direct message channels (status is null for other channel types)
+ icon = <StatusIcon status={channel.status}/>;
}
let closeButton = null;
@@ -581,7 +566,6 @@ export default class Sidebar extends React.Component {
className={rowClass}
>
{icon}
- {status}
{channel.display_name}
{badge}
{closeButton}
diff --git a/webapp/components/status_icon.jsx b/webapp/components/status_icon.jsx
new file mode 100644
index 000000000..a4242fb60
--- /dev/null
+++ b/webapp/components/status_icon.jsx
@@ -0,0 +1,37 @@
+// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import Constants from 'utils/constants.jsx';
+
+import React from 'react';
+
+export default class StatusIcon extends React.Component {
+ render() {
+ const status = this.props.status;
+
+ if (!status) {
+ return null;
+ }
+
+ let statusIcon = '';
+ if (status === 'online') {
+ statusIcon = Constants.ONLINE_ICON_SVG;
+ } else if (status === 'away') {
+ statusIcon = Constants.AWAY_ICON_SVG;
+ } else {
+ statusIcon = Constants.OFFLINE_ICON_SVG;
+ }
+
+ return (
+ <span
+ className='status'
+ dangerouslySetInnerHTML={{__html: statusIcon}}
+ />
+ );
+ }
+
+}
+
+StatusIcon.propTypes = {
+ status: React.PropTypes.string.isRequired
+}; \ No newline at end of file
diff --git a/webapp/sass/components/_status-icon.scss b/webapp/sass/components/_status-icon.scss
new file mode 100644
index 000000000..32ce07164
--- /dev/null
+++ b/webapp/sass/components/_status-icon.scss
@@ -0,0 +1,33 @@
+@charset 'UTF-8';
+
+.status {
+ display: inline-block;
+ margin-right: 6px;
+ position: relative;
+ top: 1px;
+ width: 12px;
+
+ svg {
+ max-height: 14px;
+ }
+
+ i,
+ path,
+ ellipse {
+ @include opacity(.5);
+
+ &.online--icon,
+ &.away--icon {
+ @include opacity(1);
+ }
+ }
+
+ .fa-lock {
+ margin-left: 1px;
+ }
+
+ .fa-globe {
+ position: relative;
+ top: -1px;
+ }
+} \ No newline at end of file
diff --git a/webapp/sass/layout/_headers.scss b/webapp/sass/layout/_headers.scss
index d0ccfbe23..2382a39e4 100644
--- a/webapp/sass/layout/_headers.scss
+++ b/webapp/sass/layout/_headers.scss
@@ -144,6 +144,8 @@
white-space: normal;
}
}
+
+ @import "../components/status-icon";
}
.channel-intro {
diff --git a/webapp/sass/layout/_sidebar-left.scss b/webapp/sass/layout/_sidebar-left.scss
index 6b8658d25..d4d01c865 100644
--- a/webapp/sass/layout/_sidebar-left.scss
+++ b/webapp/sass/layout/_sidebar-left.scss
@@ -60,38 +60,6 @@
top: 5px;
}
- .status {
- display: inline-block;
- margin-right: 6px;
- position: relative;
- top: 1px;
- width: 12px;
-
- svg {
- max-height: 14px;
- }
-
- i,
- path,
- ellipse {
- @include opacity(.5);
-
- &.online--icon,
- &.away--icon {
- @include opacity(1);
- }
- }
-
- .fa-lock {
- margin-left: 1px;
- }
-
- .fa-globe {
- position: relative;
- top: -1px;
- }
- }
-
.nav-pills__container {
-webkit-overflow-scrolling: touch;
height: calc(100% - 80px);
@@ -234,6 +202,8 @@
color: #666666;
}
}
+
+ @import "../components/status-icon";
}
.channel-loading-gif {
diff --git a/webapp/sass/responsive/_mobile.scss b/webapp/sass/responsive/_mobile.scss
index cb1191fe3..6308829ec 100644
--- a/webapp/sass/responsive/_mobile.scss
+++ b/webapp/sass/responsive/_mobile.scss
@@ -590,6 +590,8 @@
.navbar-brand {
white-space: nowrap;
+
+ @import "../components/status-icon";
}
}
}
diff --git a/webapp/utils/utils.jsx b/webapp/utils/utils.jsx
index 96eedaf75..d2fd2efbf 100644
--- a/webapp/utils/utils.jsx
+++ b/webapp/utils/utils.jsx
@@ -521,7 +521,7 @@ export function applyTheme(theme) {
changeCss('@media(max-width: 768px){.app__body .modal .settings-modal .settings-table .nav>li>a', 'color:' + theme.sidebarText, 1);
changeCss('.app__body .sidebar--left .nav-pills__container li>h4, .app__body .sidebar--left .add-channel-btn', 'color:' + changeOpacity(theme.sidebarText, 0.6), 1);
changeCss('.app__body .sidebar--left .add-channel-btn:hover, .app__body .sidebar--left .add-channel-btn:focus', 'color:' + theme.sidebarText, 1);
- changeCss('.app__body .sidebar--left .status .offline--icon, .app__body .sidebar--left .status .offline--icon', 'fill:' + theme.sidebarText, 1);
+ changeCss('.app__body .sidebar--left .status .offline--icon', 'fill:' + theme.sidebarText, 1);
changeCss('@media(max-width: 768px){.app__body .modal .settings-modal .settings-table .nav>li>a', 'border-color:' + changeOpacity(theme.sidebarText, 0.2), 2);
}
@@ -566,10 +566,14 @@ export function applyTheme(theme) {
if (theme.onlineIndicator) {
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);
}
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);
}
if (theme.mentionBj) {
@@ -659,6 +663,8 @@ export function applyTheme(theme) {
changeCss('.app__body .post.post--comment .post__body', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1);
changeCss('@media(min-width: 768px){.app__body .post.post--compact.same--root.post--comment .post__content', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1);
changeCss('.app__body .post.post--comment.current--user .post__body', 'border-color:' + changeOpacity(theme.centerChannelColor, 0.2), 1);
+ changeCss('.app__body .channel-header__info .status .offline--icon', 'fill:' + theme.centerChannelColor, 1);
+ changeCss('.app__body .navbar .status .offline--icon', 'fill:' + theme.centerChannelColor, 1);
}
if (theme.newMessageSeparator) {