diff --git a/app/components/base/Dropdown.js b/app/components/base/Dropdown.js index 941557586..39455eb18 100644 --- a/app/components/base/Dropdown.js +++ b/app/components/base/Dropdown.js @@ -36,14 +36,20 @@ class Dropdown extends Component { } componentDidMount () { - ReactDOM.findDOMNode(this).addEventListener('keydown', e => { + this._bodyKeydownHandler = e => { if (this.state.open && e.keyCode === 27) { e.preventDefault(); e.stopPropagation(); // Pressed escape this.hide(); } - }); + }; + + document.body.addEventListener('keydown', this._bodyKeydownHandler); + } + + componentWillUnmount () { + document.body.removeEventListener('keydown', this._bodyKeydownHandler); } render () { @@ -62,8 +68,7 @@ class Dropdown extends Component { return (
e.preventDefault()} - ref={n => this._node = n}> + onMouseDown={e => e.preventDefault()}> {this.props.children}
diff --git a/app/components/modals/CookiesModal.js b/app/components/modals/CookiesModal.js index 1c106e6fb..cc2c2b473 100644 --- a/app/components/modals/CookiesModal.js +++ b/app/components/modals/CookiesModal.js @@ -143,9 +143,7 @@ class CookiesModal extends Component { } } -CookiesModal.propTypes = { - onChange: PropTypes.func.isRequired -}; +CookiesModal.propTypes = {}; // export CookiesModal; export default CookiesModal; diff --git a/app/components/modals/RequestSwitcherModal.js b/app/components/modals/RequestSwitcherModal.js index 8f0175a40..0d2c29377 100644 --- a/app/components/modals/RequestSwitcherModal.js +++ b/app/components/modals/RequestSwitcherModal.js @@ -14,15 +14,17 @@ class RequestSwitcherModal extends Component { this.state = { searchString: '', matchedRequests: [], + matchedWorkspaces: [], requestGroups: [], activeIndex: -1 } } _setActiveIndex (activeIndex) { + const maxIndex = this.state.matchedRequests.length + this.state.matchedWorkspaces.length; if (activeIndex < 0) { activeIndex = this.state.matchedRequests.length - 1; - } else if (activeIndex >= this.state.matchedRequests.length) { + } else if (activeIndex >= maxIndex) { activeIndex = 0; } @@ -30,21 +32,51 @@ class RequestSwitcherModal extends Component { } _activateCurrentIndex () { - if (this.state.matchedRequests.length) { - // Activate the request if there is one - const request = this.state.matchedRequests[this.state.activeIndex]; - this._activateRequest(request); - } else { - // Create the request if nothing matched - const name = this.state.searchString; - const parentId = this.props.activeRequestParentId; + const { + activeIndex, + matchedRequests, + matchedWorkspaces + } = this.state; - db.requestCreate({name, parentId}).then(request => { - this._activateRequest(request); - }); + if (activeIndex < matchedRequests.length) { + // Activate the request if there is one + const request = matchedRequests[activeIndex]; + this._activateRequest(request); + } else if (activeIndex < (matchedRequests.length + matchedWorkspaces.length)) { + // Activate the workspace if there is one + const index = activeIndex - matchedRequests.length; + const workspace = matchedWorkspaces[index]; + this._activateWorkspace(workspace); + } else { + // Create request if no match + this._createRequestFromSearch(); } } + _createRequestFromSearch () { + const {activeRequestParentId} = this.props; + const {searchString} = this.state; + + // Create the request if nothing matched + const patch = { + name: searchString, + parentId: activeRequestParentId + }; + + db.requestCreate(patch).then(request => { + this._activateRequest(request); + }); + } + + _activateWorkspace (workspace) { + if (!workspace) { + return; + } + + this.props.activateWorkspace(workspace); + this.modal.hide(); + } + _activateRequest (request) { if (!request) { return; @@ -59,10 +91,12 @@ class RequestSwitcherModal extends Component { Promise.all([ db.requestAll(), - db.requestGroupAll() + db.requestGroupAll(), + db.workspaceAll() ]).then(([ allRequests, - allRequestGroups + allRequestGroups, + allWorkspaces ]) => { // TODO: Support nested RequestGroups // Filter out RequestGroups that don't belong to this Workspace @@ -82,9 +116,14 @@ class RequestSwitcherModal extends Component { const parentId = this.props.activeRequestParentId; // OPTIMIZATION: This only filters if we have a filter - let matchedRequests = !searchString ? requests : requests.filter( - r => r.name.toLowerCase().indexOf(searchString.toLowerCase()) !== -1 - ); + let matchedRequests = requests; + if (searchString) { + matchedRequests = matchedRequests.filter(r => { + const name = r.name.toLowerCase(); + const toMatch = searchString.toLowerCase(); + return name.indexOf(toMatch) !== -1 + }); + } // OPTIMIZATION: Apply sort after the filter so we have to sort less matchedRequests = matchedRequests.sort( @@ -107,11 +146,24 @@ class RequestSwitcherModal extends Component { } ); + let matchedWorkspaces = []; + if (searchString) { + // Only match workspaces if there is a search + matchedWorkspaces = allWorkspaces + .filter(w => w._id !== workspaceId) + .filter(w => { + const name = w.name.toLowerCase(); + const toMatch = searchString.toLowerCase(); + return name.indexOf(toMatch) !== -1 + }); + } + const activeIndex = searchString ? 0 : -1; this.setState({ activeIndex, matchedRequests, + matchedWorkspaces, requestGroups, searchString }); @@ -152,6 +204,7 @@ class RequestSwitcherModal extends Component { render () { const { matchedRequests, + matchedWorkspaces, requestGroups, searchString, activeIndex @@ -170,7 +223,7 @@ class RequestSwitcherModal extends Component {     esc to dismiss

-

Jump To Request

+

Quick Switch

@@ -208,9 +261,31 @@ class RequestSwitcherModal extends Component { ) })} + + {matchedRequests.length && matchedWorkspaces.length ? ( +
+ ) : null} + + {matchedWorkspaces.map((w, i) => { + const buttonClasses = classnames( + 'btn btn--compact wide text-left', + {focus: (activeIndex - matchedRequests.length) === i} + ); + + return ( +
  • + +
  • + ) + })} - {matchedRequests.length === 0 ? ( + {!matchedRequests.length && !matchedWorkspaces.length ? (

    No matches found for {searchString} @@ -229,6 +304,7 @@ class RequestSwitcherModal extends Component { RequestSwitcherModal.propTypes = { activateRequest: PropTypes.func.isRequired, + activateWorkspace: PropTypes.func.isRequired, workspaceId: PropTypes.string.isRequired, activeRequestParentId: PropTypes.string.isRequired }; diff --git a/app/containers/App.js b/app/containers/App.js index 8830129cf..afe0de143 100644 --- a/app/containers/App.js +++ b/app/containers/App.js @@ -5,9 +5,7 @@ import {connect} from 'react-redux'; import {bindActionCreators} from 'redux'; import HTML5Backend from 'react-dnd-html5-backend'; import {DragDropContext} from 'react-dnd'; - import Mousetrap from '../lib/mousetrap'; - import {addModal} from '../components/modals'; import WorkspaceEnvironmentsEditModal from '../components/modals/WorkspaceEnvironmentsEditModal'; import CookiesModal from '../components/modals/CookiesModal'; @@ -23,21 +21,21 @@ import ResponsePane from '../components/ResponsePane'; import Sidebar from '../components/sidebar/Sidebar'; import {PREVIEW_MODE_FRIENDLY} from '../lib/previewModes'; import { - MAX_PANE_WIDTH, MIN_PANE_WIDTH, + MAX_PANE_WIDTH, + MIN_PANE_WIDTH, DEFAULT_PANE_WIDTH, MAX_SIDEBAR_REMS, MIN_SIDEBAR_REMS, - DEFAULT_SIDEBAR_WIDTH -} from '../lib/constants' - + DEFAULT_SIDEBAR_WIDTH, + CHECK_FOR_UPDATES_INTERVAL +} from '../lib/constants'; import * as GlobalActions from '../redux/modules/global'; import * as RequestActions from '../redux/modules/requests'; - +import * as WorkspaceActions from '../redux/modules/workspaces'; import * as db from '../database'; import {importCurl} from '../lib/export/curl'; import {trackEvent} from '../lib/analytics'; import {getAppVersion} from '../lib/appInfo'; -import {CHECK_FOR_UPDATES_INTERVAL} from '../lib/constants'; import {getModal} from '../components/modals/index'; @@ -473,7 +471,8 @@ class App extends Component { const gridTemplateColumns = `${sidebarWidth}rem 0 ${paneWidth}fr 0 ${1 - paneWidth}fr`; return ( -

    +
    this._sidebar = n} showEnvironmentsModal={() => getModal(WorkspaceEnvironmentsEditModal).show(workspace)} @@ -494,7 +493,10 @@ class App extends Component { />
    -
    {e.preventDefault(); this._startDragSidebar()}} +
    { + e.preventDefault(); + this._startDragSidebar() + }} onDoubleClick={() => this._resetDragSidebar()}>
    @@ -548,6 +550,7 @@ class App extends Component { workspaceId={workspace._id} activeRequestParentId={activeRequest ? activeRequest.parentId : workspace._id} activateRequest={r => db.workspaceUpdate(workspace, {metaActiveRequestId: r._id})} + activateWorkspace={w => actions.workspaces.activate(w)} /> addModal(m)} @@ -555,9 +558,7 @@ class App extends Component { addModal(m)} onChange={w => db.workspaceUpdate(w)}/> - addModal(m)} - onChange={() => console.log('TODO: COOKIES!!!')}/> + addModal(m)}/> {/*
    */} {/*
    How's it going?
    */} @@ -574,6 +575,9 @@ App.propTypes = { requests: PropTypes.shape({ send: PropTypes.func.isRequired }).isRequired, + workspaces: PropTypes.shape({ + activate: PropTypes.func.isRequired + }).isRequired, global: PropTypes.shape({ importFile: PropTypes.func.isRequired }).isRequired @@ -604,7 +608,8 @@ function mapDispatchToProps (dispatch) { return { actions: { global: bindActionCreators(GlobalActions, dispatch), - requests: bindActionCreators(RequestActions, dispatch) + requests: bindActionCreators(RequestActions, dispatch), + workspaces: bindActionCreators(WorkspaceActions, dispatch) } } }