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
Этот коммит содержится в:
Kevyn Bruyere
2016-06-15 14:30:32 +02:00
коммит произвёл Christopher Speller
родитель dbcf8572e5
Коммит d6fdd93679
9 изменённых файлов: 126 добавлений и 56 удалений

Просмотреть файл

@@ -15,6 +15,7 @@ import ChannelNotificationsModal from './channel_notifications_modal.jsx';
import DeleteChannelModal from './delete_channel_modal.jsx'; import DeleteChannelModal from './delete_channel_modal.jsx';
import RenameChannelModal from './rename_channel_modal.jsx'; import RenameChannelModal from './rename_channel_modal.jsx';
import ToggleModalButton from './toggle_modal_button.jsx'; import ToggleModalButton from './toggle_modal_button.jsx';
import StatusIcon from './status_icon.jsx';
import ChannelStore from 'stores/channel_store.jsx'; import ChannelStore from 'stores/channel_store.jsx';
import UserStore from 'stores/user_store.jsx'; import UserStore from 'stores/user_store.jsx';
@@ -82,6 +83,7 @@ export default class ChannelHeader extends React.Component {
SearchStore.addSearchChangeListener(this.onListenerChange); SearchStore.addSearchChangeListener(this.onListenerChange);
PreferenceStore.addChangeListener(this.onListenerChange); PreferenceStore.addChangeListener(this.onListenerChange);
UserStore.addChangeListener(this.onListenerChange); UserStore.addChangeListener(this.onListenerChange);
UserStore.addStatusesChangeListener(this.onListenerChange);
$('.sidebar--left .dropdown-menu').perfectScrollbar(); $('.sidebar--left .dropdown-menu').perfectScrollbar();
document.addEventListener('keydown', this.openRecentMentions); document.addEventListener('keydown', this.openRecentMentions);
} }
@@ -91,6 +93,7 @@ export default class ChannelHeader extends React.Component {
SearchStore.removeSearchChangeListener(this.onListenerChange); SearchStore.removeSearchChangeListener(this.onListenerChange);
PreferenceStore.removeChangeListener(this.onListenerChange); PreferenceStore.removeChangeListener(this.onListenerChange);
UserStore.removeChangeListener(this.onListenerChange); UserStore.removeChangeListener(this.onListenerChange);
UserStore.removeStatusesChangeListener(this.onListenerChange);
document.removeEventListener('keydown', this.openRecentMentions); document.removeEventListener('keydown', this.openRecentMentions);
} }
onListenerChange() { onListenerChange() {
@@ -157,6 +160,21 @@ export default class ChannelHeader extends React.Component {
showRenameChannelModal: false 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() { render() {
if (!this.validState()) { if (!this.validState()) {
return null; return null;
@@ -173,7 +191,7 @@ export default class ChannelHeader extends React.Component {
); );
const popoverContent = ( const popoverContent = (
<Popover <Popover
id='hader-popover' id='header-popover'
bStyle='info' bStyle='info'
bSize='large' bSize='large'
placement='bottom' placement='bottom'
@@ -459,7 +477,7 @@ export default class ChannelHeader extends React.Component {
data-toggle='dropdown' data-toggle='dropdown'
aria-expanded='true' 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'/> <span className='glyphicon glyphicon-chevron-down header-dropdown__icon'/>
</a> </a>
<ul <ul

Просмотреть файл

@@ -13,6 +13,7 @@ import ChannelNotificationsModal from './channel_notifications_modal.jsx';
import DeleteChannelModal from './delete_channel_modal.jsx'; import DeleteChannelModal from './delete_channel_modal.jsx';
import RenameChannelModal from './rename_channel_modal.jsx'; import RenameChannelModal from './rename_channel_modal.jsx';
import ToggleModalButton from './toggle_modal_button.jsx'; import ToggleModalButton from './toggle_modal_button.jsx';
import StatusIcon from './status_icon.jsx';
import UserStore from 'stores/user_store.jsx'; import UserStore from 'stores/user_store.jsx';
import ChannelStore from 'stores/channel_store.jsx'; import ChannelStore from 'stores/channel_store.jsx';
@@ -77,12 +78,14 @@ export default class Navbar extends React.Component {
componentDidMount() { componentDidMount() {
ChannelStore.addChangeListener(this.onChange); ChannelStore.addChangeListener(this.onChange);
ChannelStore.addExtraInfoChangeListener(this.onChange); ChannelStore.addExtraInfoChangeListener(this.onChange);
UserStore.addStatusesChangeListener(this.onChange);
$('.inner-wrap').click(this.hideSidebars); $('.inner-wrap').click(this.hideSidebars);
document.addEventListener('keydown', this.showChannelSwitchModal); document.addEventListener('keydown', this.showChannelSwitchModal);
} }
componentWillUnmount() { componentWillUnmount() {
ChannelStore.removeChangeListener(this.onChange); ChannelStore.removeChangeListener(this.onChange);
ChannelStore.removeExtraInfoChangeListener(this.onChange); ChannelStore.removeExtraInfoChangeListener(this.onChange);
UserStore.removeStatusesChangeListener(this.onChange);
document.removeEventListener('keydown', this.showChannelSwitchModal); document.removeEventListener('keydown', this.showChannelSwitchModal);
} }
handleSubmit(e) { handleSubmit(e) {
@@ -352,7 +355,7 @@ export default class Navbar extends React.Component {
data-toggle='dropdown' data-toggle='dropdown'
aria-expanded='true' 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> <span className='glyphicon glyphicon-chevron-down header-dropdown__icon'></span>
</a> </a>
<ul <ul
@@ -446,6 +449,21 @@ export default class Navbar extends React.Component {
return buttons; 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() { render() {
if (!this.isStateValid()) { if (!this.isStateValid()) {
return null; return null;

Просмотреть файл

@@ -8,6 +8,7 @@ import MoreDirectChannels from './more_direct_channels.jsx';
import SidebarHeader from './sidebar_header.jsx'; import SidebarHeader from './sidebar_header.jsx';
import UnreadChannelIndicator from './unread_channel_indicator.jsx'; import UnreadChannelIndicator from './unread_channel_indicator.jsx';
import TutorialTip from './tutorial/tutorial_tip.jsx'; import TutorialTip from './tutorial/tutorial_tip.jsx';
import StatusIcon from './status_icon.jsx';
import ChannelStore from 'stores/channel_store.jsx'; import ChannelStore from 'stores/channel_store.jsx';
import UserStore from 'stores/user_store.jsx'; import UserStore from 'stores/user_store.jsx';
@@ -503,30 +504,14 @@ export default class Sidebar extends React.Component {
rowClass += ' has-badge'; 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; var icon = null;
if (channel.type === 'O') { if (channel.type === 'O') {
icon = <div className='status'><i className='fa fa-globe'></i></div>; icon = <div className='status'><i className='fa fa-globe'></i></div>;
} else if (channel.type === 'P') { } else if (channel.type === 'P') {
icon = <div className='status'><i className='fa fa-lock'></i></div>; 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; let closeButton = null;
@@ -581,7 +566,6 @@ export default class Sidebar extends React.Component {
className={rowClass} className={rowClass}
> >
{icon} {icon}
{status}
{channel.display_name} {channel.display_name}
{badge} {badge}
{closeButton} {closeButton}

37
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
};

33
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;
}
}

Просмотреть файл

@@ -144,6 +144,8 @@
white-space: normal; white-space: normal;
} }
} }
@import "../components/status-icon";
} }
.channel-intro { .channel-intro {

Просмотреть файл

@@ -60,38 +60,6 @@
top: 5px; 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 { .nav-pills__container {
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
height: calc(100% - 80px); height: calc(100% - 80px);
@@ -234,6 +202,8 @@
color: #666666; color: #666666;
} }
} }
@import "../components/status-icon";
} }
.channel-loading-gif { .channel-loading-gif {

Просмотреть файл

@@ -590,6 +590,8 @@
.navbar-brand { .navbar-brand {
white-space: nowrap; white-space: nowrap;
@import "../components/status-icon";
} }
} }
} }

Просмотреть файл

@@ -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('@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 .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 .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); 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) { if (theme.onlineIndicator) {
changeCss('.app__body .sidebar--left .status .online--icon', 'fill:' + theme.onlineIndicator, 1); 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) { if (theme.awayIndicator) {
changeCss('.app__body .sidebar--left .status .away--icon', 'fill:' + theme.awayIndicator, 1); 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) { 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('.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('@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 .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) { if (theme.newMessageSeparator) {