summaryrefslogtreecommitdiffstats
path: root/web/react/components/sidebar.jsx
diff options
context:
space:
mode:
Diffstat (limited to 'web/react/components/sidebar.jsx')
-rw-r--r--web/react/components/sidebar.jsx576
1 files changed, 333 insertions, 243 deletions
diff --git a/web/react/components/sidebar.jsx b/web/react/components/sidebar.jsx
index 5b74165f3..983260187 100644
--- a/web/react/components/sidebar.jsx
+++ b/web/react/components/sidebar.jsx
@@ -8,127 +8,137 @@ var SocketStore = require('../stores/socket_store.jsx');
var UserStore = require('../stores/user_store.jsx');
var TeamStore = require('../stores/team_store.jsx');
var BrowserStore = require('../stores/browser_store.jsx');
-var utils = require('../utils/utils.jsx');
+var Utils = require('../utils/utils.jsx');
var SidebarHeader = require('./sidebar_header.jsx');
var SearchBox = require('./search_bar.jsx');
var Constants = require('../utils/constants.jsx');
-function getStateFromStores() {
- var members = ChannelStore.getAllMembers();
- var teamMemberMap = UserStore.getActiveOnlyProfiles();
- var currentId = ChannelStore.getCurrentId();
+export default class Sidebar extends React.Component {
+ constructor(props) {
+ super(props);
- var teammates = [];
- for (var id in teamMemberMap) {
- if (id === UserStore.getCurrentId()) {
- continue;
- }
- teammates.push(teamMemberMap[id]);
- }
+ this.badgesActive = false;
+ this.firstUnreadChannel = null;
+ this.lastUnreadChannel = null;
- // Create lists of all read and unread direct channels
- var showDirectChannels = [];
- var readDirectChannels = [];
- for (var i = 0; i < teammates.length; i++) {
- var teammate = teammates[i];
+ this.onChange = this.onChange.bind(this);
+ this.onScroll = this.onScroll.bind(this);
+ this.onResize = this.onResize.bind(this);
+ this.updateUnreadIndicators = this.updateUnreadIndicators.bind(this);
+ this.createChannelElement = this.createChannelElement.bind(this);
- if (teammate.id === UserStore.getCurrentId()) {
- continue;
+ this.state = this.getStateFromStores();
+ this.state.loadingDMChannel = -1;
+ }
+ getStateFromStores() {
+ var members = ChannelStore.getAllMembers();
+ var teamMemberMap = UserStore.getActiveOnlyProfiles();
+ var currentId = ChannelStore.getCurrentId();
+
+ var teammates = [];
+ for (var id in teamMemberMap) {
+ if (id === UserStore.getCurrentId()) {
+ continue;
+ }
+ teammates.push(teamMemberMap[id]);
}
- var channelName = '';
- if (teammate.id > UserStore.getCurrentId()) {
- channelName = UserStore.getCurrentId() + '__' + teammate.id;
- } else {
- channelName = teammate.id + '__' + UserStore.getCurrentId();
- }
+ // Create lists of all read and unread direct channels
+ var showDirectChannels = [];
+ var readDirectChannels = [];
+ for (var i = 0; i < teammates.length; i++) {
+ var teammate = teammates[i];
- var channel = ChannelStore.getByName(channelName);
+ if (teammate.id === UserStore.getCurrentId()) {
+ continue;
+ }
- if (channel != null) {
- channel.display_name = teammate.username;
- channel.teammate_username = teammate.username;
+ var channelName = '';
+ if (teammate.id > UserStore.getCurrentId()) {
+ channelName = UserStore.getCurrentId() + '__' + teammate.id;
+ } else {
+ channelName = teammate.id + '__' + UserStore.getCurrentId();
+ }
- channel.status = UserStore.getStatus(teammate.id);
+ var channel = ChannelStore.getByName(channelName);
- var channelMember = members[channel.id];
- var msgCount = channel.total_msg_count - channelMember.msg_count;
- if (msgCount > 0) {
- showDirectChannels.push(channel);
- } else if (currentId === channel.id) {
- showDirectChannels.push(channel);
+ if (channel != null) {
+ channel.display_name = teammate.username;
+ channel.teammate_username = teammate.username;
+
+ channel.status = UserStore.getStatus(teammate.id);
+
+ var channelMember = members[channel.id];
+ var msgCount = channel.total_msg_count - channelMember.msg_count;
+ if (msgCount > 0) {
+ showDirectChannels.push(channel);
+ } else if (currentId === channel.id) {
+ showDirectChannels.push(channel);
+ } else {
+ readDirectChannels.push(channel);
+ }
} else {
- readDirectChannels.push(channel);
+ var tempChannel = {};
+ tempChannel.fake = true;
+ tempChannel.name = channelName;
+ tempChannel.display_name = teammate.username;
+ tempChannel.teammate_username = teammate.username;
+ tempChannel.status = UserStore.getStatus(teammate.id);
+ tempChannel.last_post_at = 0;
+ tempChannel.total_msg_count = 0;
+ tempChannel.type = 'D';
+ readDirectChannels.push(tempChannel);
}
- } else {
- var tempChannel = {};
- tempChannel.fake = true;
- tempChannel.name = channelName;
- tempChannel.display_name = teammate.username;
- tempChannel.teammate_username = teammate.username;
- tempChannel.status = UserStore.getStatus(teammate.id);
- tempChannel.last_post_at = 0;
- tempChannel.total_msg_count = 0;
- tempChannel.type = 'D';
- readDirectChannels.push(tempChannel);
}
- }
- // If we don't have MAX_DMS unread channels, sort the read list by last_post_at
- if (showDirectChannels.length < Constants.MAX_DMS) {
- readDirectChannels.sort(function sortByLastPost(a, b) {
- // sort by last_post_at first
- if (a.last_post_at > b.last_post_at) {
- return -1;
- }
- if (a.last_post_at < b.last_post_at) {
- return 1;
- }
+ // If we don't have MAX_DMS unread channels, sort the read list by last_post_at
+ if (showDirectChannels.length < Constants.MAX_DMS) {
+ readDirectChannels.sort(function sortByLastPost(a, b) {
+ // sort by last_post_at first
+ if (a.last_post_at > b.last_post_at) {
+ return -1;
+ }
+ if (a.last_post_at < b.last_post_at) {
+ return 1;
+ }
- // if last_post_at is equal, sort by name
- if (a.display_name < b.display_name) {
- return -1;
- }
- if (a.display_name > b.display_name) {
- return 1;
+ // if last_post_at is equal, sort by name
+ if (a.display_name < b.display_name) {
+ return -1;
+ }
+ if (a.display_name > b.display_name) {
+ return 1;
+ }
+ return 0;
+ });
+
+ var index = 0;
+ while (showDirectChannels.length < Constants.MAX_DMS && index < readDirectChannels.length) {
+ showDirectChannels.push(readDirectChannels[index]);
+ index++;
}
- return 0;
- });
+ readDirectChannels = readDirectChannels.slice(index);
- var index = 0;
- while (showDirectChannels.length < Constants.MAX_DMS && index < readDirectChannels.length) {
- showDirectChannels.push(readDirectChannels[index]);
- index++;
+ showDirectChannels.sort(function directSort(a, b) {
+ if (a.display_name < b.display_name) {
+ return -1;
+ }
+ if (a.display_name > b.display_name) {
+ return 1;
+ }
+ return 0;
+ });
}
- readDirectChannels = readDirectChannels.slice(index);
- showDirectChannels.sort(function directSort(a, b) {
- if (a.display_name < b.display_name) {
- return -1;
- }
- if (a.display_name > b.display_name) {
- return 1;
- }
- return 0;
- });
+ return {
+ activeId: currentId,
+ channels: ChannelStore.getAll(),
+ members: members,
+ showDirectChannels: showDirectChannels,
+ hideDirectChannels: readDirectChannels
+ };
}
-
- return {
- activeId: currentId,
- channels: ChannelStore.getAll(),
- members: members,
- showDirectChannels: showDirectChannels,
- hideDirectChannels: readDirectChannels
- };
-}
-
-module.exports = React.createClass({
- displayName: 'Sidebar',
- propTypes: {
- teamType: React.PropTypes.string,
- teamDisplayName: React.PropTypes.string
- },
- componentDidMount: function() {
+ componentDidMount() {
ChannelStore.addChangeListener(this.onChange);
UserStore.addChangeListener(this.onChange);
UserStore.addStatusesChangeListener(this.onChange);
@@ -140,12 +150,12 @@ module.exports = React.createClass({
this.updateUnreadIndicators();
$(window).on('resize', this.onResize);
- },
- componentDidUpdate: function() {
+ }
+ componentDidUpdate() {
this.updateTitle();
this.updateUnreadIndicators();
- },
- componentWillUnmount: function() {
+ }
+ componentWillUnmount() {
$(window).off('resize', this.onResize);
ChannelStore.removeChangeListener(this.onChange);
@@ -153,14 +163,14 @@ module.exports = React.createClass({
UserStore.removeStatusesChangeListener(this.onChange);
TeamStore.removeChangeListener(this.onChange);
SocketStore.removeChangeListener(this.onSocketChange);
- },
- onChange: function() {
- var newState = getStateFromStores();
- if (!utils.areStatesEqual(newState, this.state)) {
+ }
+ onChange() {
+ var newState = this.getStateFromStores();
+ if (!Utils.areStatesEqual(newState, this.state)) {
this.setState(newState);
}
- },
- onSocketChange: function(msg) {
+ }
+ onSocketChange(msg) {
if (msg.action === 'posted') {
if (ChannelStore.getCurrentId() === msg.channel_id) {
if (window.isActive) {
@@ -208,17 +218,17 @@ module.exports = React.createClass({
if (notifyText.length === 0) {
if (msgProps.image) {
- utils.notifyMe(title, username + ' uploaded an image', channel);
+ Utils.notifyMe(title, username + ' uploaded an image', channel);
} else if (msgProps.otherFile) {
- utils.notifyMe(title, username + ' uploaded a file', channel);
+ Utils.notifyMe(title, username + ' uploaded a file', channel);
} else {
- utils.notifyMe(title, username + ' did something new', channel);
+ Utils.notifyMe(title, username + ' did something new', channel);
}
} else {
- utils.notifyMe(title, username + ' wrote: ' + notifyText, channel);
+ Utils.notifyMe(title, username + ' wrote: ' + notifyText, channel);
}
if (!user.notify_props || user.notify_props.desktop_sound === 'true') {
- utils.ding();
+ Utils.ding();
}
}
} else if (msg.action === 'viewed') {
@@ -243,186 +253,196 @@ module.exports = React.createClass({
}
}
}
- },
- updateTitle: function() {
+ }
+ updateTitle() {
var channel = ChannelStore.getCurrent();
if (channel) {
if (channel.type === 'D') {
- var teammateUsername = utils.getDirectTeammate(channel.id).username;
+ var teammateUsername = Utils.getDirectTeammate(channel.id).username;
document.title = teammateUsername + ' ' + document.title.substring(document.title.lastIndexOf('-'));
} else {
document.title = channel.display_name + ' ' + document.title.substring(document.title.lastIndexOf('-'));
}
}
- },
- onScroll: function() {
+ }
+ onScroll() {
this.updateUnreadIndicators();
- },
- onResize: function() {
+ }
+ onResize() {
this.updateUnreadIndicators();
- },
- updateUnreadIndicators: function() {
- var container = $(this.refs.container.getDOMNode());
+ }
+ updateUnreadIndicators() {
+ var container = $(React.findDOMNode(this.refs.container));
if (this.firstUnreadChannel) {
- var firstUnreadElement = $(this.refs[this.firstUnreadChannel].getDOMNode());
+ var firstUnreadElement = $(React.findDOMNode(this.refs[this.firstUnreadChannel]));
if (firstUnreadElement.position().top + firstUnreadElement.height() < 0) {
- $(this.refs.topUnreadIndicator.getDOMNode()).css('display', 'initial');
+ $(React.findDOMNode(this.refs.topUnreadIndicator)).css('display', 'initial');
} else {
- $(this.refs.topUnreadIndicator.getDOMNode()).css('display', 'none');
+ $(React.findDOMNode(this.refs.topUnreadIndicator)).css('display', 'none');
}
}
if (this.lastUnreadChannel) {
- var lastUnreadElement = $(this.refs[this.lastUnreadChannel].getDOMNode());
+ var lastUnreadElement = $(React.findDOMNode(this.refs[this.lastUnreadChannel]));
if (lastUnreadElement.position().top > container.height()) {
- $(this.refs.bottomUnreadIndicator.getDOMNode()).css('display', 'initial');
+ $(React.findDOMNode(this.refs.bottomUnreadIndicator)).css('display', 'initial');
} else {
- $(this.refs.bottomUnreadIndicator.getDOMNode()).css('display', 'none');
+ $(React.findDOMNode(this.refs.bottomUnreadIndicator)).css('display', 'none');
}
}
- },
- getInitialState: function() {
- var newState = getStateFromStores();
- newState.loadingDMChannel = -1;
-
- return newState;
- },
- render: function() {
+ }
+ createChannelElement(channel, index) {
var members = this.state.members;
var activeId = this.state.activeId;
- var badgesActive = false;
+ var channelMember = members[channel.id];
+ var msgCount;
- // keep track of the first and last unread channels so we can use them to set the unread indicators
- var self = this;
- this.firstUnreadChannel = null;
- this.lastUnreadChannel = null;
-
- function createChannelElement(channel, index) {
- var channelMember = members[channel.id];
- var msgCount;
-
- var linkClass = '';
- if (channel.id === activeId) {
- linkClass = 'active';
- }
+ var linkClass = '';
+ if (channel.id === activeId) {
+ linkClass = 'active';
+ }
- var unread = false;
- if (channelMember) {
- msgCount = channel.total_msg_count - channelMember.msg_count;
- unread = (msgCount > 0 && channelMember.notify_level !== 'quiet') || channelMember.mention_count > 0;
- }
+ var unread = false;
+ if (channelMember) {
+ msgCount = channel.total_msg_count - channelMember.msg_count;
+ unread = (msgCount > 0 && channelMember.notify_level !== 'quiet') || channelMember.mention_count > 0;
+ }
- var titleClass = '';
- if (unread) {
- titleClass = 'unread-title';
+ var titleClass = '';
+ if (unread) {
+ titleClass = 'unread-title';
- if (!self.firstUnreadChannel) {
- self.firstUnreadChannel = channel.name;
- }
- self.lastUnreadChannel = channel.name;
- }
-
- var badge = null;
- if (channelMember) {
- if (channel.type === 'D') {
- // direct message channels show badges for any number of unread posts
- msgCount = channel.total_msg_count - channelMember.msg_count;
- if (msgCount > 0) {
- badge = <span className='badge pull-right small'>{msgCount}</span>;
- badgesActive = true;
- }
- } else if (channelMember.mention_count > 0) {
- // public and private channels only show badges for mentions
- badge = <span className='badge pull-right small'>{channelMember.mention_count}</span>;
- badgesActive = true;
- }
- } else if (self.state.loadingDMChannel === index && channel.type === 'D') {
- badge = <img className='channel-loading-gif pull-right' src='/static/images/load.gif'/>;
+ if (!this.firstUnreadChannel) {
+ this.firstUnreadChannel = channel.name;
}
+ this.lastUnreadChannel = channel.name;
+ }
- // set up status icon for direct message channels
- var status = null;
+ var badge = null;
+ if (channelMember) {
if (channel.type === 'D') {
- var statusIcon = '';
- if (channel.status === 'online') {
- statusIcon = Constants.ONLINE_ICON_SVG;
- } else if (channel.status === 'away') {
- statusIcon = Constants.ONLINE_ICON_SVG;
- } else {
- statusIcon = Constants.OFFLINE_ICON_SVG;
+ // direct message channels show badges for any number of unread posts
+ msgCount = channel.total_msg_count - channelMember.msg_count;
+ if (msgCount > 0) {
+ badge = <span className='badge pull-right small'>{msgCount}</span>;
+ this.badgesActive = true;
}
- status = <span className='status' dangerouslySetInnerHTML={{__html: statusIcon}} />;
+ } else if (channelMember.mention_count > 0) {
+ // public and private channels only show badges for mentions
+ badge = <span className='badge pull-right small'>{channelMember.mention_count}</span>;
+ this.badgesActive = true;
}
+ } else if (this.state.loadingDMChannel === index && channel.type === 'D') {
+ badge = (
+ <img
+ className='channel-loading-gif pull-right'
+ src='/static/images/load.gif'
+ />
+ );
+ }
- // set up click handler to switch channels (or create a new channel for non-existant ones)
- var handleClick = null;
- var href = '#';
- var teamURL = TeamStore.getCurrentTeamUrl();
+ // 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.ONLINE_ICON_SVG;
+ } else {
+ statusIcon = Constants.OFFLINE_ICON_SVG;
+ }
+ status = (
+ <span
+ className='status'
+ dangerouslySetInnerHTML={{__html: statusIcon}}
+ />
+ );
+ }
- if (!channel.fake) {
+ // set up click handler to switch channels (or create a new channel for non-existant ones)
+ var handleClick = null;
+ var href = '#';
+ var teamURL = TeamStore.getCurrentTeamUrl();
+
+ if (!channel.fake) {
+ handleClick = function clickHandler(e) {
+ e.preventDefault();
+ Utils.switchChannel(channel);
+ };
+ } else if (channel.fake && teamURL) {
+ // It's a direct message channel that doesn't exist yet so let's create it now
+ var otherUserId = Utils.getUserIdFromChannelName(channel);
+
+ if (this.state.loadingDMChannel === -1) {
handleClick = function clickHandler(e) {
e.preventDefault();
- utils.switchChannel(channel);
- };
- } else if (channel.fake && teamURL) {
- // It's a direct message channel that doesn't exist yet so let's create it now
- var otherUserId = utils.getUserIdFromChannelName(channel);
-
- if (self.state.loadingDMChannel === -1) {
- handleClick = function clickHandler(e) {
- e.preventDefault();
- self.setState({loadingDMChannel: index});
-
- Client.createDirectChannel(channel, otherUserId,
- function success(data) {
- self.setState({loadingDMChannel: -1});
- AsyncClient.getChannel(data.id);
- utils.switchChannel(data);
- },
- function error() {
- self.setState({loadingDMChannel: -1});
- window.location.href = TeamStore.getCurrentTeamUrl() + '/channels/' + channel.name;
- }
- );
- };
- }
+ this.setState({loadingDMChannel: index});
+
+ Client.createDirectChannel(channel, otherUserId,
+ function success(data) {
+ this.setState({loadingDMChannel: -1});
+ AsyncClient.getChannel(data.id);
+ Utils.switchChannel(data);
+ }.bind(this),
+ function error() {
+ this.setState({loadingDMChannel: -1});
+ window.location.href = TeamStore.getCurrentTeamUrl() + '/channels/' + channel.name;
+ }.bind(this)
+ );
+ }.bind(this);
}
-
- return (
- <li key={channel.name} ref={channel.name} className={linkClass}>
- <a className={'sidebar-channel ' + titleClass} href={href} onClick={handleClick}>
- {status}
- {channel.display_name}
- {badge}
- </a>
- </li>
- );
}
+ return (
+ <li
+ key={channel.name}
+ ref={channel.name}
+ className={linkClass}
+ >
+ <a
+ className={'sidebar-channel ' + titleClass}
+ href={href}
+ onClick={handleClick}
+ >
+ {status}
+ {channel.display_name}
+ {badge}
+ </a>
+ </li>
+ );
+ }
+ render() {
+ this.badgesActive = false;
+
+ // keep track of the first and last unread channels so we can use them to set the unread indicators
+ this.firstUnreadChannel = null;
+ this.lastUnreadChannel = null;
+
// create elements for all 3 types of channels
var channelItems = this.state.channels.filter(
function filterPublicChannels(channel) {
return channel.type === 'O';
}
- ).map(createChannelElement);
+ ).map(this.createChannelElement);
var privateChannelItems = this.state.channels.filter(
function filterPrivateChannels(channel) {
return channel.type === 'P';
}
- ).map(createChannelElement);
+ ).map(this.createChannelElement);
- var directMessageItems = this.state.showDirectChannels.map(createChannelElement);
+ var directMessageItems = this.state.showDirectChannels.map(this.createChannelElement);
// update the favicon to show if there are any notifications
var link = document.createElement('link');
link.type = 'image/x-icon';
link.rel = 'shortcut icon';
link.id = 'favicon';
- if (badgesActive) {
+ if (this.badgesActive) {
link.href = '/static/images/redfavicon.ico';
} else {
link.href = '/static/images/favicon.ico';
@@ -438,7 +458,13 @@ module.exports = React.createClass({
if (this.state.hideDirectChannels.length > 0) {
directMessageMore = (
<li>
- <a href='#' data-toggle='modal' className='nav-more' data-target='#more_direct_channels' data-channels={JSON.stringify(this.state.hideDirectChannels)}>
+ <a
+ href='#'
+ data-toggle='modal'
+ className='nav-more'
+ data-target='#more_direct_channels'
+ data-channels={JSON.stringify(this.state.hideDirectChannels)}
+ >
{'More (' + this.state.hideDirectChannels.length + ')'}
</a>
</li>
@@ -447,21 +473,76 @@ module.exports = React.createClass({
return (
<div>
- <SidebarHeader teamDisplayName={this.props.teamDisplayName} teamType={this.props.teamType} />
+ <SidebarHeader
+ teamDisplayName={this.props.teamDisplayName}
+ teamType={this.props.teamType}
+ />
<SearchBox />
- <div ref='topUnreadIndicator' className='nav-pills__unread-indicator nav-pills__unread-indicator-top' style={{display: 'none'}}>Unread post(s) above</div>
- <div ref='bottomUnreadIndicator' className='nav-pills__unread-indicator nav-pills__unread-indicator-bottom' style={{display: 'none'}}>Unread post(s) below</div>
+ <div
+ ref='topUnreadIndicator'
+ className='nav-pills__unread-indicator nav-pills__unread-indicator-top'
+ style={{display: 'none'}}
+ >
+ Unread post(s) above
+ </div>
+ <div
+ ref='bottomUnreadIndicator'
+ className='nav-pills__unread-indicator nav-pills__unread-indicator-bottom'
+ style={{display: 'none'}}
+ >
+ Unread post(s) below
+ </div>
- <div ref='container' className='nav-pills__container' onScroll={this.onScroll}>
+ <div
+ ref='container'
+ className='nav-pills__container'
+ onScroll={this.onScroll}
+ >
<ul className='nav nav-pills nav-stacked'>
- <li><h4>Channels<a className='add-channel-btn' href='#' data-toggle='modal' data-target='#new_channel' data-channeltype='O'>+</a></h4></li>
+ <li>
+ <h4>
+ Channels
+ <a
+ className='add-channel-btn'
+ href='#'
+ data-toggle='modal'
+ data-target='#new_channel'
+ data-channeltype='O'
+ >
+ +
+ </a>
+ </h4>
+ </li>
{channelItems}
- <li><a href='#' data-toggle='modal' className='nav-more' data-target='#more_channels' data-channeltype='O'>More...</a></li>
+ <li>
+ <a
+ href='#'
+ data-toggle='modal'
+ className='nav-more'
+ data-target='#more_channels'
+ data-channeltype='O'
+ >
+ More...
+ </a>
+ </li>
</ul>
<ul className='nav nav-pills nav-stacked'>
- <li><h4>Private Groups<a className='add-channel-btn' href='#' data-toggle='modal' data-target='#new_channel' data-channeltype='P'>+</a></h4></li>
+ <li>
+ <h4>
+ Private Groups
+ <a
+ className='add-channel-btn'
+ href='#'
+ data-toggle='modal'
+ data-target='#new_channel'
+ data-channeltype='P'
+ >
+ +
+ </a>
+ </h4>
+ </li>
{privateChannelItems}
</ul>
<ul className='nav nav-pills nav-stacked'>
@@ -473,4 +554,13 @@ module.exports = React.createClass({
</div>
);
}
-});
+}
+
+Sidebar.defaultProps = {
+ teamType: '',
+ teamDisplayName: ''
+};
+Sidebar.propTypes = {
+ teamType: React.PropTypes.string,
+ teamDisplayName: React.PropTypes.string
+};