diff options
author | enahum <nahumhbl@gmail.com> | 2016-12-19 10:05:46 -0300 |
---|---|---|
committer | Joram Wilander <jwawilander@gmail.com> | 2016-12-19 08:05:46 -0500 |
commit | 999d1553e1ce45adf58f6082b160bc1147dc592b (patch) | |
tree | 369a9b7f46dd44d136a79a050469429169433cec /webapp/components/team_sidebar | |
parent | 3ce2ce9dc882ed962dc3ce7550bdb07963f376b6 (diff) | |
download | chat-999d1553e1ce45adf58f6082b160bc1147dc592b.tar.gz chat-999d1553e1ce45adf58f6082b160bc1147dc592b.tar.bz2 chat-999d1553e1ce45adf58f6082b160bc1147dc592b.zip |
PLT-4167 Team Sidebar (#4569)
* PLT-4167 Team Sidebar
* Address feedback from PM
* change route from my_members to members
* bug fixes
* Updating styles for teams sidebar (#4681)
* Added PM changes
* Fix corner cases
* Addressing feedback
* use two different endpoints
* Bug fixes
* Rename model and client functions, using preferences to store last team and channel viewed
* Fix mobile notification count and closing the team sidebar
* unit test, fixed bad merge and retrieve from cached when available
* bug fixes
* use id for last channel in preferences, query optimization
* Updating multi team css (#4830)
Diffstat (limited to 'webapp/components/team_sidebar')
-rw-r--r-- | webapp/components/team_sidebar/components/team_button.jsx | 84 | ||||
-rw-r--r-- | webapp/components/team_sidebar/team_sidebar_controller.jsx | 169 |
2 files changed, 253 insertions, 0 deletions
diff --git a/webapp/components/team_sidebar/components/team_button.jsx b/webapp/components/team_sidebar/components/team_button.jsx new file mode 100644 index 000000000..0033ae25a --- /dev/null +++ b/webapp/components/team_sidebar/components/team_button.jsx @@ -0,0 +1,84 @@ +// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import Constants from 'utils/constants.jsx'; + +import React from 'react'; +import {Link} from 'react-router/es6'; +import {Tooltip, OverlayTrigger} from 'react-bootstrap'; + +export default class TeamButton extends React.Component { + constructor(props) { + super(props); + + this.handleDisabled = this.handleDisabled.bind(this); + } + + handleDisabled(e) { + e.preventDefault(); + } + + render() { + let teamClass = this.props.active ? 'active' : ''; + const disabled = this.props.disabled ? 'team-disabled' : ''; + const handleClick = (this.props.active || this.props.disabled) ? this.handleDisabled : null; + let badge; + + if (!teamClass) { + teamClass = this.props.unread ? 'unread' : ''; + + if (this.props.mentions) { + badge = ( + <span className='badge pull-right small'>{this.props.mentions}</span> + ); + } + } + + return ( + <div + className={`team-container ${teamClass}`} + > + <Link + className={disabled} + to={this.props.url} + onClick={handleClick} + > + <OverlayTrigger + delayShow={Constants.OVERLAY_TIME_DELAY} + placement={this.props.placement} + overlay={ + <Tooltip id={`tooltip-${this.props.url}`}> + {this.props.tip} + </Tooltip> + } + > + <div className='team-btn'> + {badge} + {this.props.contents} + </div> + </OverlayTrigger> + </Link> + </div> + ); + } +} + +TeamButton.defaultProps = { + tip: '', + placement: 'right', + active: false, + disabled: false, + unread: false, + mentions: 0 +}; + +TeamButton.propTypes = { + url: React.PropTypes.string.isRequired, + contents: React.PropTypes.node.isRequired, + tip: React.PropTypes.node, + active: React.PropTypes.bool, + disabled: React.PropTypes.bool, + unread: React.PropTypes.bool, + mentions: React.PropTypes.number, + placement: React.PropTypes.oneOf(['left', 'right', 'top', 'bottom']) +}; diff --git a/webapp/components/team_sidebar/team_sidebar_controller.jsx b/webapp/components/team_sidebar/team_sidebar_controller.jsx new file mode 100644 index 000000000..f005afeb3 --- /dev/null +++ b/webapp/components/team_sidebar/team_sidebar_controller.jsx @@ -0,0 +1,169 @@ +// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import TeamButton from './components/team_button.jsx'; + +import TeamStore from 'stores/team_store.jsx'; +import UserStore from 'stores/user_store.jsx'; + +import * as AsyncClient from 'utils/async_client.jsx'; +import * as Utils from 'utils/utils.jsx'; + +import $ from 'jquery'; +import React from 'react'; +import {FormattedMessage} from 'react-intl'; + +export default class TeamSidebar extends React.Component { + constructor(props) { + super(props); + + this.getStateFromStores = this.getStateFromStores.bind(this); + this.onChange = this.onChange.bind(this); + this.handleResize = this.handleResize.bind(this); + this.setStyles = this.setStyles.bind(this); + + this.state = this.getStateFromStores(); + } + + getStateFromStores() { + const teamMembers = TeamStore.getMyTeamMembers(); + const currentTeamId = TeamStore.getCurrentId(); + + return { + teams: TeamStore.getAll(), + teamListings: TeamStore.getTeamListings(), + teamMembers, + currentTeamId, + show: teamMembers && teamMembers.length > 1 + }; + } + + componentDidMount() { + window.addEventListener('resize', this.handleResize); + TeamStore.addChangeListener(this.onChange); + TeamStore.addUnreadChangeListener(this.onChange); + AsyncClient.getAllTeamListings(); + this.setStyles(); + } + + componentWillUnmount() { + window.removeEventListener('resize', this.handleResize); + TeamStore.removeChangeListener(this.onChange); + TeamStore.removeUnreadChangeListener(this.onChange); + } + + componentDidUpdate(prevProps, prevState) { + $('.team-wrapper').perfectScrollbar(); + + // reset the scrollbar upon switching teams + if (this.state.currentTeam !== prevState.currentTeam) { + this.refs.container.scrollTop = 0; + $('.team-wrapper').perfectScrollbar('update'); + } + } + + onChange() { + this.setState(this.getStateFromStores()); + this.setStyles(); + } + + handleResize() { + const teamMembers = this.state.teamMembers; + this.setState({show: teamMembers && teamMembers.length > 1}); + this.setStyles(); + } + + setStyles() { + const root = document.querySelector('#root'); + + if (this.state.show) { + root.classList.add('multi-teams'); + } else { + root.classList.remove('multi-teams'); + } + } + + render() { + if (!this.state.show) { + return null; + } + + const myTeams = []; + const isSystemAdmin = Utils.isSystemAdmin(UserStore.getCurrentUser().roles); + const isAlreadyMember = new Map(); + let moreTeams = false; + + for (const index in this.state.teamMembers) { + if (this.state.teamMembers.hasOwnProperty(index)) { + const teamMember = this.state.teamMembers[index]; + const teamId = teamMember.team_id; + myTeams.push(Object.assign({ + unread: teamMember.msg_count > 0, + mentions: teamMember.mention_count + }, this.state.teams[teamId])); + isAlreadyMember[teamId] = true; + } + } + + for (const id in this.state.teamListings) { + if (this.state.teamListings.hasOwnProperty(id) && !isAlreadyMember[id]) { + moreTeams = true; + break; + } + } + + const teams = myTeams. + sort((a, b) => a.display_name.localeCompare(b.display_name)). + map((team) => { + return ( + <TeamButton + key={'switch_team_' + team.name} + url={`/${team.name}`} + tip={team.display_name} + active={team.id === this.state.currentTeamId} + contents={team.display_name.substring(0, 1).toUpperCase()} + unread={team.unread} + mentions={team.mentions} + /> + ); + }); + + if (moreTeams) { + teams.push( + <TeamButton + key='more_teams' + url='/select_team' + tip={ + <FormattedMessage + id='team_sidebar.join' + defaultMessage='Other teams you can join.' + /> + } + contents={<i className='fa fa-plus'/>} + /> + ); + } else if (global.window.mm_config.EnableTeamCreation === 'true' || isSystemAdmin) { + teams.push( + <TeamButton + key='more_teams' + url='/create_team' + tip={ + <FormattedMessage + id='navbar_dropdown.create' + defaultMessage='Create a New Team' + /> + } + contents={<i className='fa fa-plus'/>} + /> + ); + } + + return ( + <div className='team-sidebar'> + <div className='team-wrapper'> + {teams} + </div> + </div> + ); + } +} |