insomnia/app/containers/App.js

419 lines
12 KiB
JavaScript
Raw Normal View History

import React, {Component, PropTypes} from 'react';
2016-07-19 16:15:03 +00:00
import ReactDOM from 'react-dom';
import classnames from 'classnames';
2016-03-20 04:47:43 +00:00
import {connect} from 'react-redux'
import {bindActionCreators} from 'redux';
import Mousetrap from '../lib/mousetrap';
2016-03-20 04:47:43 +00:00
import EnvironmentEditModal from '../components/EnvironmentEditModal';
import RequestSwitcherModal from '../components/RequestSwitcherModal';
import CurlExportModal from '../components/CurlExportModal';
import PromptModal from '../components/PromptModal';
import SettingsModal from '../components/SettingsModal';
import RequestPane from '../components/RequestPane';
import ResponsePane from '../components/ResponsePane';
import Sidebar from '../components/Sidebar';
import {PREVIEW_MODE_FRIENDLY} from '../lib/previewModes';
import {
MAX_PANE_WIDTH, MIN_PANE_WIDTH,
DEFAULT_PANE_WIDTH,
MAX_SIDEBAR_REMS,
MIN_SIDEBAR_REMS,
DEFAULT_SIDEBAR_WIDTH
} from '../lib/constants'
2016-03-20 04:47:43 +00:00
import * as RequestGroupActions from '../redux/modules/requestGroups';
import * as RequestActions from '../redux/modules/requests';
2016-04-16 23:24:57 +00:00
import * as db from '../database';
import {importCurl} from "../lib/export/curl";
2016-03-20 04:47:43 +00:00
class App extends Component {
2016-07-07 20:10:55 +00:00
constructor (props) {
2016-04-18 04:39:15 +00:00
super(props);
const workspace = this._getActiveWorkspace(props);
2016-04-18 04:39:15 +00:00
this.state = {
activeResponse: null,
2016-06-19 00:08:14 +00:00
activeRequest: null,
draggingSidebar: false,
draggingPane: false,
2016-07-19 16:15:03 +00:00
sidebarWidth: workspace.meta.sidebarWidth || DEFAULT_SIDEBAR_WIDTH, // rem
paneWidth: workspace.meta.paneWidth || DEFAULT_PANE_WIDTH // % (fr)
};
this.globalKeyMap = {
// Show Settings
'mod+,': () => {
2016-07-14 22:48:56 +00:00
SettingsModal.toggle();
},
// Show Environment Editor
'mod+e': () => {
this._fetchActiveRequestGroup().then(requestGroup => {
2016-07-14 22:48:56 +00:00
EnvironmentEditModal.toggle(requestGroup);
2016-07-15 00:26:04 +00:00
}, () => {
// No RequestGroup is active
})
},
// Show Request Switcher
'mod+k|mod+p': () => {
2016-07-14 22:48:56 +00:00
RequestSwitcherModal.toggle();
},
// Request Send
'mod+enter|mod+r': () => {
const request = this._getActiveRequest();
if (request) {
this.props.actions.requests.send(request);
}
},
// Request Create
'mod+n': () => {
const workspace = this._getActiveWorkspace();
const request = this._getActiveRequest();
if (!workspace) {
// Nothing to do if no workspace
return;
}
const parentId = request ? request.parentId : workspace._id;
db.requestCreateAndActivate(workspace, {parentId});
},
// Request Duplicate
'mod+d': () => {
const request = this._getActiveRequest();
if (!request) {
return;
}
db.requestCopyAndActivate(workspace, request);
},
// Change Http Method
'mod+m': () => {
// TODO: This
},
// Sidebar Toggle
'mod+\\': () => {
// TODO: This
2016-07-07 22:06:18 +00:00
}
2016-04-18 04:39:15 +00:00
}
}
2016-07-07 20:10:55 +00:00
_generateSidebarTree (parentId, entities) {
const children = entities.filter(e => e.parentId === parentId);
2016-05-01 19:56:30 +00:00
if (children.length > 0) {
return children.map(c => ({
doc: c,
children: this._generateSidebarTree(c._id, entities)
}));
} else {
return children;
}
}
2016-07-14 22:48:56 +00:00
_handleUrlChanged (url) {
// TODO: Should this be moved elsewhere?
const requestPatch = importCurl(url);
if (requestPatch) {
// If the user typed in a curl cmd, dissect it and update the whole request
db.requestUpdate(this._getActiveRequest(), requestPatch);
} else {
db.requestUpdate(this._getActiveRequest(), {url});
}
}
2016-07-07 20:10:55 +00:00
_startDragSidebar () {
2016-06-19 00:08:14 +00:00
this.setState({
draggingSidebar: true
})
}
2016-07-07 20:10:55 +00:00
_resetDragSidebar () {
// TODO: Remove setTimeout need be not triggering drag on double click
setTimeout(() => {
this.setState({
sidebarWidth: DEFAULT_SIDEBAR_WIDTH
})
2016-06-19 00:40:14 +00:00
this._saveSidebarWidth();
}, 50);
2016-06-19 00:08:14 +00:00
}
2016-07-07 20:10:55 +00:00
_startDragPane () {
this.setState({
draggingPane: true
2016-06-19 00:08:14 +00:00
})
}
2016-07-07 20:10:55 +00:00
_resetDragPane () {
// TODO: Remove setTimeout need be not triggering drag on double click
setTimeout(() => {
this.setState({
paneWidth: DEFAULT_PANE_WIDTH
})
2016-06-19 00:08:14 +00:00
this._savePaneWidth();
}, 50);
}
2016-07-07 20:10:55 +00:00
_savePaneWidth () {
const {paneWidth} = this.state;
2016-07-19 16:15:03 +00:00
db.workspaceUpdateMeta(this._getActiveWorkspace(), {paneWidth});
}
2016-07-07 20:10:55 +00:00
_saveSidebarWidth () {
const {sidebarWidth} = this.state;
2016-07-19 16:15:03 +00:00
db.workspaceUpdateMeta(this._getActiveWorkspace(), {sidebarWidth});
}
2016-05-01 19:56:30 +00:00
2016-07-07 20:10:55 +00:00
_getActiveWorkspace (props) {
// TODO: Factor this out into a selector
const {entities, workspaces} = props || this.props;
let workspace = entities.workspaces[workspaces.activeId];
if (!workspace) {
workspace = entities.workspaces[Object.keys(entities.workspaces)[0]];
}
return workspace;
}
2016-07-07 20:10:55 +00:00
_getActiveRequest (props) {
props = props || this.props;
const {entities} = props;
2016-07-19 16:15:03 +00:00
let activeRequestId = this._getActiveWorkspace(props).meta.activeRequestId;
return activeRequestId ? entities.requests[activeRequestId] : null;
}
2016-07-07 20:10:55 +00:00
_fetchActiveRequestGroup (props) {
return new Promise((resolve, reject) => {
props = props || this.props;
const request = this._getActiveRequest(props);
if (!request) {
reject();
}
db.requestGroupById(request.parentId).then(requestGroup => {
if (requestGroup) {
resolve(requestGroup);
} else {
reject();
}
});
});
}
2016-07-07 20:10:55 +00:00
_handleMouseMove (e) {
if (this.state.draggingPane) {
const requestPane = ReactDOM.findDOMNode(this.refs.requestPane);
const responsePane = ReactDOM.findDOMNode(this.refs.responsePane);
const requestPaneWidth = requestPane.offsetWidth;
const responsePaneWidth = responsePane.offsetWidth;
const pixelOffset = e.clientX - requestPane.offsetLeft;
let paneWidth = pixelOffset / (requestPaneWidth + responsePaneWidth);
paneWidth = Math.min(Math.max(paneWidth, MIN_PANE_WIDTH), MAX_PANE_WIDTH);
this.setState({paneWidth});
} else if (this.state.draggingSidebar) {
const currentPixelWidth = ReactDOM.findDOMNode(this.refs.sidebar).offsetWidth;
const ratio = e.clientX / currentPixelWidth;
const width = this.state.sidebarWidth * ratio;
const sidebarWidth = Math.max(Math.min(width, MAX_SIDEBAR_REMS), MIN_SIDEBAR_REMS);
this.setState({sidebarWidth})
}
}
2016-07-07 20:10:55 +00:00
_handleMouseUp () {
if (this.state.draggingSidebar) {
this.setState({
draggingSidebar: false
2016-07-14 22:48:56 +00:00
});
this._saveSidebarWidth();
}
2016-03-22 05:01:58 +00:00
if (this.state.draggingPane) {
this.setState({
draggingPane: false
2016-07-14 22:48:56 +00:00
});
this._savePaneWidth();
2016-06-19 07:21:43 +00:00
}
}
2016-07-07 20:10:55 +00:00
componentWillReceiveProps (nextProps) {
2016-07-19 16:15:03 +00:00
const sidebarWidth = this._getActiveWorkspace(nextProps).meta.sidebarWidth;
this.setState({sidebarWidth});
}
2016-07-07 20:10:55 +00:00
componentDidMount () {
// Bind handlers before we use them
this._handleMouseUp = this._handleMouseUp.bind(this);
this._handleMouseMove = this._handleMouseMove.bind(this);
// Bind mouse handlers
document.addEventListener('mouseup', this._handleMouseUp);
document.addEventListener('mousemove', this._handleMouseMove);
// Map global keyboard shortcuts
Object.keys(this.globalKeyMap).map(key => {
Mousetrap.bindGlobal(key.split('|'), this.globalKeyMap[key]);
});
}
2016-07-07 20:10:55 +00:00
componentWillUnmount () {
// Remove mouse handlers
document.removeEventListener('mouseup', this._handleMouseUp);
document.removeEventListener('mousemove', this._handleMouseMove);
// Unbind global keyboard shortcuts
Mousetrap.unbind();
}
2016-07-07 20:10:55 +00:00
render () {
const {actions, entities, requests} = this.props;
const workspace = this._getActiveWorkspace();
2016-07-19 16:15:03 +00:00
const activeRequest = this._getActiveRequest();
const activeRequestId = activeRequest ? activeRequest._id : null;
2016-06-19 07:21:43 +00:00
const responses = Object.keys(entities.responses).map(id => entities.responses[id]);
const allRequests = Object.keys(entities.requests).map(id => entities.requests[id]);
const allRequestGroups = Object.keys(entities.requestGroups).map(id => entities.requestGroups[id]);
const activeResponse = responses.sort(
(a, b) => a._id > b._id ? -1 : 1
).find(r => r.parentId === activeRequestId);
const children = this._generateSidebarTree(
workspace._id,
allRequests.concat(allRequestGroups)
);
2016-06-19 00:40:14 +00:00
const {sidebarWidth, paneWidth} = this.state;
const gridTemplateColumns = `${sidebarWidth}rem 0 ${paneWidth}fr 0 ${1 - paneWidth}fr`;
2016-07-19 16:15:03 +00:00
const classes = classnames('wrapper', {'wrapper--sidebar-hidden': !workspace.meta.sidebarVisible});
2016-03-20 04:47:43 +00:00
return (
2016-07-19 16:15:03 +00:00
<div id="wrapper" className={classes} style={{gridTemplateColumns: gridTemplateColumns}}>
2016-04-17 22:46:17 +00:00
<Sidebar
2016-06-19 00:08:14 +00:00
ref="sidebar"
2016-07-19 16:15:03 +00:00
activateRequest={r => db.workspaceUpdateMeta(workspace, {activeRequestId: r._id})}
changeFilter={filter => db.workspaceUpdate(workspace, {filter})}
addRequestToRequestGroup={requestGroup => db.requestCreate({parentId: requestGroup._id})}
2016-07-19 16:15:03 +00:00
toggleRequestGroup={requestGroup => db.requestGroupUpdateMeta(requestGroup, {collapsed: !requestGroup.meta.collapsed})}
activeRequestId={activeRequest ? activeRequest._id : null}
filter={workspace.filter || ''}
children={children}
/>
2016-05-07 17:29:24 +00:00
2016-06-19 00:08:14 +00:00
<div className="drag drag--sidebar">
2016-07-14 22:48:56 +00:00
<div onMouseDown={() => this._startDragSidebar()}
onDoubleClick={() => this._resetDragSidebar()}>
</div>
2016-06-19 00:08:14 +00:00
</div>
2016-05-01 19:56:30 +00:00
<RequestPane
2016-06-19 00:08:14 +00:00
ref="requestPane"
2016-05-01 19:56:30 +00:00
request={activeRequest}
sendRequest={actions.requests.send}
updateRequestBody={body => db.requestUpdate(activeRequest, {body})}
2016-07-14 22:48:56 +00:00
updateRequestUrl={url => this._handleUrlChanged(url)}
2016-05-01 19:56:30 +00:00
updateRequestMethod={method => db.requestUpdate(activeRequest, {method})}
updateRequestParameters={parameters => db.requestUpdate(activeRequest, {parameters})}
2016-05-01 19:56:30 +00:00
updateRequestAuthentication={authentication => db.requestUpdate(activeRequest, {authentication})}
updateRequestHeaders={headers => db.requestUpdate(activeRequest, {headers})}
updateRequestContentType={contentType => db.requestUpdate(activeRequest, {contentType})}
/>
2016-05-07 17:29:24 +00:00
2016-06-19 00:08:14 +00:00
<div className="drag drag--pane">
<div onMouseDown={() => this._startDragPane()}
onDoubleClick={() => this._resetDragPane()}></div>
2016-06-19 00:08:14 +00:00
</div>
2016-05-01 19:56:30 +00:00
<ResponsePane
2016-06-19 00:08:14 +00:00
ref="responsePane"
2016-05-01 19:56:30 +00:00
response={activeResponse}
2016-07-07 20:10:55 +00:00
request={activeRequest}
2016-07-19 16:15:03 +00:00
previewMode={activeRequest ? activeRequest.meta.previewMode : PREVIEW_MODE_FRIENDLY}
updatePreviewMode={previewMode => db.requestUpdateMeta(activeRequest, {previewMode})}
loadingRequests={requests.loadingRequests}
2016-05-01 19:56:30 +00:00
/>
2016-07-14 22:48:56 +00:00
<PromptModal />
<SettingsModal />
<RequestSwitcherModal />
<CurlExportModal />
<EnvironmentEditModal onChange={rg => db.requestGroupUpdate(rg)}/>
</div>
2016-03-20 04:47:43 +00:00
)
}
}
App.propTypes = {
2016-03-22 05:01:58 +00:00
actions: PropTypes.shape({
2016-04-16 23:24:57 +00:00
requests: PropTypes.shape({
send: PropTypes.func.isRequired,
changeFilter: PropTypes.func.isRequired
}),
requestGroups: PropTypes.shape({
toggle: PropTypes.func.isRequired
}),
modals: PropTypes.shape({
hide: PropTypes.func.isRequired
2016-04-16 23:24:57 +00:00
})
2016-03-22 05:01:58 +00:00
}).isRequired,
entities: PropTypes.shape({
requests: PropTypes.object.isRequired,
requestGroups: PropTypes.object.isRequired,
responses: PropTypes.object.isRequired
}).isRequired,
workspaces: PropTypes.shape({
activeId: PropTypes.string
2016-04-09 21:08:55 +00:00
}).isRequired,
2016-03-22 05:01:58 +00:00
requests: PropTypes.shape({
filter: PropTypes.string.isRequired,
loadingRequests: PropTypes.object.isRequired
2016-03-22 05:01:58 +00:00
}).isRequired,
2016-04-15 05:23:54 +00:00
modals: PropTypes.array.isRequired
2016-03-20 04:47:43 +00:00
};
2016-07-07 20:10:55 +00:00
function mapStateToProps (state) {
2016-03-20 04:47:43 +00:00
return {
actions: state.actions,
workspaces: state.workspaces,
2016-03-22 05:01:58 +00:00
requests: state.requests,
entities: state.entities,
2016-04-15 05:23:54 +00:00
modals: state.modals
2016-03-20 04:47:43 +00:00
};
}
2016-07-07 20:10:55 +00:00
function mapDispatchToProps (dispatch) {
2016-03-20 04:47:43 +00:00
return {
2016-04-16 23:24:57 +00:00
actions: {
requestGroups: bindActionCreators(RequestGroupActions, dispatch),
requests: bindActionCreators(RequestActions, dispatch)
}
2016-03-20 04:47:43 +00:00
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);