From ccbbda274d2e70ce3b199dc9adb5e9dcc4c90ea8 Mon Sep 17 00:00:00 2001
From: Gregory Schier
Date: Sat, 10 Sep 2016 11:30:50 -0700
Subject: [PATCH] Add workspace switching to quick switcher
---
app/components/base/Dropdown.js | 13 +-
app/components/modals/CookiesModal.js | 4 +-
app/components/modals/RequestSwitcherModal.js | 114 +++++++++++++++---
app/containers/App.js | 33 ++---
4 files changed, 124 insertions(+), 40 deletions(-)
diff --git a/app/components/base/Dropdown.js b/app/components/base/Dropdown.js
index 9415575865..39455eb189 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 1c106e6fbe..cc2c2b4737 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 8f0175a408..0d2c293774 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 8830129cf6..afe0de1430 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)
}
}
}