- {this.renderRequestGroupRow(null)}
- {requestGroups.map(requestGroup => this.renderRequestGroupRow(requestGroup))}
+ {this._renderChildren(children)}
@@ -145,7 +95,7 @@ class Sidebar extends Component {
type="text"
placeholder="Filter Items"
debounceMillis={300}
- value={activeFilter}
+ value={filter}
onChange={this.onFilterChange.bind(this)}/>
@@ -156,14 +106,19 @@ class Sidebar extends Component {
}
Sidebar.propTypes = {
+ // Functions
activateRequest: PropTypes.func.isRequired,
+ toggleRequestGroup: PropTypes.func.isRequired,
addRequestToRequestGroup: PropTypes.func.isRequired,
changeFilter: PropTypes.func.isRequired,
- toggleRequestGroup: PropTypes.func.isRequired,
- activeFilter: PropTypes.string,
- requests: PropTypes.array.isRequired,
- requestGroups: PropTypes.array.isRequired,
- activeRequest: PropTypes.object
+
+ // Other
+ children: PropTypes.array.isRequired,
+ workspaceId: PropTypes.string.isRequired,
+
+ // Optional
+ filter: PropTypes.string,
+ activeRequestId: PropTypes.string
};
export default Sidebar;
diff --git a/app/components/SidebarRequestGroupRow.js b/app/components/SidebarRequestGroupRow.js
new file mode 100644
index 000000000..b14d473e8
--- /dev/null
+++ b/app/components/SidebarRequestGroupRow.js
@@ -0,0 +1,79 @@
+import React, {Component, PropTypes} from 'react'
+import classnames from 'classnames'
+import RequestGroupActionsDropdown from './../containers/RequestGroupActionsDropdown'
+import SidebarRequestRow from './SidebarRequestRow'
+
+class SidebarRequestGroupRow extends Component {
+ render () {
+ const {
+ children,
+ hideIfNoChildren,
+ requestGroup,
+ isActive,
+ toggleRequestGroup,
+ addRequestToRequestGroup
+ } = this.props;
+
+ // If we are supposed to have children, but aren't passed any, we are probably
+ // filtering so don't render anything
+ if (hideIfNoChildren && children.length === 0) {
+ return null;
+ }
+
+ let folderIconClass = 'fa-folder';
+ let expanded = !requestGroup.collapsed;
+ folderIconClass += !expanded ? '' : '-open';
+ folderIconClass += isActive ? '' : '-o';
+
+ const sidebarItemClassNames = classnames(
+ 'sidebar__item',
+ 'sidebar__item--bordered',
+ {'sidebar__item--active': isActive}
+ );
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ {!expanded || children.length > 0 ? null : (
+ {}}
+ isActive={false}
+ request={null}
+ />
+ )}
+ {expanded ? children : null}
+
+
+ );
+ }
+}
+
+SidebarRequestGroupRow.propTypes = {
+ // Functions
+ toggleRequestGroup: PropTypes.func.isRequired,
+ addRequestToRequestGroup: PropTypes.func.isRequired,
+
+ // Other
+ isActive: PropTypes.bool.isRequired,
+ hideIfNoChildren: PropTypes.number.isRequired,
+ requestGroup: PropTypes.object.isRequired
+};
+
+export default SidebarRequestGroupRow;
diff --git a/app/components/SidebarRequestRow.js b/app/components/SidebarRequestRow.js
new file mode 100644
index 000000000..20481e953
--- /dev/null
+++ b/app/components/SidebarRequestRow.js
@@ -0,0 +1,47 @@
+import React, {Component, PropTypes} from 'react'
+import RequestActionsDropdown from './../containers/RequestActionsDropdown'
+import MethodTag from './MethodTag'
+
+class SidebarRequestRow extends Component {
+ render () {
+ const {request, requestGroup, isActive, activateRequest} = this.props;
+
+ return (
+
+
+
+ {request ? (
+
+ ) : (
+
+ )}
+
+ {request ? (
+
+ ) : null}
+
+
+ );
+ }
+}
+
+SidebarRequestRow.propTypes = {
+ // Functions
+ activateRequest: PropTypes.func.isRequired,
+
+ // Other
+ isActive: PropTypes.bool.isRequired,
+
+ // Optional
+ requestGroup: PropTypes.object,
+ request: PropTypes.object
+};
+
+export default SidebarRequestRow;
diff --git a/app/containers/App.js b/app/containers/App.js
index ec14081ab..481ff275f 100644
--- a/app/containers/App.js
+++ b/app/containers/App.js
@@ -1,31 +1,20 @@
import React, {Component, PropTypes} from 'react'
import {connect} from 'react-redux'
import {bindActionCreators} from 'redux'
-import {Tab, Tabs, TabList, TabPanel} from 'react-tabs'
-import Editor from '../components/base/Editor'
import Prompts from './Prompts'
-import KeyValueEditor from '../components/base/KeyValueEditor'
-import RequestBodyEditor from '../components/RequestBodyEditor'
-import RequestAuthEditor from '../components/RequestAuthEditor'
-import RequestUrlBar from '../components/RequestUrlBar'
-import StatusTag from '../components/StatusTag'
-import SizeTag from '../components/SizeTag'
-import TimeTag from '../components/TimeTag'
-import Sidebar from '../components/Sidebar'
import EnvironmentEditModal from '../components/EnvironmentEditModal'
+import RequestPane from '../components/RequestPane'
+import ResponsePane from '../components/ResponsePane'
+import Sidebar from '../components/Sidebar'
import * as GlobalActions from '../redux/modules/global'
import * as RequestGroupActions from '../redux/modules/requestGroups'
import * as RequestActions from '../redux/modules/requests'
import * as ModalActions from '../redux/modules/modals'
-import * as TabActions from '../redux/modules/tabs'
import * as db from '../database'
-// Don't inject component styles (use our own)
-Tabs.setUseDefaultStyles(false);
-
class App extends Component {
constructor (props) {
super(props);
@@ -35,183 +24,74 @@ class App extends Component {
}
}
- _renderRequestPanel (actions, activeRequest, tabs) {
- if (!activeRequest) {
- return (
-
- )
+ _generateSidebarTree (parentId, entities) {
+ const children = entities.filter(e => e.parentId === parentId);
+
+ if (children.length > 0) {
+ return children.map(c => ({
+ doc: c,
+ children: this._generateSidebarTree(c._id, entities)
+ }));
+ } else {
+ return children;
}
-
- return (
-
-
-
- {db.update(activeRequest, {url})}}
- onMethodChange={method => {db.update(activeRequest, {method})}}
- request={activeRequest}
- />
-
-
actions.tabs.select('request', i)}
- selectedIndex={tabs.request || 0}>
-
-
-
-
-
-
-
-
-
-
-
- {db.update(activeRequest, {body})}}
- request={activeRequest}/>
-
-
-
- {db.update(activeRequest, {params})}}
- />
-
-
-
-
- {db.update(activeRequest, {authentication})}}
- />
-
-
-
-
- {db.update(activeRequest, {headers})}}
- />
-
-
-
-
-
- )
- }
-
- _renderResponsePanel (actions, activeResponse, tabs) {
- if (!activeResponse) {
- return (
-
- )
- }
-
- return (
-
-
-
- {!activeResponse ? null : (
-
-
-
-
-
- )}
-
-
actions.tabs.select('response', i)}
- selectedIndex={tabs.response || 0}>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {!activeResponse ? null : activeResponse.headers.map((h, i) => (
-
- ))}
-
-
-
-
-
-
- )
}
render () {
- const {actions, requests, responses, requestGroups, tabs, modals} = this.props;
- const activeRequest = requests.all.find(r => r._id === requests.active);
- const activeResponse = activeRequest ? responses[activeRequest._id] : undefined;
+ const {actions, modals, workspaces, requests, entities} = this.props;
+
+ // TODO: Factor this out into a selector
+ let workspace = entities.workspaces[workspaces.activeId];
+ if (!workspace) {
+ workspace = entities.workspaces[Object.keys(entities.workspaces)[0]];
+ }
+
+ const activeRequestId = workspace.activeRequestId;
+ const activeRequest = activeRequestId ? entities.requests[activeRequestId] : null;
+
+ 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)
+ );
return (
db.update(workspace, {activeRequestId: r._id})}
changeFilter={actions.requests.changeFilter}
- addRequestToRequestGroup={requestGroup => db.requestCreate({parent: requestGroup._id})}
+ addRequestToRequestGroup={requestGroup => db.requestCreate({parentId: requestGroup._id})}
toggleRequestGroup={requestGroup => db.update(requestGroup, {collapsed: !requestGroup.collapsed})}
- activeRequest={activeRequest}
- activeFilter={requests.filter}
- requestGroups={requestGroups.all}
- requests={requests.all}/>
+ activeRequestId={activeRequest ? activeRequest._id : null}
+ filter={requests.filter}
+ children={children}
+ />
- {this._renderRequestPanel(actions, activeRequest, tabs)}
- {this._renderResponsePanel(actions, activeResponse, tabs)}
+ db.update(activeRequest, {body})}
+ updateRequestUrl={url => db.update(activeRequest, {url})}
+ updateRequestMethod={method => db.update(activeRequest, {method})}
+ updateRequestParams={params => db.update(activeRequest, {params})}
+ updateRequestAuthentication={authentication => db.update(activeRequest, {authentication})}
+ updateRequestHeaders={headers => db.update(activeRequest, {headers})}
+ />
+
+
+
{modals.map(m => {
if (m.id === EnvironmentEditModal.defaultProps.id) {
return (
@@ -234,43 +114,36 @@ class App extends Component {
App.propTypes = {
actions: PropTypes.shape({
requests: PropTypes.shape({
- activate: PropTypes.func.isRequired,
- update: PropTypes.func.isRequired,
- remove: PropTypes.func.isRequired,
send: PropTypes.func.isRequired,
changeFilter: PropTypes.func.isRequired
}),
requestGroups: PropTypes.shape({
- remove: PropTypes.func.isRequired,
- update: PropTypes.func.isRequired,
toggle: PropTypes.func.isRequired
}),
modals: PropTypes.shape({
hide: PropTypes.func.isRequired
- }),
- tabs: PropTypes.shape({
- select: PropTypes.func.isRequired
})
}).isRequired,
- requestGroups: PropTypes.shape({
- all: PropTypes.array.isRequired
+ entities: PropTypes.shape({
+ requests: PropTypes.object.isRequired,
+ requestGroups: PropTypes.object.isRequired,
+ responses: PropTypes.object.isRequired
+ }).isRequired,
+ workspaces: PropTypes.shape({
+ activeId: PropTypes.string
}).isRequired,
requests: PropTypes.shape({
- all: PropTypes.array.isRequired,
- active: PropTypes.string // "required" but can be null
+ filter: PropTypes.string.isRequired
}).isRequired,
- responses: PropTypes.object.isRequired,
- tabs: PropTypes.object.isRequired,
modals: PropTypes.array.isRequired
};
function mapStateToProps (state) {
return {
actions: state.actions,
+ workspaces: state.workspaces,
requests: state.requests,
- requestGroups: state.requestGroups,
- responses: state.responses,
- tabs: state.tabs,
+ entities: state.entities,
modals: state.modals
};
}
@@ -279,7 +152,6 @@ function mapDispatchToProps (dispatch) {
return {
actions: {
global: bindActionCreators(GlobalActions, dispatch),
- tabs: bindActionCreators(TabActions, dispatch),
modals: bindActionCreators(ModalActions, dispatch),
requestGroups: bindActionCreators(RequestGroupActions, dispatch),
requests: bindActionCreators(RequestActions, dispatch)
diff --git a/app/containers/Prompts.js b/app/containers/Prompts.js
index c5dabbd45..83ad938e2 100644
--- a/app/containers/Prompts.js
+++ b/app/containers/Prompts.js
@@ -3,12 +3,14 @@ import {connect} from 'react-redux'
import {bindActionCreators} from 'redux'
import * as ModalActions from '../redux/modules/modals'
-import * as RequestGroupActions from '../redux/modules/requestGroups'
-import * as RequestActions from '../redux/modules/requests'
import PromptModal from '../components/base/PromptModal'
import * as db from '../database'
-import {MODAL_REQUEST_RENAME, MODAL_REQUEST_GROUP_RENAME} from '../lib/constants';
+import {
+ MODAL_REQUEST_RENAME,
+ MODAL_REQUEST_GROUP_RENAME,
+ MODAL_WORKSPACE_RENAME
+} from '../lib/constants';
class Prompts extends Component {
constructor (props) {
@@ -29,6 +31,14 @@ class Prompts extends Component {
db.update(modal.data.requestGroup, {name})
}
};
+
+ this._prompts[MODAL_WORKSPACE_RENAME] = {
+ header: 'Rename Workspace',
+ submit: 'Rename',
+ onSubmit: (modal, name) => {
+ db.update(modal.data.workspace, {name})
+ }
+ };
}
render () {
@@ -65,12 +75,6 @@ Prompts.propTypes = {
actions: PropTypes.shape({
modals: PropTypes.shape({
hide: PropTypes.func.isRequired
- }),
- requestGroups: PropTypes.shape({
- update: PropTypes.func.isRequired
- }),
- requests: PropTypes.shape({
- update: PropTypes.func.isRequired
})
}),
modals: PropTypes.array.isRequired
@@ -86,9 +90,7 @@ function mapStateToProps (state) {
function mapDispatchToProps (dispatch) {
return {
actions: {
- requests: bindActionCreators(RequestActions, dispatch),
- modals: bindActionCreators(ModalActions, dispatch),
- requestGroups: bindActionCreators(RequestGroupActions, dispatch)
+ modals: bindActionCreators(ModalActions, dispatch)
}
}
}
diff --git a/app/containers/RequestActionsDropdown.js b/app/containers/RequestActionsDropdown.js
index 6b9a48b5d..d5bba2c80 100644
--- a/app/containers/RequestActionsDropdown.js
+++ b/app/containers/RequestActionsDropdown.js
@@ -17,7 +17,7 @@ class RequestActionsDropdown extends Component {
-
-
diff --git a/app/containers/RequestGroupActionsDropdown.js b/app/containers/RequestGroupActionsDropdown.js
index 1ae002b37..a950282fd 100644
--- a/app/containers/RequestGroupActionsDropdown.js
+++ b/app/containers/RequestGroupActionsDropdown.js
@@ -38,8 +38,6 @@ class RequestGroupActionsDropdown extends Component {
RequestGroupActionsDropdown.propTypes = {
actions: PropTypes.shape({
- update: PropTypes.func.isRequired,
- remove: PropTypes.func.isRequired,
showUpdateNamePrompt: PropTypes.func.isRequired,
showEnvironmentEditModal: PropTypes.func.isRequired
}),
diff --git a/app/containers/WorkspaceDropdown.js b/app/containers/WorkspaceDropdown.js
index 53bfac449..ec76236d8 100644
--- a/app/containers/WorkspaceDropdown.js
+++ b/app/containers/WorkspaceDropdown.js
@@ -8,6 +8,7 @@ import {connect} from 'react-redux'
import Dropdown from '../components/base/Dropdown'
import DropdownDivider from '../components/base/DropdownDivider'
import * as RequestGroupActions from '../redux/modules/requestGroups'
+import * as WorkspaceActions from '../redux/modules/workspaces'
import * as db from '../database'
import importData from '../lib/import'
@@ -19,25 +20,46 @@ class WorkspaceDropdown extends Component {
name: 'Insomnia Imports', extensions: ['json']
}]
};
-
+
+ // TODO: Factor this out into a selector
+ const {entities, workspaces} = this.props;
+ let workspace = entities.workspaces[workspaces.activeId];
+ if (!workspace) {
+ workspace = entities.workspaces[Object.keys(entities.workspaces)[0]];
+ }
+
electron.remote.dialog.showOpenDialog(options, paths => {
paths.map(path => {
fs.readFile(path, 'utf8', (err, data) => {
- err || importData(data);
+ err || importData(workspace, data);
})
})
});
}
+
+ _workspaceCreate () {
+ db.workspaceCreate({name: 'New Workspace'}).then(workspace => {
+ this.props.actions.workspaces.activate(workspace);
+ });
+ }
render () {
- const {actions, loading, ...other} = this.props;
+ const {actions, loading, workspaces, entities, ...other} = this.props;
+
+ const allWorkspaces = Object.keys(entities.workspaces).map(id => entities.workspaces[id]);
+
+ // TODO: Factor this out into a selector
+ let workspace = entities.workspaces[workspaces.activeId];
+ if (!workspace) {
+ workspace = entities.workspaces[Object.keys(entities.workspaces)[0]];
+ }
return (