summaryrefslogtreecommitdiffstats
path: root/webapp/components/quick_switch_modal
diff options
context:
space:
mode:
authorJoram Wilander <jwawilander@gmail.com>2017-05-31 16:51:42 -0400
committerGitHub <noreply@github.com>2017-05-31 16:51:42 -0400
commit5aaedb9663b987caf1fb11ea6062bcc44e6bafca (patch)
treebd77c10168f9fb1b0f998b08a3b2a3761512a451 /webapp/components/quick_switch_modal
parent8ce72aedc3a5b4f783fb6ebab38aac8bf5f413ae (diff)
downloadchat-5aaedb9663b987caf1fb11ea6062bcc44e6bafca.tar.gz
chat-5aaedb9663b987caf1fb11ea6062bcc44e6bafca.tar.bz2
chat-5aaedb9663b987caf1fb11ea6062bcc44e6bafca.zip
PLT-5699 Improvements to channel switcher (#6486)
* Refactor channel switcher to not wait on server results * Change channel switcher to quick switcher and include team switching * Add sections, update ordering and add discoverability button * Fix styling error * Use CMD in text if on mac * Clean yarn cache on every install * Various UX updates per feedback * Add shortcut help text for team switcher * Couple more updates per feedback * Some minor fixes for GM and autocomplete race * Updating UI for channel switcher (#6504) * Updating channel switcher button (#6506) * Updating switcher modal on mobile (#6507) * Removed jQuery usage * Rename function to toggleQuickSwitchModal
Diffstat (limited to 'webapp/components/quick_switch_modal')
-rw-r--r--webapp/components/quick_switch_modal/index.js16
-rw-r--r--webapp/components/quick_switch_modal/quick_switch_modal.jsx322
2 files changed, 338 insertions, 0 deletions
diff --git a/webapp/components/quick_switch_modal/index.js b/webapp/components/quick_switch_modal/index.js
new file mode 100644
index 000000000..7826fd8f5
--- /dev/null
+++ b/webapp/components/quick_switch_modal/index.js
@@ -0,0 +1,16 @@
+// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import {connect} from 'react-redux';
+import {getMyTeams} from 'mattermost-redux/selectors/entities/teams';
+
+import QuickSwitchModal from './quick_switch_modal.jsx';
+
+function mapStateToProps(state, ownProps) {
+ return {
+ ...ownProps,
+ showTeamSwitcher: getMyTeams(state).length > 1
+ };
+}
+
+export default connect(mapStateToProps)(QuickSwitchModal);
diff --git a/webapp/components/quick_switch_modal/quick_switch_modal.jsx b/webapp/components/quick_switch_modal/quick_switch_modal.jsx
new file mode 100644
index 000000000..c3095caf9
--- /dev/null
+++ b/webapp/components/quick_switch_modal/quick_switch_modal.jsx
@@ -0,0 +1,322 @@
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import SuggestionList from 'components/suggestion/suggestion_list.jsx';
+import SuggestionBox from 'components/suggestion/suggestion_box.jsx';
+import SwitchChannelProvider from 'components/suggestion/switch_channel_provider.jsx';
+import SwitchTeamProvider from 'components/suggestion/switch_team_provider.jsx';
+
+import {goToChannel, openDirectChannelToUser} from 'actions/channel_actions.jsx';
+
+import Constants from 'utils/constants.jsx';
+import * as Utils from 'utils/utils.jsx';
+
+import React from 'react';
+import PropTypes from 'prop-types';
+import {browserHistory} from 'react-router/es6';
+import {Modal} from 'react-bootstrap';
+import {FormattedMessage} from 'react-intl';
+
+// Redux actions
+import store from 'stores/redux_store.jsx';
+const getState = store.getState;
+
+import {getChannel} from 'mattermost-redux/selectors/entities/channels';
+import {getUserByUsername} from 'mattermost-redux/selectors/entities/users';
+
+const CHANNEL_MODE = 'channel';
+const TEAM_MODE = 'team';
+
+export default class QuickSwitchModal extends React.PureComponent {
+ static propTypes = {
+
+ /**
+ * The mode to start in when showing the modal, either 'channel' or 'team'
+ */
+ initialMode: PropTypes.string.isRequired,
+
+ /**
+ * Set to show the modal
+ */
+ show: PropTypes.bool.isRequired,
+
+ /**
+ * The function called to hide the modal
+ */
+ onHide: PropTypes.func.isRequired,
+
+ /**
+ * Set to show team switcher
+ */
+ showTeamSwitcher: PropTypes.bool
+ }
+
+ static defaultProps = {
+ initialMode: CHANNEL_MODE
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.onChange = this.onChange.bind(this);
+ this.onShow = this.onShow.bind(this);
+ this.onHide = this.onHide.bind(this);
+ this.onExited = this.onExited.bind(this);
+ this.handleKeyDown = this.handleKeyDown.bind(this);
+ this.handleSubmit = this.handleSubmit.bind(this);
+ this.switchToChannel = this.switchToChannel.bind(this);
+ this.switchMode = this.switchMode.bind(this);
+ this.focusTextbox = this.focusTextbox.bind(this);
+
+ this.enableChannelProvider = this.enableChannelProvider.bind(this);
+ this.enableTeamProvider = this.enableTeamProvider.bind(this);
+ this.channelProviders = [new SwitchChannelProvider()];
+ this.teamProviders = [new SwitchTeamProvider()];
+
+ this.state = {
+ text: '',
+ mode: props.initialMode
+ };
+ }
+
+ componentDidUpdate(prevProps) {
+ if (this.props.show && !prevProps.show) {
+ this.focusTextbox();
+ }
+ }
+
+ componentWillReceiveProps(nextProps) {
+ if (!this.props.show && nextProps.show) {
+ this.setState({mode: nextProps.initialMode, text: ''});
+ }
+ }
+
+ focusTextbox() {
+ if (this.refs.switchbox == null) {
+ return;
+ }
+
+ const textbox = this.refs.switchbox.getTextbox();
+ textbox.focus();
+ Utils.placeCaretAtEnd(textbox);
+ }
+
+ onShow() {
+ this.setState({
+ text: ''
+ });
+ }
+
+ onHide() {
+ this.setState({
+ text: ''
+ });
+ this.props.onHide();
+ }
+
+ onExited() {
+ setTimeout(() => {
+ document.querySelector('#post_textbox').focus();
+ });
+ }
+
+ onChange(e) {
+ this.setState({text: e.target.value});
+ }
+
+ handleKeyDown(e) {
+ if (e.keyCode === Constants.KeyCodes.TAB) {
+ e.preventDefault();
+ this.switchMode();
+ }
+ }
+
+ handleSubmit(selected) {
+ let channel = null;
+
+ if (!selected) {
+ return;
+ }
+
+ if (this.state.mode === CHANNEL_MODE) {
+ const selectedChannel = selected.channel;
+ if (selectedChannel.type === Constants.DM_CHANNEL) {
+ const user = getUserByUsername(getState(), selectedChannel.name);
+
+ if (user) {
+ openDirectChannelToUser(
+ user.id,
+ (ch) => {
+ channel = ch;
+ this.switchToChannel(channel);
+ },
+ () => {
+ channel = null;
+ this.switchToChannel(channel);
+ }
+ );
+ }
+ } else {
+ channel = getChannel(getState(), selectedChannel.id);
+ this.switchToChannel(channel);
+ }
+ } else {
+ browserHistory.push('/' + selected.name);
+ this.onHide();
+ }
+ }
+
+ switchToChannel(channel) {
+ if (channel != null) {
+ goToChannel(channel);
+ this.onHide();
+ }
+ }
+
+ enableChannelProvider() {
+ this.channelProviders[0].disableDispatches = false;
+ this.teamProviders[0].disableDispatches = true;
+ }
+
+ enableTeamProvider() {
+ this.teamProviders[0].disableDispatches = false;
+ this.channelProviders[0].disableDispatches = true;
+ }
+
+ switchMode() {
+ if (this.state.mode === CHANNEL_MODE && this.props.showTeamSwitcher) {
+ this.enableTeamProvider();
+ this.setState({mode: TEAM_MODE});
+ } else if (this.state.mode === TEAM_MODE) {
+ this.enableChannelProvider();
+ this.setState({mode: CHANNEL_MODE});
+ }
+ }
+
+ render() {
+ let providers = this.channelProviders;
+ let header;
+ let renderDividers = true;
+
+ let channelShortcut = 'quick_switch_modal.channelsShortcut.windows';
+ if (Utils.isMac()) {
+ channelShortcut = 'quick_switch_modal.channelsShortcut.mac';
+ }
+
+ let teamShortcut = 'quick_switch_modal.teamsShortcut.windows';
+ if (Utils.isMac()) {
+ teamShortcut = 'quick_switch_modal.teamsShortcut.mac';
+ }
+
+ if (this.props.showTeamSwitcher) {
+ let channelsActiveClass = '';
+ let teamsActiveClass = '';
+ if (this.state.mode === TEAM_MODE) {
+ providers = this.teamProviders;
+ renderDividers = false;
+ teamsActiveClass = 'active';
+ } else {
+ channelsActiveClass = 'active';
+ }
+
+ header = (
+ <div className='nav nav-tabs'>
+ <li className={channelsActiveClass}>
+ <a
+ href='#'
+ onClick={(e) => {
+ e.preventDefault();
+ this.enableChannelProvider();
+ this.setState({mode: 'channel'});
+ this.focusTextbox();
+ }}
+ >
+ <FormattedMessage
+ id='quick_switch_modal.channels'
+ defaultMessage='Channels'
+ />
+ <span className='small'>
+ <FormattedMessage
+ id={channelShortcut}
+ defaultMessage='CTRL+K'
+ />
+ </span>
+ </a>
+ </li>
+ <li className={teamsActiveClass}>
+ <a
+ href='#'
+ onClick={(e) => {
+ e.preventDefault();
+ this.enableTeamProvider();
+ this.setState({mode: 'team'});
+ this.focusTextbox();
+ }}
+ >
+ <FormattedMessage
+ id='quick_switch_modal.teams'
+ defaultMessage='Teams'
+ />
+ <span className='small'>
+ <FormattedMessage
+ id={teamShortcut}
+ defaultMessage='CTRL+ALT+K'
+ />
+ </span>
+ </a>
+ </li>
+ </div>
+ );
+ }
+
+ let help;
+ if (this.props.showTeamSwitcher) {
+ help = (
+ <FormattedMessage
+ id='quick_switch_modal.help'
+ defaultMessage='Use TAB to toggle between teams/channels, ↑↓ to browse, ↵ to confirm, ESC to dismiss'
+ />
+ );
+ } else {
+ help = (
+ <FormattedMessage
+ id='quick_switch_modal.help_no_team'
+ defaultMessage='Type a channel name. Use ↑↓ to browse, ↵ to confirm, ESC to dismiss'
+ />
+ );
+ }
+
+ return (
+ <Modal
+ dialogClassName='channel-switch-modal modal--overflow'
+ ref='modal'
+ show={this.props.show}
+ onHide={this.onHide}
+ onExited={this.onExited}
+ >
+ <Modal.Header closeButton={true}/>
+ <Modal.Body>
+ {header}
+ <div className='modal__hint'>
+ {help}
+ </div>
+ <SuggestionBox
+ ref='switchbox'
+ className='form-control focused'
+ type='input'
+ onChange={this.onChange}
+ value={this.state.text}
+ onKeyDown={this.handleKeyDown}
+ onItemSelected={this.handleSubmit}
+ listComponent={SuggestionList}
+ maxLength='64'
+ providers={providers}
+ listStyle='bottom'
+ completeOnTab={false}
+ renderDividers={renderDividers}
+ />
+ </Modal.Body>
+ </Modal>
+ );
+ }
+}