2015-10-08 12:27:09 -04:00
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
2015-06-14 23:53:32 -08:00
// See License.txt for license information.
2016-03-14 08:50:46 -04:00
import $ from 'jquery' ;
import ReactDOM from 'react-dom' ;
2015-11-19 21:12:56 -05:00
import NewChannelFlow from './new_channel_flow.jsx' ;
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' ;
2016-06-15 14:30:32 +02:00
import StatusIcon from './status_icon.jsx' ;
2015-11-19 21:12:56 -05:00
2016-03-14 08:50:46 -04:00
import ChannelStore from 'stores/channel_store.jsx' ;
import UserStore from 'stores/user_store.jsx' ;
import TeamStore from 'stores/team_store.jsx' ;
import PreferenceStore from 'stores/preference_store.jsx' ;
2015-11-19 21:12:56 -05:00
2016-03-14 08:50:46 -04:00
import * as AsyncClient from 'utils/async_client.jsx' ;
import * as Utils from 'utils/utils.jsx' ;
2016-06-01 13:05:48 -07:00
import * as ChannelActions from 'actions/channel_actions.jsx' ;
2015-11-19 21:12:56 -05:00
2016-03-14 08:50:46 -04:00
import Constants from 'utils/constants.jsx' ;
2016-01-31 22:03:30 -03:00
2016-03-14 08:50:46 -04:00
import { FormattedMessage , FormattedHTMLMessage } from 'react-intl' ;
2016-01-31 22:03:30 -03:00
2015-10-30 11:35:16 -04:00
const Preferences = Constants . Preferences ;
const TutorialSteps = Constants . TutorialSteps ;
2016-03-14 08:50:46 -04:00
import { Tooltip , OverlayTrigger } from 'react-bootstrap' ;
import loadingGif from 'images/load.gif' ;
import React from 'react' ;
2016-06-22 10:30:01 -04:00
import { browserHistory , Link } from 'react-router/es6' ;
2016-03-14 08:50:46 -04:00
import favicon from 'images/favicon/favicon-16x16.png' ;
import redFavicon from 'images/favicon/redfavicon-16x16.png' ;
2015-06-14 23:53:32 -08:00
2015-09-01 08:37:09 -04:00
export default class Sidebar extends React . Component {
constructor ( props ) {
super ( props ) ;
2015-06-14 23:53:32 -08:00
2015-09-01 08:37:09 -04:00
this . badgesActive = false ;
2015-09-16 14:53:16 -04:00
this . firstUnreadChannel = null ;
this . lastUnreadChannel = null ;
2015-06-14 23:53:32 -08:00
2015-10-02 09:50:34 -04:00
this . getStateFromStores = this . getStateFromStores . bind ( this ) ;
2015-09-01 08:37:09 -04:00
this . onChange = this . onChange . bind ( this ) ;
this . onScroll = this . onScroll . bind ( this ) ;
2015-09-16 14:53:16 -04:00
this . updateUnreadIndicators = this . updateUnreadIndicators . bind ( this ) ;
2015-10-02 14:25:55 -04:00
this . handleLeaveDirectChannel = this . handleLeaveDirectChannel . bind ( this ) ;
2015-10-14 16:57:03 -04:00
2015-12-18 17:58:08 -05:00
this . showMoreChannelsModal = this . showMoreChannelsModal . bind ( this ) ;
2015-10-14 16:57:03 -04:00
this . showNewChannelModal = this . showNewChannelModal . bind ( this ) ;
this . hideNewChannelModal = this . hideNewChannelModal . bind ( this ) ;
this . showMoreDirectChannelsModal = this . showMoreDirectChannelsModal . bind ( this ) ;
this . hideMoreDirectChannelsModal = this . hideMoreDirectChannelsModal . bind ( this ) ;
2015-09-01 08:37:09 -04:00
this . createChannelElement = this . createChannelElement . bind ( this ) ;
2015-10-25 15:12:36 +01:00
this . updateTitle = this . updateTitle . bind ( this ) ;
2015-06-14 23:53:32 -08:00
2016-06-01 13:05:48 -07:00
this . navigateChannelShortcut = this . navigateChannelShortcut . bind ( this ) ;
this . navigateUnreadChannelShortcut = this . navigateUnreadChannelShortcut . bind ( this ) ;
this . getDisplayedChannels = this . getDisplayedChannels . bind ( this ) ;
this . updateScrollbarOnChannelChange = this . updateScrollbarOnChannelChange . bind ( this ) ;
2015-10-07 11:34:29 -04:00
this . isLeaving = new Map ( ) ;
2015-10-05 09:58:42 -04:00
const state = this . getStateFromStores ( ) ;
2015-10-14 16:57:03 -04:00
state . newChannelModalType = '' ;
2015-10-16 10:15:52 -04:00
state . showDirectChannelsModal = false ;
2015-10-05 09:58:42 -04:00
state . loadingDMChannel = - 1 ;
this . state = state ;
2015-10-25 15:12:36 +01:00
}
2016-06-15 05:01:31 -07:00
2015-12-02 10:22:39 -05:00
getTotalUnreadCount ( ) {
2015-10-25 15:12:36 +01:00
let msgs = 0 ;
2015-12-02 10:22:39 -05:00
let mentions = 0 ;
const unreadCounts = this . state . unreadCounts ;
2015-10-25 15:12:36 +01:00
2015-12-02 10:22:39 -05:00
Object . keys ( unreadCounts ) . forEach ( ( chId ) => {
msgs += unreadCounts [ chId ] . msgs ;
mentions += unreadCounts [ chId ] . mentions ;
2015-10-25 15:12:36 +01:00
} ) ;
return { msgs , mentions } ;
}
2016-06-15 05:01:31 -07:00
2015-09-01 08:37:09 -04:00
getStateFromStores ( ) {
2015-10-02 09:50:34 -04:00
const members = ChannelStore . getAllMembers ( ) ;
2015-11-03 14:52:25 -05:00
const currentChannelId = ChannelStore . getCurrentId ( ) ;
2015-12-08 09:45:21 -05:00
const currentUserId = UserStore . getCurrentId ( ) ;
2015-11-03 14:52:25 -05:00
const channels = Object . assign ( [ ] , ChannelStore . getAll ( ) ) ;
2015-11-12 11:38:52 -05:00
channels . sort ( ( a , b ) => a . display _name . localeCompare ( b . display _name ) ) ;
2015-11-03 14:52:25 -05:00
const publicChannels = channels . filter ( ( channel ) => channel . type === Constants . OPEN _CHANNEL ) ;
const privateChannels = channels . filter ( ( channel ) => channel . type === Constants . PRIVATE _CHANNEL ) ;
2015-06-14 23:53:32 -08:00
2015-12-15 11:36:14 -05:00
const preferences = PreferenceStore . getCategory ( Constants . Preferences . CATEGORY _DIRECT _CHANNEL _SHOW ) ;
2015-10-02 09:50:34 -04:00
2015-12-08 09:45:21 -05:00
const directChannels = [ ] ;
2016-05-06 12:08:58 -04:00
const directNonTeamChannels = [ ] ;
2016-03-23 13:03:12 -04:00
for ( const [ name , value ] of preferences ) {
if ( value !== 'true' ) {
2015-11-03 15:11:16 -05:00
continue ;
}
2015-06-14 23:53:32 -08:00
2016-03-23 13:03:12 -04:00
const teammateId = name ;
2015-09-01 08:37:09 -04:00
2015-12-08 09:45:21 -05:00
let directChannel = channels . find ( Utils . isDirectChannelForUser . bind ( null , teammateId ) ) ;
2015-10-02 14:25:55 -04:00
2015-12-08 09:45:21 -05:00
// a direct channel doesn't exist yet so create a fake one
2016-02-08 08:29:06 -05:00
if ( directChannel == null ) {
2015-12-08 09:45:21 -05:00
directChannel = {
name : Utils . getDirectChannelName ( currentUserId , teammateId ) ,
last _post _at : 0 ,
total _msg _count : 0 ,
type : Constants . DM _CHANNEL ,
fake : true
} ;
2016-02-08 08:29:06 -05:00
} else {
directChannel = JSON . parse ( JSON . stringify ( directChannel ) ) ;
2015-06-14 23:53:32 -08:00
}
2015-12-08 09:45:21 -05:00
directChannel . display _name = Utils . displayUsername ( teammateId ) ;
directChannel . teammate _id = teammateId ;
directChannel . status = UserStore . getStatus ( teammateId ) ;
2016-05-06 12:08:58 -04:00
if ( UserStore . hasTeamProfile ( teammateId ) ) {
directChannels . push ( directChannel ) ;
} else {
directNonTeamChannels . push ( directChannel ) ;
}
2015-06-14 23:53:32 -08:00
}
2015-12-08 09:45:21 -05:00
directChannels . sort ( this . sortChannelsByDisplayName ) ;
2016-05-06 12:08:58 -04:00
directNonTeamChannels . sort ( this . sortChannelsByDisplayName ) ;
2015-11-03 14:52:25 -05:00
2015-12-15 11:36:14 -05:00
const tutorialStep = PreferenceStore . getInt ( Preferences . TUTORIAL _STEP , UserStore . getCurrentId ( ) , 999 ) ;
2015-10-30 11:35:16 -04:00
2015-09-01 08:37:09 -04:00
return {
2015-11-03 14:52:25 -05:00
activeId : currentChannelId ,
2015-10-07 13:07:59 -04:00
members ,
2015-11-03 14:52:25 -05:00
publicChannels ,
privateChannels ,
2015-12-08 09:45:21 -05:00
directChannels ,
2016-05-06 12:08:58 -04:00
directNonTeamChannels ,
2015-12-02 10:22:39 -05:00
unreadCounts : JSON . parse ( JSON . stringify ( ChannelStore . getUnreadCounts ( ) ) ) ,
2016-02-08 07:26:10 -05:00
showTutorialTip : tutorialStep === TutorialSteps . CHANNEL _POPOVER ,
currentTeam : TeamStore . getCurrent ( ) ,
2016-04-01 09:11:27 -04:00
currentUser : UserStore . getCurrentUser ( ) ,
townSquare : ChannelStore . getByName ( Constants . DEFAULT _CHANNEL ) ,
offTopic : ChannelStore . getByName ( Constants . OFFTOPIC _CHANNEL )
2015-09-01 08:37:09 -04:00
} ;
2015-08-11 11:18:15 -04:00
}
2015-10-02 09:50:34 -04:00
2015-09-01 08:37:09 -04:00
componentDidMount ( ) {
2015-08-03 12:09:27 -07:00
ChannelStore . addChangeListener ( this . onChange ) ;
UserStore . addChangeListener ( this . onChange ) ;
UserStore . addStatusesChangeListener ( this . onChange ) ;
2015-08-13 11:00:49 -07:00
TeamStore . addChangeListener ( this . onChange ) ;
2015-10-02 09:50:34 -04:00
PreferenceStore . addChangeListener ( this . onChange ) ;
2015-06-14 23:53:32 -08:00
this . updateTitle ( ) ;
2015-09-16 14:53:16 -04:00
this . updateUnreadIndicators ( ) ;
2016-06-01 13:05:48 -07:00
document . addEventListener ( 'keydown' , this . navigateChannelShortcut ) ;
document . addEventListener ( 'keydown' , this . navigateUnreadChannelShortcut ) ;
2015-09-01 08:37:09 -04:00
}
2016-06-15 05:01:31 -07:00
2015-09-16 14:53:16 -04:00
shouldComponentUpdate ( nextProps , nextState ) {
2015-11-12 11:19:59 -05:00
if ( ! Utils . areObjectsEqual ( nextState , this . state ) ) {
2015-09-16 14:53:16 -04:00
return true ;
}
return false ;
}
2016-06-15 05:01:31 -07:00
2016-05-02 08:08:53 -04:00
componentDidUpdate ( prevProps , prevState ) {
2015-06-14 23:53:32 -08:00
this . updateTitle ( ) ;
2015-09-16 14:53:16 -04:00
this . updateUnreadIndicators ( ) ;
2016-03-23 04:06:08 +05:00
if ( ! Utils . isMobile ( ) ) {
$ ( '.sidebar--left .nav-pills__container' ) . perfectScrollbar ( ) ;
}
2016-04-25 10:46:56 -04:00
2016-05-31 05:13:43 -07:00
// reset the scrollbar upon switching teams
if ( this . state . currentTeam !== prevState . currentTeam ) {
this . refs . container . scrollTop = 0 ;
$ ( '.nav-pills__container' ) . perfectScrollbar ( 'update' ) ;
}
2016-04-25 10:46:56 -04:00
// close the LHS on mobile when you change channels
2016-05-02 08:08:53 -04:00
if ( this . state . activeId !== prevState . activeId ) {
$ ( '.app__body .inner-wrap' ) . removeClass ( 'move--right' ) ;
$ ( '.app__body .sidebar--left' ) . removeClass ( 'move--right' ) ;
}
2015-09-01 08:37:09 -04:00
}
2016-06-15 05:01:31 -07:00
2015-09-01 08:37:09 -04:00
componentWillUnmount ( ) {
2015-08-03 12:09:27 -07:00
ChannelStore . removeChangeListener ( this . onChange ) ;
UserStore . removeChangeListener ( this . onChange ) ;
UserStore . removeStatusesChangeListener ( this . onChange ) ;
2015-08-13 11:00:49 -07:00
TeamStore . removeChangeListener ( this . onChange ) ;
2015-10-02 09:50:34 -04:00
PreferenceStore . removeChangeListener ( this . onChange ) ;
2016-06-01 13:05:48 -07:00
document . removeEventListener ( 'keydown' , this . navigateChannelShortcut ) ;
document . removeEventListener ( 'keydown' , this . navigateUnreadChannelShortcut ) ;
2015-09-01 08:37:09 -04:00
}
2016-06-15 05:01:31 -07:00
2015-09-01 08:37:09 -04:00
onChange ( ) {
2015-11-12 11:19:59 -05:00
this . setState ( this . getStateFromStores ( ) ) ;
2015-09-01 08:37:09 -04:00
}
2016-06-15 05:01:31 -07:00
2015-09-01 08:37:09 -04:00
updateTitle ( ) {
2015-09-28 14:58:14 -07:00
const channel = ChannelStore . getCurrent ( ) ;
2016-02-08 07:26:10 -05:00
if ( channel && this . state . currentTeam ) {
2015-09-28 14:58:14 -07:00
let currentSiteName = '' ;
2015-10-16 09:10:54 -07:00
if ( global . window . mm _config . SiteName != null ) {
currentSiteName = global . window . mm _config . SiteName ;
2015-09-28 14:58:14 -07:00
}
let currentChannelName = channel . display _name ;
2015-06-14 23:53:32 -08:00
if ( channel . type === 'D' ) {
2016-02-01 08:01:49 -05:00
const teammate = Utils . getDirectTeammate ( channel . id ) ;
if ( teammate != null ) {
currentChannelName = teammate . username ;
}
2015-06-14 23:53:32 -08:00
}
2015-09-28 14:58:14 -07:00
2015-12-02 10:22:39 -05:00
const unread = this . getTotalUnreadCount ( ) ;
2015-10-25 15:12:36 +01:00
const mentionTitle = unread . mentions > 0 ? '(' + unread . mentions + ') ' : '' ;
const unreadTitle = unread . msgs > 0 ? '* ' : '' ;
2016-02-08 07:26:10 -05:00
document . title = mentionTitle + unreadTitle + currentChannelName + ' - ' + this . state . currentTeam . display _name + ' ' + currentSiteName ;
2015-06-14 23:53:32 -08:00
}
2015-09-01 08:37:09 -04:00
}
2016-06-15 05:01:31 -07:00
2015-09-01 08:37:09 -04:00
onScroll ( ) {
2015-09-16 14:53:16 -04:00
this . updateUnreadIndicators ( ) ;
2015-09-01 08:37:09 -04:00
}
2016-06-15 05:01:31 -07:00
2015-09-16 14:53:16 -04:00
updateUnreadIndicators ( ) {
2015-10-15 12:07:06 -04:00
const container = $ ( ReactDOM . findDOMNode ( this . refs . container ) ) ;
2015-09-16 14:53:16 -04:00
var showTopUnread = false ;
var showBottomUnread = false ;
if ( this . firstUnreadChannel ) {
2015-10-15 12:07:06 -04:00
var firstUnreadElement = $ ( ReactDOM . findDOMNode ( this . refs [ this . firstUnreadChannel ] ) ) ;
2015-09-16 14:53:16 -04:00
if ( firstUnreadElement . position ( ) . top + firstUnreadElement . height ( ) < 0 ) {
showTopUnread = true ;
}
}
if ( this . lastUnreadChannel ) {
2015-10-15 12:07:06 -04:00
var lastUnreadElement = $ ( ReactDOM . findDOMNode ( this . refs [ this . lastUnreadChannel ] ) ) ;
2015-09-16 14:53:16 -04:00
if ( lastUnreadElement . position ( ) . top > container . height ( ) ) {
showBottomUnread = true ;
}
}
this . setState ( {
showTopUnread ,
showBottomUnread
} ) ;
}
2016-06-15 05:01:31 -07:00
2016-06-01 13:05:48 -07:00
updateScrollbarOnChannelChange ( channel ) {
const curChannel = this . refs [ channel . name ] . getBoundingClientRect ( ) ;
if ( ( curChannel . top - Constants . CHANNEL _SCROLL _ADJUSTMENT < 0 ) || ( curChannel . top + curChannel . height > this . refs . container . getBoundingClientRect ( ) . height ) ) {
this . refs . container . scrollTop = this . refs . container . scrollTop + ( curChannel . top - Constants . CHANNEL _SCROLL _ADJUSTMENT ) ;
$ ( '.nav-pills__container' ) . perfectScrollbar ( 'update' ) ;
}
}
2016-06-15 05:01:31 -07:00
2016-06-01 13:05:48 -07:00
navigateChannelShortcut ( e ) {
if ( e . altKey && ! e . shiftKey && ( e . keyCode === Constants . KeyCodes . UP || e . keyCode === Constants . KeyCodes . DOWN ) ) {
e . preventDefault ( ) ;
const allChannels = this . getDisplayedChannels ( ) ;
const curChannelId = this . state . activeId ;
let curIndex = - 1 ;
for ( let i = 0 ; i < allChannels . length ; i ++ ) {
if ( allChannels [ i ] . id === curChannelId ) {
curIndex = i ;
}
}
let nextChannel = allChannels [ curIndex ] ;
let nextIndex = curIndex ;
if ( e . keyCode === Constants . KeyCodes . DOWN ) {
nextIndex = curIndex + 1 ;
} else if ( e . keyCode === Constants . KeyCodes . UP ) {
nextIndex = curIndex - 1 ;
}
nextChannel = allChannels [ Utils . mod ( nextIndex , allChannels . length ) ] ;
ChannelActions . goToChannel ( nextChannel ) ;
this . updateScrollbarOnChannelChange ( nextChannel ) ;
}
}
2016-06-15 05:01:31 -07:00
2016-06-01 13:05:48 -07:00
navigateUnreadChannelShortcut ( e ) {
if ( e . altKey && e . shiftKey && ( e . keyCode === Constants . KeyCodes . UP || e . keyCode === Constants . KeyCodes . DOWN ) ) {
e . preventDefault ( ) ;
const allChannels = this . getDisplayedChannels ( ) ;
const curChannelId = this . state . activeId ;
let curIndex = - 1 ;
for ( let i = 0 ; i < allChannels . length ; i ++ ) {
if ( allChannels [ i ] . id === curChannelId ) {
curIndex = i ;
}
}
let nextChannel = allChannels [ curIndex ] ;
let nextIndex = curIndex ;
let count = 0 ;
let increment = 0 ;
if ( e . keyCode === Constants . KeyCodes . UP ) {
increment = - 1 ;
} else if ( e . keyCode === Constants . KeyCodes . DOWN ) {
increment = 1 ;
}
let unreadCounts = ChannelStore . getUnreadCount ( allChannels [ nextIndex ] . id ) ;
while ( count < allChannels . length && unreadCounts . msgs === 0 && unreadCounts . mentions === 0 ) {
nextIndex += increment ;
count ++ ;
nextIndex = Utils . mod ( nextIndex , allChannels . length ) ;
unreadCounts = ChannelStore . getUnreadCount ( allChannels [ nextIndex ] . id ) ;
}
if ( unreadCounts . msgs !== 0 || unreadCounts . mentions !== 0 ) {
nextChannel = allChannels [ nextIndex ] ;
ChannelActions . goToChannel ( nextChannel ) ;
this . updateScrollbarOnChannelChange ( nextChannel ) ;
}
}
}
2016-06-15 05:01:31 -07:00
2016-06-01 13:05:48 -07:00
getDisplayedChannels ( ) {
return this . state . publicChannels . concat ( this . state . privateChannels ) . concat ( this . state . directChannels ) . concat ( this . state . directNonTeamChannels ) ;
}
2016-06-15 05:01:31 -07:00
2016-03-30 10:14:34 -04:00
handleLeaveDirectChannel ( e , channel ) {
e . preventDefault ( ) ;
2015-10-07 11:34:29 -04:00
if ( ! this . isLeaving . get ( channel . id ) ) {
this . isLeaving . set ( channel . id , true ) ;
2015-10-02 14:25:55 -04:00
2016-03-23 13:03:12 -04:00
AsyncClient . savePreference (
Constants . Preferences . CATEGORY _DIRECT _CHANNEL _SHOW ,
channel . teammate _id ,
'false' ,
2015-10-02 14:25:55 -04:00
( ) => {
2015-10-07 11:34:29 -04:00
this . isLeaving . set ( channel . id , false ) ;
2015-10-02 14:25:55 -04:00
} ,
( ) => {
2015-10-07 11:34:29 -04:00
this . isLeaving . set ( channel . id , false ) ;
2015-10-02 14:25:55 -04:00
}
) ;
this . setState ( this . getStateFromStores ( ) ) ;
}
if ( channel . id === this . state . activeId ) {
2016-03-30 10:14:34 -04:00
browserHistory . push ( '/' + this . state . currentTeam . name + '/channels/town-square' ) ;
2015-10-02 14:25:55 -04:00
}
}
sortChannelsByDisplayName ( a , b ) {
return a . display _name . localeCompare ( b . display _name ) ;
}
2015-12-18 17:58:08 -05:00
showMoreChannelsModal ( ) {
// manually show the modal because using data-toggle messes with keyboard focus when the modal is dismissed
$ ( '#more_channels' ) . modal ( { 'data-channeltype' : 'O' } ) . modal ( 'show' ) ;
}
2015-10-14 16:57:03 -04:00
showNewChannelModal ( type ) {
this . setState ( { newChannelModalType : type } ) ;
}
2016-06-15 05:01:31 -07:00
2015-10-14 16:57:03 -04:00
hideNewChannelModal ( ) {
this . setState ( { newChannelModalType : '' } ) ;
}
showMoreDirectChannelsModal ( ) {
this . setState ( { showDirectChannelsModal : true } ) ;
}
2016-06-15 05:01:31 -07:00
2015-10-14 16:57:03 -04:00
hideMoreDirectChannelsModal ( ) {
this . setState ( { showDirectChannelsModal : false } ) ;
}
2016-06-14 22:53:47 +05:00
openLeftSidebar ( ) {
if ( Utils . isMobile ( ) ) {
setTimeout ( ( ) => {
document . querySelector ( '.app__body .inner-wrap' ) . classList . add ( 'move--right' ) ;
document . querySelector ( '.app__body .sidebar--left' ) . classList . add ( 'move--right' ) ;
} ) ;
}
}
2015-10-30 11:35:16 -04:00
createTutorialTip ( ) {
const screens = [ ] ;
2016-04-01 09:11:27 -04:00
let townSquareDisplayName = Constants . DEFAULT _CHANNEL _UI _NAME ;
if ( this . state . townSquare ) {
townSquareDisplayName = this . state . townSquare . display _name ;
}
let offTopicDisplayName = Constants . OFFTOPIC _CHANNEL _UI _NAME ;
if ( this . state . offTopic ) {
offTopicDisplayName = this . state . offTopic . display _name ;
}
2015-10-30 11:35:16 -04:00
screens . push (
< div >
2016-01-31 22:03:30 -03:00
< FormattedHTMLMessage
id = 'sidebar.tutorialScreen1'
defaultMessage = '<h4>Channels</h4><p><strong>Channels</strong> organize conversations across different topics. They’ re open to everyone on your team. To send private communications use <strong>Direct Messages</strong> for a single person or <strong>Private Groups</strong> for multiple people.</p>'
/ >
2015-10-30 11:35:16 -04:00
< / div >
) ;
screens . push (
< div >
2016-01-31 22:03:30 -03:00
< FormattedHTMLMessage
id = 'sidebar.tutorialScreen2'
2016-04-01 09:11:27 -04:00
defaultMessage = ' < h4 > "{townsquare}" and "{offtopic}" channels < / h4 >
2016-01-31 22:03:30 -03:00
< p > Here are two public channels to start : < / p >
2016-04-01 09:11:27 -04:00
< p > < strong > { townsquare } < / strong > is a place for team - wide communication . Everyone in your team is a member of this channel . < / p >
< p > < strong > { offtopic } < / strong > is a place for fun and humor outside of work - related channels . You and your team can decide what other channels to create . < / p > '
values = { {
townsquare : townSquareDisplayName ,
offtopic : offTopicDisplayName
} }
2016-01-31 22:03:30 -03:00
/ >
2015-10-30 11:35:16 -04:00
< / div >
) ;
screens . push (
< div >
2016-01-31 22:03:30 -03:00
< FormattedHTMLMessage
id = 'sidebar.tutorialScreen3'
defaultMessage = ' < h4 > Creating and Joining Channels < / h4 >
< p > Click < strong > "More..." < / strong > to create a new channel or join an existing one . < / p >
< p > You can also create a new channel or private group by clicking the < strong > "+" symbol < / strong > next to the channel or private group header . < / p > '
/ >
2015-10-30 11:35:16 -04:00
< / div >
) ;
return (
< TutorialTip
placement = 'right'
screens = { screens }
2015-11-02 09:21:08 -05:00
overlayClass = 'tip-overlay--sidebar'
2015-10-30 11:35:16 -04:00
/ >
) ;
}
2015-10-02 14:25:55 -04:00
createChannelElement ( channel , index , arr , handleClose ) {
2015-12-02 10:22:39 -05:00
const members = this . state . members ;
const activeId = this . state . activeId ;
const channelMember = members [ channel . id ] ;
const unreadCount = this . state . unreadCounts [ channel . id ] || { msgs : 0 , mentions : 0 } ;
let msgCount ;
2015-06-14 23:53:32 -08:00
2015-12-02 10:22:39 -05:00
let linkClass = '' ;
2015-09-01 08:37:09 -04:00
if ( channel . id === activeId ) {
linkClass = 'active' ;
}
2015-06-14 23:53:32 -08:00
2015-10-13 14:35:38 -04:00
let rowClass = 'sidebar-channel' ;
2015-09-01 08:37:09 -04:00
var unread = false ;
if ( channelMember ) {
2015-10-25 15:12:36 +01:00
msgCount = unreadCount . msgs + unreadCount . mentions ;
2015-11-05 09:39:07 -05:00
unread = msgCount > 0 || channelMember . mention _count > 0 ;
2015-09-01 08:37:09 -04:00
}
2015-06-14 23:53:32 -08:00
2015-09-01 08:37:09 -04:00
if ( unread ) {
2015-10-13 14:35:38 -04:00
rowClass += ' unread-title' ;
2015-08-06 16:28:43 -04:00
2015-09-09 15:21:44 -04:00
if ( channel . id !== activeId ) {
2015-09-16 14:53:16 -04:00
if ( ! this . firstUnreadChannel ) {
this . firstUnreadChannel = channel . name ;
}
this . lastUnreadChannel = channel . name ;
2015-06-14 23:53:32 -08:00
}
2015-09-01 08:37:09 -04:00
}
2015-06-14 23:53:32 -08:00
2015-09-01 08:37:09 -04:00
var badge = null ;
if ( channelMember ) {
2015-10-25 15:12:36 +01:00
if ( unreadCount . mentions ) {
badge = < span className = 'badge pull-right small' > { unreadCount . mentions } < / span > ;
2015-09-01 08:37:09 -04:00
this . badgesActive = true ;
2015-06-14 23:53:32 -08:00
}
2015-09-02 14:26:01 -04:00
} else if ( this . state . loadingDMChannel === index && channel . type === 'D' ) {
2015-09-01 08:37:09 -04:00
badge = (
< img
className = 'channel-loading-gif pull-right'
2016-03-14 08:50:46 -04:00
src = { loadingGif }
2015-09-01 08:37:09 -04:00
/ >
) ;
}
2015-06-14 23:53:32 -08:00
2015-09-16 20:32:10 +05:00
if ( msgCount > 0 ) {
2015-10-13 14:35:38 -04:00
rowClass += ' has-badge' ;
2015-09-16 20:32:10 +05:00
}
2015-12-15 21:12:19 +05:00
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 > ;
2016-06-15 14:30:32 +02:00
} else {
// set up status icon for direct message channels (status is null for other channel types)
icon = < StatusIcon status = { channel . status } / > ;
2015-12-15 21:12:19 +05:00
}
2015-10-02 14:25:55 -04:00
let closeButton = null ;
2015-10-16 10:15:52 -04:00
const removeTooltip = (
2016-01-31 22:03:30 -03:00
< Tooltip id = 'remove-dm-tooltip' >
< FormattedMessage
id = 'sidebar.removeList'
defaultMessage = 'Remove from list'
/ >
< / Tooltip >
2015-10-16 10:15:52 -04:00
) ;
2015-10-05 13:16:39 -04:00
if ( handleClose && ! badge ) {
2015-10-02 14:25:55 -04:00
closeButton = (
2015-10-14 17:04:13 +05:00
< OverlayTrigger
2015-10-16 10:15:52 -04:00
delayShow = { 1000 }
2015-10-14 17:04:13 +05:00
placement = 'top'
overlay = { removeTooltip }
>
2016-03-24 20:04:40 -04:00
< span
2016-03-30 10:14:34 -04:00
onClick = { ( e ) => handleClose ( e , channel ) }
2016-03-24 20:04:40 -04:00
className = 'btn-close'
>
{ '× ' }
< / span >
2015-10-14 17:04:13 +05:00
< / OverlayTrigger >
2015-10-02 14:25:55 -04:00
) ;
2015-10-13 14:35:38 -04:00
2015-10-14 09:11:35 -04:00
rowClass += ' has-close' ;
2015-10-02 14:25:55 -04:00
}
2015-10-30 11:35:16 -04:00
let tutorialTip = null ;
if ( this . state . showTutorialTip && channel . name === Constants . DEFAULT _CHANNEL ) {
tutorialTip = this . createTutorialTip ( ) ;
2016-06-14 22:53:47 +05:00
this . openLeftSidebar ( ) ;
2015-10-30 11:35:16 -04:00
}
2016-03-24 20:04:40 -04:00
let link = '' ;
if ( channel . fake ) {
2016-03-29 16:51:27 -04:00
link = '/' + this . state . currentTeam . name + '/channels/' + channel . name + '?fakechannel=' + encodeURIComponent ( JSON . stringify ( channel ) ) ;
2016-03-24 20:04:40 -04:00
} else {
2016-03-29 16:51:27 -04:00
link = '/' + this . state . currentTeam . name + '/channels/' + channel . name ;
2016-03-24 20:04:40 -04:00
}
2015-09-01 08:37:09 -04:00
return (
< li
key = { channel . name }
ref = { channel . name }
className = { linkClass }
>
2016-03-24 20:04:40 -04:00
< Link
to = { link }
2015-10-13 14:35:38 -04:00
className = { rowClass }
2015-09-01 08:37:09 -04:00
>
2015-12-15 21:12:19 +05:00
{ icon }
2015-09-01 08:37:09 -04:00
{ channel . display _name }
{ badge }
2015-10-02 14:25:55 -04:00
{ closeButton }
2016-03-24 20:04:40 -04:00
< / Link >
2015-10-30 11:35:16 -04:00
{ tutorialTip }
2015-09-01 08:37:09 -04:00
< / li >
) ;
}
render ( ) {
2016-02-08 07:26:10 -05:00
// Check if we have all info needed to render
if ( this . state . currentTeam == null || this . state . currentUser == null ) {
return ( < div / > ) ;
}
2016-05-04 10:10:19 -04:00
this . lastBadgesActive = this . badgesActive ;
2015-09-01 08:37:09 -04:00
this . badgesActive = false ;
2015-09-16 14:53:16 -04:00
// 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 ;
2015-09-01 08:37:09 -04:00
2015-08-06 14:03:45 -04:00
// create elements for all 3 types of channels
2015-11-03 14:52:25 -05:00
const publicChannelItems = this . state . publicChannels . map ( this . createChannelElement ) ;
2015-06-14 23:53:32 -08:00
2015-11-03 14:52:25 -05:00
const privateChannelItems = this . state . privateChannels . map ( this . createChannelElement ) ;
2015-08-06 14:03:45 -04:00
2015-12-08 09:45:21 -05:00
const directMessageItems = this . state . directChannels . map ( ( channel , index , arr ) => {
2015-10-02 14:25:55 -04:00
return this . createChannelElement ( channel , index , arr , this . handleLeaveDirectChannel ) ;
} ) ;
2015-06-14 23:53:32 -08:00
2016-05-06 12:08:58 -04:00
const directMessageNonTeamItems = this . state . directNonTeamChannels . map ( ( channel , index , arr ) => {
return this . createChannelElement ( channel , index , arr , this . handleLeaveDirectChannel ) ;
} ) ;
let directDivider ;
if ( directMessageNonTeamItems . length !== 0 ) {
2016-05-09 17:14:43 +05:00
directDivider =
( < div className = 'sidebar__divider' >
< div className = 'sidebar__divider__text' >
< FormattedMessage
id = 'sidebar.otherMembers'
2016-05-12 09:31:08 -03:00
defaultMessage = 'Outside this team'
2016-05-09 17:14:43 +05:00
/ >
< / div >
< / div > ) ;
2016-05-06 12:08:58 -04:00
}
2015-08-06 14:03:45 -04:00
// update the favicon to show if there are any notifications
2016-05-04 10:10:19 -04:00
if ( this . lastBadgesActive !== this . badgesActive ) {
var link = document . createElement ( 'link' ) ;
link . type = 'image/x-icon' ;
link . rel = 'shortcut icon' ;
link . id = 'favicon' ;
if ( this . badgesActive ) {
link . href = redFavicon ;
} else {
link . href = favicon ;
}
var head = document . getElementsByTagName ( 'head' ) [ 0 ] ;
var oldLink = document . getElementById ( 'favicon' ) ;
if ( oldLink ) {
head . removeChild ( oldLink ) ;
}
head . appendChild ( link ) ;
2015-06-14 23:53:32 -08:00
}
2016-05-04 06:31:42 -07:00
var directMessageMore = (
< li key = 'more' >
< a
href = '#'
onClick = { this . showMoreDirectChannelsModal }
>
< FormattedMessage
2016-06-03 00:34:47 +05:00
id = 'sidebar.moreElips'
defaultMessage = 'More...'
2016-05-04 06:31:42 -07:00
/ >
< / a >
< / li >
) ;
2015-08-06 19:07:51 -04:00
2015-09-14 13:56:58 -04:00
let showChannelModal = false ;
2015-10-14 16:57:03 -04:00
if ( this . state . newChannelModalType !== '' ) {
2015-09-14 13:56:58 -04:00
showChannelModal = true ;
}
2015-10-16 10:15:52 -04:00
const createChannelTootlip = (
2016-01-31 22:03:30 -03:00
< Tooltip id = 'new-channel-tooltip' >
< FormattedMessage
id = 'sidebar.createChannel'
defaultMessage = 'Create new channel'
/ >
< / Tooltip >
2015-10-16 10:15:52 -04:00
) ;
const createGroupTootlip = (
2016-01-31 22:03:30 -03:00
< Tooltip id = 'new-group-tooltip' >
< FormattedMessage
id = 'sidebar.createGroup'
defaultMessage = 'Create new group'
/ >
< / Tooltip >
) ;
const above = (
< FormattedMessage
id = 'sidebar.unreadAbove'
defaultMessage = 'Unread post(s) above'
/ >
) ;
const below = (
< FormattedMessage
id = 'sidebar.unreadBelow'
defaultMessage = 'Unread post(s) below'
/ >
2015-10-16 10:15:52 -04:00
) ;
2015-10-14 17:04:13 +05:00
2016-07-06 08:23:24 -04:00
const isAdmin = TeamStore . isTeamAdminForCurrentTeam ( ) || UserStore . isSystemAdminForCurrentUser ( ) ;
const isSystemAdmin = UserStore . isSystemAdminForCurrentUser ( ) ;
let createPublicChannelIcon = (
< OverlayTrigger
delayShow = { 500 }
placement = 'top'
overlay = { createChannelTootlip }
>
< a
className = 'add-channel-btn'
href = '#'
onClick = { this . showNewChannelModal . bind ( this , Constants . OPEN _CHANNEL ) }
>
{ '+' }
< / a >
< / OverlayTrigger >
) ;
let createPrivateChannelIcon = (
< OverlayTrigger
delayShow = { 500 }
placement = 'top'
overlay = { createGroupTootlip }
>
< a
className = 'add-channel-btn'
href = '#'
onClick = { this . showNewChannelModal . bind ( this , Constants . PRIVATE _CHANNEL ) }
>
{ '+' }
< / a >
< / OverlayTrigger >
) ;
if ( global . window . mm _license . IsLicensed === 'true' ) {
if ( global . window . mm _config . RestrictPublicChannelManagement === Constants . PERMISSIONS _SYSTEM _ADMIN && ! isSystemAdmin ) {
createPublicChannelIcon = null ;
} else if ( global . window . mm _config . RestrictPublicChannelManagement === Constants . PERMISSIONS _TEAM _ADMIN && ! isAdmin ) {
createPublicChannelIcon = null ;
}
if ( global . window . mm _config . RestrictPrivateChannelManagement === Constants . PERMISSIONS _SYSTEM _ADMIN && ! isSystemAdmin ) {
createPrivateChannelIcon = null ;
} else if ( global . window . mm _config . RestrictPrivateChannelManagement === Constants . PERMISSIONS _TEAM _ADMIN && ! isAdmin ) {
createPrivateChannelIcon = null ;
}
}
2015-06-14 23:53:32 -08:00
return (
2016-02-08 07:26:10 -05:00
< div
className = 'sidebar--left'
id = 'sidebar-left'
2016-03-24 20:04:40 -04:00
key = 'sidebar-left'
2016-02-08 07:26:10 -05:00
>
2015-09-14 13:56:58 -04:00
< NewChannelFlow
show = { showChannelModal }
2015-10-14 16:57:03 -04:00
channelType = { this . state . newChannelModalType }
onModalDismissed = { this . hideNewChannelModal }
/ >
< MoreDirectChannels
show = { this . state . showDirectChannelsModal }
onModalDismissed = { this . hideMoreDirectChannelsModal }
2015-09-14 13:56:58 -04:00
/ >
2015-10-14 16:57:03 -04:00
2015-09-01 08:37:09 -04:00
< SidebarHeader
2016-02-08 07:26:10 -05:00
teamDisplayName = { this . state . currentTeam . display _name }
teamName = { this . state . currentTeam . name }
teamType = { this . state . currentTeam . type }
currentUser = { this . state . currentUser }
2015-09-01 08:37:09 -04:00
/ >
2015-06-14 23:53:32 -08:00
2015-09-16 14:53:16 -04:00
< UnreadChannelIndicator
show = { this . state . showTopUnread }
extraClass = 'nav-pills__unread-indicator-top'
2016-01-31 22:03:30 -03:00
text = { above }
2015-09-16 14:53:16 -04:00
/ >
< UnreadChannelIndicator
show = { this . state . showBottomUnread }
extraClass = 'nav-pills__unread-indicator-bottom'
2016-01-31 22:03:30 -03:00
text = { below }
2015-09-15 17:36:48 -04:00
/ >
2015-08-06 18:57:57 -04:00
2015-09-01 08:37:09 -04:00
< div
ref = 'container'
className = 'nav-pills__container'
onScroll = { this . onScroll }
>
2015-08-03 12:09:27 -07:00
< ul className = 'nav nav-pills nav-stacked' >
2015-09-01 08:37:09 -04:00
< li >
< h4 >
2016-01-31 22:03:30 -03:00
< FormattedMessage
id = 'sidebar.channels'
defaultMessage = 'Channels'
/ >
2016-07-06 08:23:24 -04:00
{ createPublicChannelIcon }
2015-09-01 08:37:09 -04:00
< / h4 >
< / li >
2015-09-15 17:36:48 -04:00
{ publicChannelItems }
2015-09-01 08:37:09 -04:00
< li >
< a
href = '#'
className = 'nav-more'
2015-12-18 17:58:08 -05:00
onClick = { this . showMoreChannelsModal }
2015-09-01 08:37:09 -04:00
>
2016-01-31 22:03:30 -03:00
< FormattedMessage
id = 'sidebar.moreElips'
defaultMessage = 'More...'
/ >
2015-09-01 08:37:09 -04:00
< / a >
< / li >
2015-06-14 23:53:32 -08:00
< / ul >
2015-08-03 12:09:27 -07:00
< ul className = 'nav nav-pills nav-stacked' >
2015-09-01 08:37:09 -04:00
< li >
< h4 >
2016-01-31 22:03:30 -03:00
< FormattedMessage
id = 'sidebar.pg'
defaultMessage = 'Private Groups'
/ >
2016-07-06 08:23:24 -04:00
{ createPrivateChannelIcon }
2015-09-01 08:37:09 -04:00
< / h4 >
< / li >
2015-06-14 23:53:32 -08:00
{ privateChannelItems }
< / ul >
2015-08-03 12:09:27 -07:00
< ul className = 'nav nav-pills nav-stacked' >
2016-01-31 22:03:30 -03:00
< li >
< h4 >
< FormattedMessage
id = 'sidebar.direct'
defaultMessage = 'Direct Messages'
/ >
< / h4 >
< / li >
2015-06-14 23:53:32 -08:00
{ directMessageItems }
2016-05-06 12:08:58 -04:00
{ directDivider }
{ directMessageNonTeamItems }
2015-08-06 19:07:51 -04:00
{ directMessageMore }
2015-06-14 23:53:32 -08:00
< / ul >
< / div >
< / div >
) ;
}
2015-09-01 08:37:09 -04:00
}