2016-07-16 02:06:10 +00:00
|
|
|
import React, {Component, PropTypes} from 'react';
|
2016-07-27 20:07:50 +00:00
|
|
|
import {ipcRenderer} from 'electron';
|
2016-07-19 16:15:03 +00:00
|
|
|
import ReactDOM from 'react-dom';
|
2016-11-19 07:11:10 +00:00
|
|
|
import * as importers from 'insomnia-importers';
|
2016-07-19 16:59:26 +00:00
|
|
|
import {connect} from 'react-redux';
|
2016-07-16 02:06:10 +00:00
|
|
|
import {bindActionCreators} from 'redux';
|
2016-07-19 16:59:26 +00:00
|
|
|
import HTML5Backend from 'react-dnd-html5-backend';
|
|
|
|
import {DragDropContext} from 'react-dnd';
|
2016-11-09 04:16:38 +00:00
|
|
|
import Mousetrap from '../mousetrap';
|
2016-11-16 17:18:39 +00:00
|
|
|
import {toggleModal, showModal} from '../components/modals';
|
|
|
|
import Wrapper from '../components/Wrapper';
|
2016-08-15 17:04:36 +00:00
|
|
|
import WorkspaceEnvironmentsEditModal from '../components/modals/WorkspaceEnvironmentsEditModal';
|
2016-11-16 17:18:39 +00:00
|
|
|
import Toast from '../components/Toast';
|
2016-08-15 17:04:36 +00:00
|
|
|
import CookiesModal from '../components/modals/CookiesModal';
|
2016-07-22 22:27:04 +00:00
|
|
|
import RequestSwitcherModal from '../components/modals/RequestSwitcherModal';
|
|
|
|
import PromptModal from '../components/modals/PromptModal';
|
|
|
|
import ChangelogModal from '../components/modals/ChangelogModal';
|
|
|
|
import SettingsModal from '../components/modals/SettingsModal';
|
2016-11-16 17:18:39 +00:00
|
|
|
import {MAX_PANE_WIDTH, MIN_PANE_WIDTH, DEFAULT_PANE_WIDTH, MAX_SIDEBAR_REMS, MIN_SIDEBAR_REMS, DEFAULT_SIDEBAR_WIDTH, getAppVersion} from '../../common/constants';
|
|
|
|
import * as globalActions from '../redux/modules/global';
|
|
|
|
import * as workspaceMetaActions from '../redux/modules/workspaceMeta';
|
|
|
|
import * as requestMetaActions from '../redux/modules/requestMeta';
|
|
|
|
import * as requestGroupMetaActions from '../redux/modules/requestGroupMeta';
|
2016-11-10 05:56:23 +00:00
|
|
|
import * as db from '../../common/database';
|
|
|
|
import * as models from '../../models';
|
2016-11-19 07:11:10 +00:00
|
|
|
import {importRaw} from '../../common/import';
|
2016-11-10 05:47:20 +00:00
|
|
|
import {trackEvent, trackLegacyEvent} from '../../analytics';
|
2016-11-16 17:18:39 +00:00
|
|
|
import {PREVIEW_MODE_SOURCE} from '../../common/constants';
|
2016-08-15 17:04:36 +00:00
|
|
|
|
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);
|
|
|
|
this.state = {
|
2016-06-19 00:08:14 +00:00
|
|
|
draggingSidebar: false,
|
|
|
|
draggingPane: false,
|
2016-10-24 23:30:37 +00:00
|
|
|
forceRefreshCounter: 0,
|
2016-07-06 20:18:26 +00:00
|
|
|
};
|
|
|
|
|
2016-11-16 17:18:39 +00:00
|
|
|
// Bind functions once, so we don't have to on every render
|
|
|
|
this._boundStartDragSidebar = this._startDragSidebar.bind(this);
|
|
|
|
this._boundResetDragSidebar = this._resetDragSidebar.bind(this);
|
|
|
|
this._boundStartDragPane = this._startDragPane.bind(this);
|
|
|
|
this._boundResetDragPane = this._resetDragPane.bind(this);
|
|
|
|
this._boundHandleUrlChange = this._handleUrlChanged.bind(this);
|
|
|
|
this._boundRequestCreate = this._requestCreate.bind(this);
|
|
|
|
this._boundRequestGroupCreate = this._requestGroupCreate.bind(this);
|
|
|
|
|
2016-07-06 20:18:26 +00:00
|
|
|
this.globalKeyMap = {
|
|
|
|
|
|
|
|
// Show Settings
|
|
|
|
'mod+,': () => {
|
2016-08-22 20:05:42 +00:00
|
|
|
// NOTE: This is controlled via a global menu shortcut in app.js
|
2016-07-06 20:18:26 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
// Show Request Switcher
|
2016-07-25 22:27:29 +00:00
|
|
|
'mod+p': () => {
|
2016-11-07 20:24:38 +00:00
|
|
|
toggleModal(RequestSwitcherModal);
|
2016-07-06 20:18:26 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
// Request Send
|
2016-07-21 21:34:38 +00:00
|
|
|
'mod+enter': () => {
|
2016-11-16 17:18:39 +00:00
|
|
|
const {handleSendRequestWithEnvironment, activeRequest, activeEnvironment} = this.props;
|
|
|
|
handleSendRequestWithEnvironment(
|
|
|
|
activeRequest ? activeRequest._id : 'n/a',
|
|
|
|
activeEnvironment ? activeEnvironment._id : 'n/a',
|
|
|
|
);
|
2016-07-06 20:18:26 +00:00
|
|
|
},
|
|
|
|
|
2016-08-15 17:04:36 +00:00
|
|
|
// Edit Workspace Environments
|
|
|
|
'mod+e': () => {
|
2016-11-16 17:18:39 +00:00
|
|
|
const {activeWorkspace} = this.props;
|
|
|
|
toggleModal(WorkspaceEnvironmentsEditModal, activeWorkspace);
|
2016-08-15 17:04:36 +00:00
|
|
|
},
|
|
|
|
|
2016-09-18 22:58:50 +00:00
|
|
|
// Focus URL Bar
|
|
|
|
'mod+l': () => {
|
|
|
|
const node = document.body.querySelector('.urlbar input');
|
|
|
|
node && node.focus();
|
2016-09-12 20:04:15 +00:00
|
|
|
},
|
|
|
|
|
2016-08-15 17:04:36 +00:00
|
|
|
// Edit Cookies
|
|
|
|
'mod+k': () => {
|
2016-11-16 17:18:39 +00:00
|
|
|
const {activeWorkspace} = this.props;
|
|
|
|
toggleModal(CookiesModal, activeWorkspace);
|
2016-08-15 17:04:36 +00:00
|
|
|
},
|
|
|
|
|
2016-09-10 03:50:45 +00:00
|
|
|
// Request Create
|
|
|
|
'mod+n': () => {
|
2016-11-16 17:18:39 +00:00
|
|
|
const {activeRequest, activeWorkspace} = this.props;
|
2016-09-10 03:50:45 +00:00
|
|
|
|
2016-11-16 17:18:39 +00:00
|
|
|
const parentId = activeRequest ? activeRequest.parentId : activeWorkspace._id;
|
2016-09-10 03:50:45 +00:00
|
|
|
this._requestCreate(parentId);
|
|
|
|
},
|
|
|
|
|
2016-07-06 20:18:26 +00:00
|
|
|
// Request Duplicate
|
2016-10-21 17:20:36 +00:00
|
|
|
'mod+d': async () => {
|
2016-11-16 17:18:39 +00:00
|
|
|
const {activeWorkspace, activeRequest, handleSetActiveRequest} = this.props;
|
2016-07-06 20:18:26 +00:00
|
|
|
|
2016-10-21 17:20:36 +00:00
|
|
|
if (!activeRequest) {
|
2016-07-06 20:18:26 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-11-10 01:15:27 +00:00
|
|
|
const request = await models.request.duplicate(activeRequest);
|
2016-11-16 17:18:39 +00:00
|
|
|
handleSetActiveRequest(activeWorkspace._id, request._id)
|
2016-07-19 16:59:26 +00:00
|
|
|
}
|
2016-10-02 20:57:00 +00:00
|
|
|
}
|
2016-07-19 16:59:26 +00:00
|
|
|
}
|
|
|
|
|
2016-10-02 20:57:00 +00:00
|
|
|
async _requestGroupCreate (parentId) {
|
2016-11-07 20:24:38 +00:00
|
|
|
const name = await showModal(PromptModal, {
|
2016-08-15 17:04:36 +00:00
|
|
|
headerName: 'Create New Folder',
|
|
|
|
defaultValue: 'My Folder',
|
|
|
|
selectText: true
|
|
|
|
});
|
2016-10-02 20:57:00 +00:00
|
|
|
|
2016-11-10 01:15:27 +00:00
|
|
|
models.requestGroup.create({parentId, name})
|
2016-08-15 17:04:36 +00:00
|
|
|
}
|
|
|
|
|
2016-10-02 20:57:00 +00:00
|
|
|
async _requestCreate (parentId) {
|
2016-11-07 20:24:38 +00:00
|
|
|
const name = await showModal(PromptModal, {
|
2016-08-15 17:04:36 +00:00
|
|
|
headerName: 'Create New Request',
|
|
|
|
defaultValue: 'My Request',
|
|
|
|
selectText: true
|
|
|
|
});
|
2016-10-02 20:57:00 +00:00
|
|
|
|
2016-11-16 17:18:39 +00:00
|
|
|
const {activeWorkspace, handleSetActiveRequest} = this.props;
|
2016-11-10 01:15:27 +00:00
|
|
|
const request = await models.request.create({parentId, name});
|
2016-05-01 19:56:30 +00:00
|
|
|
|
2016-11-16 17:18:39 +00:00
|
|
|
handleSetActiveRequest(activeWorkspace._id, request._id);
|
2016-04-17 20:35:35 +00:00
|
|
|
}
|
|
|
|
|
2016-10-21 17:20:36 +00:00
|
|
|
async _handleUrlChanged (request, url) {
|
2016-11-19 07:11:10 +00:00
|
|
|
// Allow user to paste any import file into the url. If it results in
|
|
|
|
// only one item, it will overwrite the current request.
|
|
|
|
|
|
|
|
try {
|
|
|
|
const {resources} = importers.import(url);
|
|
|
|
const r = resources[0];
|
|
|
|
if (r && r._type === 'request') {
|
|
|
|
// Only pull fields that we want to update
|
|
|
|
await models.request.update(request, {
|
|
|
|
url: r.url,
|
|
|
|
method: r.method,
|
2016-11-19 22:26:40 +00:00
|
|
|
headers: r.headers,
|
2016-11-19 07:11:10 +00:00
|
|
|
body: r.body,
|
|
|
|
authentication: r.authentication,
|
|
|
|
parameters: r.parameters,
|
|
|
|
});
|
|
|
|
|
|
|
|
this._forceHardRefresh();
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
// Import failed, that's alright
|
2016-07-14 22:48:56 +00:00
|
|
|
}
|
2016-11-19 07:11:10 +00:00
|
|
|
|
|
|
|
models.request.update(request, {url});
|
2016-07-14 22:48:56 +00:00
|
|
|
}
|
|
|
|
|
2016-07-07 20:10:55 +00:00
|
|
|
_startDragSidebar () {
|
2016-10-02 20:57:00 +00:00
|
|
|
this.setState({draggingSidebar: true})
|
2016-06-19 00:08:14 +00:00
|
|
|
}
|
|
|
|
|
2016-07-07 20:10:55 +00:00
|
|
|
_resetDragSidebar () {
|
2016-07-06 22:11:37 +00:00
|
|
|
// TODO: Remove setTimeout need be not triggering drag on double click
|
|
|
|
setTimeout(() => {
|
2016-11-16 17:18:39 +00:00
|
|
|
const {handleSetSidebarWidth, activeWorkspace} = this.props;
|
|
|
|
handleSetSidebarWidth(activeWorkspace._id, DEFAULT_SIDEBAR_WIDTH)
|
2016-07-06 22:11:37 +00:00
|
|
|
}, 50);
|
2016-06-19 00:08:14 +00:00
|
|
|
}
|
|
|
|
|
2016-07-07 20:10:55 +00:00
|
|
|
_startDragPane () {
|
2016-10-21 17:20:36 +00:00
|
|
|
this.setState({draggingPane: true})
|
2016-06-19 00:08:14 +00:00
|
|
|
}
|
|
|
|
|
2016-07-07 20:10:55 +00:00
|
|
|
_resetDragPane () {
|
2016-07-06 22:11:37 +00:00
|
|
|
// TODO: Remove setTimeout need be not triggering drag on double click
|
|
|
|
setTimeout(() => {
|
2016-11-16 17:18:39 +00:00
|
|
|
const {handleSetPaneWidth, activeWorkspace} = this.props;
|
|
|
|
handleSetPaneWidth(activeWorkspace._id, DEFAULT_PANE_WIDTH);
|
2016-07-06 22:11:37 +00:00
|
|
|
}, 50);
|
|
|
|
}
|
|
|
|
|
2016-07-07 20:10:55 +00:00
|
|
|
_handleMouseMove (e) {
|
2016-07-06 20:18:26 +00:00
|
|
|
if (this.state.draggingPane) {
|
2016-09-03 04:32:45 +00:00
|
|
|
const requestPane = ReactDOM.findDOMNode(this._requestPane);
|
|
|
|
const responsePane = ReactDOM.findDOMNode(this._responsePane);
|
2016-07-06 20:18:26 +00:00
|
|
|
|
|
|
|
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);
|
2016-11-16 17:18:39 +00:00
|
|
|
this.props.handleSetPaneWidth(this.props.activeWorkspace._id, paneWidth);
|
2016-07-06 22:11:37 +00:00
|
|
|
|
2016-07-06 20:18:26 +00:00
|
|
|
} else if (this.state.draggingSidebar) {
|
2016-09-03 04:32:45 +00:00
|
|
|
const currentPixelWidth = ReactDOM.findDOMNode(this._sidebar).offsetWidth;
|
2016-07-06 20:18:26 +00:00
|
|
|
const ratio = e.clientX / currentPixelWidth;
|
2016-11-16 17:18:39 +00:00
|
|
|
const width = this.props.sidebarWidth * ratio;
|
2016-08-15 17:04:36 +00:00
|
|
|
let sidebarWidth = Math.max(Math.min(width, MAX_SIDEBAR_REMS), MIN_SIDEBAR_REMS);
|
2016-11-16 17:18:39 +00:00
|
|
|
this.props.handleSetSidebarWidth(this.props.activeWorkspace._id, sidebarWidth);
|
2016-07-06 20:18:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-07 20:10:55 +00:00
|
|
|
_handleMouseUp () {
|
2016-07-06 20:18:26 +00:00
|
|
|
if (this.state.draggingSidebar) {
|
2016-11-16 17:18:39 +00:00
|
|
|
this.setState({draggingSidebar: false});
|
2016-07-06 20:18:26 +00:00
|
|
|
}
|
2016-03-22 05:01:58 +00:00
|
|
|
|
2016-07-06 20:18:26 +00:00
|
|
|
if (this.state.draggingPane) {
|
2016-11-16 17:18:39 +00:00
|
|
|
this.setState({draggingPane: false});
|
2016-06-19 07:21:43 +00:00
|
|
|
}
|
2016-07-06 20:18:26 +00:00
|
|
|
}
|
|
|
|
|
2016-09-12 20:04:15 +00:00
|
|
|
_handleToggleSidebar () {
|
2016-11-16 17:18:39 +00:00
|
|
|
const {activeWorkspace, sidebarHidden, handleSetSidebarHidden} = this.props;
|
|
|
|
handleSetSidebarHidden(activeWorkspace._id, !sidebarHidden);
|
2016-07-06 20:18:26 +00:00
|
|
|
}
|
|
|
|
|
2016-11-09 04:16:38 +00:00
|
|
|
_forceHardRefresh () {
|
|
|
|
this.setState({forceRefreshCounter: this.state.forceRefreshCounter + 1});
|
|
|
|
}
|
|
|
|
|
2016-10-02 20:57:00 +00:00
|
|
|
async componentDidMount () {
|
2016-07-06 20:18:26 +00:00
|
|
|
// 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-25 22:27:29 +00:00
|
|
|
|
|
|
|
// Do The Analytics
|
2016-10-27 03:41:30 +00:00
|
|
|
trackLegacyEvent('App Launched');
|
2016-07-25 22:27:29 +00:00
|
|
|
|
|
|
|
// Update Stats Object
|
2016-11-10 01:15:27 +00:00
|
|
|
const {lastVersion, launches} = await models.stats.get();
|
2016-10-02 20:57:00 +00:00
|
|
|
const firstLaunch = !lastVersion;
|
|
|
|
if (firstLaunch) {
|
|
|
|
// TODO: Show a welcome message
|
2016-10-27 03:41:30 +00:00
|
|
|
trackLegacyEvent('First Launch');
|
2016-10-02 20:57:00 +00:00
|
|
|
} else if (lastVersion !== getAppVersion()) {
|
2016-10-29 05:52:45 +00:00
|
|
|
trackEvent('General', 'Updated', getAppVersion());
|
2016-11-07 20:24:38 +00:00
|
|
|
showModal(ChangelogModal);
|
2016-10-02 20:57:00 +00:00
|
|
|
}
|
2016-07-25 22:27:29 +00:00
|
|
|
|
2016-10-24 23:30:37 +00:00
|
|
|
db.onChange(changes => {
|
|
|
|
for (const change of changes) {
|
|
|
|
const [event, doc, fromSync] = change;
|
|
|
|
|
|
|
|
// Not a sync-related change
|
|
|
|
if (!fromSync) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-11-16 17:18:39 +00:00
|
|
|
const {activeRequest} = this.props;
|
2016-11-07 20:24:38 +00:00
|
|
|
|
|
|
|
// No active request at the moment, so it doesn't matter
|
|
|
|
if (!activeRequest) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-10-24 23:30:37 +00:00
|
|
|
// Only force the UI to refresh if the active Request changes
|
|
|
|
// This is because things like the URL and Body editor don't update
|
|
|
|
// when you tell them to.
|
2016-11-07 20:24:38 +00:00
|
|
|
|
2016-10-24 23:30:37 +00:00
|
|
|
if (doc._id !== activeRequest._id) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
console.log('[App] Forcing update');
|
|
|
|
|
|
|
|
// All sync-related changes to data force-refresh the app.
|
2016-11-09 04:16:38 +00:00
|
|
|
this._forceHardRefresh();
|
2016-10-24 23:30:37 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2016-11-10 01:15:27 +00:00
|
|
|
models.stats.update({
|
2016-10-02 20:57:00 +00:00
|
|
|
launches: launches + 1,
|
|
|
|
lastLaunch: Date.now(),
|
|
|
|
lastVersion: getAppVersion()
|
2016-07-25 22:27:29 +00:00
|
|
|
});
|
2016-07-27 20:07:50 +00:00
|
|
|
|
2016-08-22 20:05:42 +00:00
|
|
|
ipcRenderer.on('toggle-preferences', () => {
|
2016-11-07 20:24:38 +00:00
|
|
|
toggleModal(SettingsModal);
|
2016-09-12 20:04:15 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
ipcRenderer.on('toggle-sidebar', this._handleToggleSidebar.bind(this));
|
2016-07-06 20:18:26 +00:00
|
|
|
}
|
|
|
|
|
2016-07-07 20:10:55 +00:00
|
|
|
componentWillUnmount () {
|
2016-07-06 20:18:26 +00:00
|
|
|
// 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 () {
|
2016-11-16 17:18:39 +00:00
|
|
|
return (
|
|
|
|
<div className="app">
|
|
|
|
<Wrapper
|
|
|
|
key={this.state.forceRefreshCounter}
|
|
|
|
handleSetRequestPaneRef={n => this._requestPane = n}
|
|
|
|
handleSetResponsePaneRef={n => this._responsePane = n}
|
|
|
|
handleSetSidebarRef={n => this._sidebar = n}
|
|
|
|
handleStartDragSidebar={this._boundStartDragSidebar}
|
|
|
|
handleResetDragSidebar={this._boundResetDragSidebar}
|
|
|
|
handleStartDragPane={this._boundStartDragPane}
|
|
|
|
handleResetDragPane={this._boundResetDragPane}
|
|
|
|
handleUpdateRequestUrl={this._boundHandleUrlChange}
|
|
|
|
handleCreateRequest={this._boundRequestCreate}
|
|
|
|
handleCreateRequestGroup={this._boundRequestGroupCreate}
|
|
|
|
{...this.props}
|
|
|
|
/>
|
|
|
|
<Toast/>
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
2016-07-06 20:18:26 +00:00
|
|
|
|
2016-11-16 17:18:39 +00:00
|
|
|
function mapStateToProps (state) {
|
|
|
|
const {
|
|
|
|
entities,
|
|
|
|
global,
|
|
|
|
workspaceMeta,
|
|
|
|
requestMeta,
|
|
|
|
requestGroupMeta
|
|
|
|
} = state;
|
|
|
|
|
|
|
|
const {
|
|
|
|
activeRequestIds,
|
|
|
|
activeEnvironmentIds,
|
|
|
|
sidebarHiddens,
|
|
|
|
sidebarFilters,
|
|
|
|
sidebarWidths,
|
|
|
|
paneWidths
|
|
|
|
} = workspaceMeta;
|
|
|
|
|
|
|
|
const {
|
|
|
|
loadingRequestIds,
|
|
|
|
previewModes,
|
|
|
|
responseFilters,
|
|
|
|
} = requestMeta;
|
|
|
|
|
|
|
|
const {
|
|
|
|
isLoading,
|
|
|
|
} = global;
|
|
|
|
|
|
|
|
// Entities
|
|
|
|
// TODO: Use selectors for these...
|
|
|
|
const workspaces = Object.keys(entities.workspaces).map(id => entities.workspaces[id]);
|
|
|
|
const environments = Object.keys(entities.environments).map(id => entities.environments[id]);
|
|
|
|
const requests = Object.keys(entities.requests).map(id => entities.requests[id]);
|
|
|
|
const requestGroups = Object.keys(entities.requestGroups).map(id => entities.requestGroups[id]);
|
|
|
|
const settings = entities.settings[Object.keys(entities.settings)[0]];
|
|
|
|
|
|
|
|
// Workspace stuff
|
|
|
|
const activeWorkspace = entities.workspaces[global.activeWorkspaceId] || workspaces[0];
|
|
|
|
const activeWorkspaceId = activeWorkspace._id;
|
|
|
|
const sidebarHidden = !!sidebarHiddens[activeWorkspaceId];
|
|
|
|
const sidebarFilter = sidebarFilters[activeWorkspaceId] || '';
|
|
|
|
const sidebarWidth = sidebarWidths[activeWorkspaceId] || DEFAULT_SIDEBAR_WIDTH;
|
|
|
|
const paneWidth = paneWidths[activeWorkspaceId] || DEFAULT_PANE_WIDTH;
|
|
|
|
|
|
|
|
// Request stuff
|
|
|
|
const activeRequestId = activeRequestIds[activeWorkspaceId];
|
|
|
|
const activeRequest = entities.requests[activeRequestIds[activeWorkspaceId]];
|
|
|
|
const responsePreviewMode = previewModes[activeRequestId] || PREVIEW_MODE_SOURCE;
|
|
|
|
const responseFilter = responseFilters[activeRequestId] || '';
|
|
|
|
|
|
|
|
// Environment stuff
|
|
|
|
const activeEnvironmentId = activeEnvironmentIds[activeWorkspaceId];
|
|
|
|
const activeEnvironment = entities.environments[activeEnvironmentId];
|
|
|
|
|
|
|
|
// Find other meta things
|
|
|
|
const loadStartTime = loadingRequestIds[activeRequestId] || -1;
|
|
|
|
const sidebarChildren = _generateSidebarTree(
|
|
|
|
activeWorkspace._id,
|
|
|
|
requests.concat(requestGroups),
|
|
|
|
requestGroupMeta.collapsed,
|
|
|
|
);
|
|
|
|
|
|
|
|
return Object.assign({}, state, {
|
|
|
|
settings,
|
|
|
|
workspaces,
|
|
|
|
requestGroups,
|
|
|
|
requests,
|
|
|
|
isLoading,
|
|
|
|
loadStartTime,
|
|
|
|
activeWorkspace,
|
|
|
|
activeRequest,
|
|
|
|
sidebarHidden,
|
|
|
|
sidebarFilter,
|
|
|
|
sidebarWidth,
|
|
|
|
responsePreviewMode,
|
|
|
|
responseFilter,
|
|
|
|
paneWidth,
|
|
|
|
sidebarChildren,
|
|
|
|
environments,
|
|
|
|
activeEnvironment,
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
2016-07-06 20:18:26 +00:00
|
|
|
|
2016-11-16 17:18:39 +00:00
|
|
|
function mapDispatchToProps (dispatch) {
|
|
|
|
const legacyActions = {
|
|
|
|
global: bindActionCreators(globalActions, dispatch)
|
|
|
|
};
|
2016-06-19 07:21:43 +00:00
|
|
|
|
2016-11-16 17:18:39 +00:00
|
|
|
const workspace = bindActionCreators(workspaceMetaActions, dispatch);
|
|
|
|
const requestGroups = bindActionCreators(requestGroupMetaActions, dispatch);
|
|
|
|
const requests = bindActionCreators(requestMetaActions, dispatch);
|
2016-04-26 07:29:24 +00:00
|
|
|
|
2016-11-16 17:18:39 +00:00
|
|
|
return {
|
|
|
|
actions: legacyActions,
|
|
|
|
handleSetPaneWidth: workspace.setPaneWidth,
|
|
|
|
handleSetActiveRequest: workspace.setActiveRequest,
|
|
|
|
handleSetActiveEnvironment: workspace.setActiveEnvironment,
|
|
|
|
handleSetSidebarWidth: workspace.setSidebarWidth,
|
|
|
|
handleSetSidebarHidden: workspace.setSidebarHidden,
|
|
|
|
handleSetSidebarFilter: workspace.setSidebarFilter,
|
|
|
|
handleSendRequestWithEnvironment: requests.send,
|
|
|
|
handleSetResponsePreviewMode: requests.setPreviewMode,
|
|
|
|
handleSetResponseFilter: requests.setResponseFilter,
|
|
|
|
|
|
|
|
handleSetActiveWorkspace: legacyActions.global.setActiveWorkspace,
|
|
|
|
handleImportFileToWorkspace: legacyActions.global.importFile,
|
|
|
|
handleExportFile: legacyActions.global.exportFile,
|
|
|
|
handleMoveRequest: _moveRequest,
|
|
|
|
handleMoveRequestGroup: _moveRequestGroup,
|
|
|
|
|
|
|
|
handleSetRequestGroupCollapsed: requestGroups.setCollapsed,
|
|
|
|
};
|
|
|
|
}
|
2016-03-22 03:22:45 +00:00
|
|
|
|
2016-11-16 17:18:39 +00:00
|
|
|
function _generateSidebarTree (parentId, entities, collapsed) {
|
|
|
|
const children = entities.filter(
|
|
|
|
e => e.parentId === parentId
|
|
|
|
).sort((a, b) => {
|
|
|
|
// Always sort folders above
|
|
|
|
if (a.type === models.requestGroup.type && b.type !== models.requestGroup.type) {
|
|
|
|
return -1;
|
|
|
|
}
|
2016-06-19 00:40:14 +00:00
|
|
|
|
2016-11-16 17:18:39 +00:00
|
|
|
if (a.metaSortKey === b.metaSortKey) {
|
|
|
|
return a._id > b._id ? -1 : 1;
|
|
|
|
} else {
|
|
|
|
return a.metaSortKey < b.metaSortKey ? -1 : 1;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if (children.length > 0) {
|
|
|
|
return children.map(c => ({
|
|
|
|
doc: c,
|
|
|
|
children: _generateSidebarTree(c._id, entities, collapsed),
|
|
|
|
collapsed: !!collapsed[c._id],
|
|
|
|
}));
|
|
|
|
} else {
|
|
|
|
return children;
|
|
|
|
}
|
|
|
|
}
|
2016-05-07 17:29:24 +00:00
|
|
|
|
2016-11-16 17:18:39 +00:00
|
|
|
async function _moveRequestGroup (requestGroupToMove, requestGroupToTarget, targetOffset) {
|
|
|
|
// Oh God, this function is awful...
|
2016-05-07 17:29:24 +00:00
|
|
|
|
2016-11-16 17:18:39 +00:00
|
|
|
if (requestGroupToMove._id === requestGroupToTarget._id) {
|
|
|
|
// Nothing to do
|
|
|
|
return;
|
|
|
|
}
|
2016-04-26 07:29:24 +00:00
|
|
|
|
2016-11-16 17:18:39 +00:00
|
|
|
// NOTE: using requestToTarget's parentId so we can switch parents!
|
|
|
|
let requestGroups = await models.requestGroup.findByParentId(requestGroupToTarget.parentId);
|
|
|
|
requestGroups = requestGroups.sort((a, b) => a.metaSortKey < b.metaSortKey ? -1 : 1);
|
|
|
|
|
|
|
|
// Find the index of request B so we can re-order and save everything
|
|
|
|
for (let i = 0; i < requestGroups.length; i++) {
|
|
|
|
const request = requestGroups[i];
|
|
|
|
|
|
|
|
if (request._id === requestGroupToTarget._id) {
|
|
|
|
let before, after;
|
|
|
|
if (targetOffset < 0) {
|
|
|
|
// We're moving to below
|
|
|
|
before = requestGroups[i];
|
|
|
|
after = requestGroups[i + 1];
|
|
|
|
} else {
|
|
|
|
// We're moving to above
|
|
|
|
before = requestGroups[i - 1];
|
|
|
|
after = requestGroups[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
const beforeKey = before ? before.metaSortKey : requestGroups[0].metaSortKey - 100;
|
|
|
|
const afterKey = after ? after.metaSortKey : requestGroups[requestGroups.length - 1].metaSortKey + 100;
|
|
|
|
|
|
|
|
if (Math.abs(afterKey - beforeKey) < 0.000001) {
|
|
|
|
// If sort keys get too close together, we need to redistribute the list. This is
|
|
|
|
// not performant at all (need to update all siblings in DB), but it is extremely rare
|
|
|
|
// anyway
|
|
|
|
console.log(`-- Recreating Sort Keys ${beforeKey} ${afterKey} --`);
|
|
|
|
|
|
|
|
db.bufferChanges(300);
|
|
|
|
requestGroups.map((r, i) => {
|
|
|
|
models.requestGroup.update(r, {
|
|
|
|
metaSortKey: i * 100,
|
|
|
|
parentId: requestGroupToTarget.parentId
|
|
|
|
});
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
const metaSortKey = afterKey - (afterKey - beforeKey) / 2;
|
|
|
|
models.requestGroup.update(requestGroupToMove, {
|
|
|
|
metaSortKey,
|
|
|
|
parentId: requestGroupToTarget.parentId
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
2016-03-20 04:47:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-16 17:18:39 +00:00
|
|
|
async function _moveRequest (requestToMove, parentId, targetId, targetOffset) {
|
|
|
|
// Oh God, this function is awful...
|
2016-03-20 04:47:43 +00:00
|
|
|
|
2016-11-16 17:18:39 +00:00
|
|
|
if (requestToMove._id === targetId) {
|
|
|
|
// Nothing to do. We are in the same spot as we started
|
|
|
|
return;
|
|
|
|
}
|
2016-03-20 04:47:43 +00:00
|
|
|
|
2016-11-16 17:18:39 +00:00
|
|
|
if (targetId === null) {
|
|
|
|
// We are moving to an empty area. No sorting required
|
|
|
|
models.request.update(requestToMove, {parentId});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// NOTE: using requestToTarget's parentId so we can switch parents!
|
|
|
|
let requests = await models.request.findByParentId(parentId);
|
|
|
|
requests = requests.sort((a, b) => a.metaSortKey < b.metaSortKey ? -1 : 1);
|
|
|
|
|
|
|
|
// Find the index of request B so we can re-order and save everything
|
|
|
|
for (let i = 0; i < requests.length; i++) {
|
|
|
|
const request = requests[i];
|
|
|
|
|
|
|
|
if (request._id === targetId) {
|
|
|
|
let before, after;
|
|
|
|
if (targetOffset < 0) {
|
|
|
|
// We're moving to below
|
|
|
|
before = requests[i];
|
|
|
|
after = requests[i + 1];
|
|
|
|
} else {
|
|
|
|
// We're moving to above
|
|
|
|
before = requests[i - 1];
|
|
|
|
after = requests[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
const beforeKey = before ? before.metaSortKey : requests[0].metaSortKey - 100;
|
|
|
|
const afterKey = after ? after.metaSortKey : requests[requests.length - 1].metaSortKey + 100;
|
|
|
|
|
|
|
|
if (Math.abs(afterKey - beforeKey) < 0.000001) {
|
|
|
|
// If sort keys get too close together, we need to redistribute the list. This is
|
|
|
|
// not performant at all (need to update all siblings in DB), but it is extremely rare
|
|
|
|
// anyway
|
|
|
|
console.log(`-- Recreating Sort Keys ${beforeKey} ${afterKey} --`);
|
|
|
|
|
|
|
|
db.bufferChanges(300);
|
|
|
|
requests.map((r, i) => {
|
|
|
|
models.request.update(r, {metaSortKey: i * 100, parentId});
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
const metaSortKey = afterKey - (afterKey - beforeKey) / 2;
|
|
|
|
models.request.update(requestToMove, {metaSortKey, parentId});
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
2016-04-16 23:24:57 +00:00
|
|
|
}
|
2016-03-20 04:47:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-16 17:18:39 +00:00
|
|
|
const reduxApp = connect(mapStateToProps, mapDispatchToProps)(App);
|
2016-07-19 16:59:26 +00:00
|
|
|
export default DragDropContext(HTML5Backend)(reduxApp);
|
|
|
|
|