Sweeping performance improvements

This commit is contained in:
Gregory Schier 2016-11-25 15:09:17 -08:00
parent 92e8005259
commit 3523b9767d
15 changed files with 524 additions and 371 deletions

View File

@ -53,12 +53,6 @@ class RequestPane extends Component {
<code>{MOD_SYM}P</code>
</td>
</tr>
<tr>
<td>Manage Cookies</td>
<td className="text-right">
<code>{MOD_SYM}K</code>
</td>
</tr>
<tr>
<td>Edit Environments</td>
<td className="text-right">
@ -78,7 +72,7 @@ class RequestPane extends Component {
</button>
<button className="btn inline-block btn--super-compact btn--outlined"
onClick={e => {
handleCreateRequest();
handleCreateRequest(this.props.request);
trackEvent('Request Pane', 'CTA', 'New Request');
}}>
New Request

View File

@ -1,4 +1,4 @@
import React, {PropTypes} from 'react';
import React, {PropTypes, Component} from 'react';
import classnames from 'classnames';
import {showModal, registerModal} from './modals/index';
import WorkspaceEnvironmentsEditModal from '../components/modals/WorkspaceEnvironmentsEditModal';
@ -22,176 +22,210 @@ import * as models from '../../models/index';
import {updateMimeType} from '../../models/request';
const Wrapper = props => {
const {
isLoading,
loadStartTime,
activeWorkspace,
activeRequest,
activeEnvironment,
sidebarHidden,
sidebarFilter,
sidebarWidth,
paneWidth,
forceRefreshCounter,
workspaces,
settings,
environments,
responsePreviewMode,
responseFilter,
handleSetResponsePreviewMode,
handleSetResponseFilter,
handleSendRequestWithEnvironment,
handleCreateRequest,
handleCreateRequestGroup,
handleImportFileToWorkspace,
handleExportFile,
handleSetActiveRequest,
handleSetSidebarFilter,
handleSetActiveWorkspace,
handleSetActiveEnvironment,
handleSetRequestGroupCollapsed,
handleMoveRequest,
handleMoveRequestGroup,
handleSetRequestPaneRef,
handleSetResponsePaneRef,
handleSetSidebarRef,
handleStartDragSidebar,
handleResetDragSidebar,
handleStartDragPane,
handleResetDragPane,
handleUpdateRequestUrl,
sidebarChildren,
} = props;
class Wrapper extends Component {
_handleImportFile = () => {
this.props.handleImportFileToWorkspace(this.props.activeWorkspace._id);
};
const realSidebarWidth = sidebarHidden ? 0 : sidebarWidth;
const gridTemplateColumns = `${realSidebarWidth}rem 0 ${paneWidth}fr 0 ${1 - paneWidth}fr`;
const handleImportFile = handleImportFileToWorkspace.bind(null, activeWorkspace._id);
const handleExportWorkspaceToFile = handleExportFile.bind(null, activeWorkspace._id);
const activeRequestId = activeRequest ? activeRequest._id : 'n/a';
_handleExportWorkspaceToFile = () => {
this.props.handleExportFile(this.props.activeWorkspace._id);
};
return (
<div id="wrapper"
key={`wrapper::${forceRefreshCounter}`}
className={classnames('wrapper', {'wrapper--vertical': settings.forceVerticalLayout})}
style={{gridTemplateColumns: gridTemplateColumns}}>
<Sidebar
ref={handleSetSidebarRef}
showEnvironmentsModal={() => showModal(WorkspaceEnvironmentsEditModal, activeWorkspace)}
showCookiesModal={() => showModal(CookiesModal, activeWorkspace)}
handleActivateRequest={r => handleSetActiveRequest(activeWorkspace._id, r._id)}
handleChangeFilter={filter => handleSetSidebarFilter(activeWorkspace._id, filter)}
handleImportFile={handleImportFile}
handleExportFile={handleExportFile}
handleSetActiveWorkspace={handleSetActiveWorkspace}
handleSetActiveEnvironment={handleSetActiveEnvironment}
moveRequest={handleMoveRequest}
moveRequestGroup={handleMoveRequestGroup}
handleSetRequestGroupCollapsed={handleSetRequestGroupCollapsed}
activeRequest={activeRequest}
activeEnvironment={activeEnvironment}
handleCreateRequest={handleCreateRequest}
handleCreateRequestGroup={handleCreateRequestGroup}
filter={sidebarFilter || ''}
hidden={sidebarHidden || false}
workspace={activeWorkspace}
children={sidebarChildren}
width={sidebarWidth}
isLoading={isLoading}
workspaces={workspaces}
environments={environments}
/>
_handleShowCookiesModal = () => {
showModal(CookiesModal, this.props.activeWorkspace);
};
<div className="drag drag--sidebar">
<div onMouseDown={e => e.preventDefault() || handleStartDragSidebar()}
onDoubleClick={handleResetDragSidebar}
_handleSetPreviewMode = (previewMode) => {
const activeRequest = this.props.activeRequest;
const activeRequestId = activeRequest ? activeRequest._id : 'n/a';
this.props.handleSetResponsePreviewMode(activeRequestId, previewMode);
};
_handleSetResponseFilter = (filter) => {
const activeRequest = this.props.activeRequest;
const activeRequestId = activeRequest ? activeRequest._id : 'n/a';
this.props.handleSetResponseFilter(activeRequestId, filter);
};
_handleSetSidebarFilter = (filter) => {
this.props.handleSetSidebarFilter(this.props.activeWorkspace._id, filter);
};
_showEnvironmentsModal = () => {
showModal(WorkspaceEnvironmentsEditModal, this.props.activeWorkspace);
};
_showCookiesModal = () => {
showModal(CookiesModal, this.props.activeWorkspace);
};
render () {
const {
isLoading,
loadStartTime,
activeWorkspace,
activeRequest,
activeEnvironment,
sidebarHidden,
sidebarFilter,
sidebarWidth,
paneWidth,
forceRefreshCounter,
workspaces,
settings,
environments,
responsePreviewMode,
responseFilter,
handleSendRequestWithEnvironment,
handleCreateRequest,
handleCreateRequestForWorkspace,
handleCreateRequestGroup,
handleExportFile,
handleActivateRequest,
handleSetActiveWorkspace,
handleSetActiveEnvironment,
handleSetRequestGroupCollapsed,
handleMoveRequest,
handleMoveRequestGroup,
handleSetRequestPaneRef,
handleSetResponsePaneRef,
handleSetSidebarRef,
handleStartDragSidebar,
handleResetDragSidebar,
handleStartDragPane,
handleResetDragPane,
handleUpdateRequestUrl,
sidebarChildren,
} = this.props;
const realSidebarWidth = sidebarHidden ? 0 : sidebarWidth;
const gridTemplateColumns = `${realSidebarWidth}rem 0 ${paneWidth}fr 0 ${1 - paneWidth}fr`;
return (
<div id="wrapper"
key={`wrapper::${forceRefreshCounter}`}
className={classnames('wrapper', {'wrapper--vertical': settings.forceVerticalLayout})}
style={{gridTemplateColumns: gridTemplateColumns}}>
<Sidebar
ref={handleSetSidebarRef}
showEnvironmentsModal={this._showEnvironmentsModal}
showCookiesModal={this._showCookiesModal}
handleActivateRequest={handleActivateRequest}
handleChangeFilter={this._handleSetSidebarFilter}
handleImportFile={this._handleImportFile}
handleExportFile={handleExportFile}
handleSetActiveWorkspace={handleSetActiveWorkspace}
handleSetActiveEnvironment={handleSetActiveEnvironment}
moveRequest={handleMoveRequest}
moveRequestGroup={handleMoveRequestGroup}
handleSetRequestGroupCollapsed={handleSetRequestGroupCollapsed}
activeRequest={activeRequest}
activeEnvironment={activeEnvironment}
handleCreateRequest={handleCreateRequest}
handleCreateRequestGroup={handleCreateRequestGroup}
filter={sidebarFilter || ''}
hidden={sidebarHidden || false}
workspace={activeWorkspace}
children={sidebarChildren}
width={sidebarWidth}
isLoading={isLoading}
workspaces={workspaces}
environments={environments}
/>
<div className="drag drag--sidebar">
<div onDoubleClick={handleResetDragSidebar} onMouseDown={e => {
e.preventDefault();
handleStartDragSidebar();
}}></div>
</div>
<RequestPane
ref={handleSetRequestPaneRef}
handleImportFile={this._handleImportFile}
request={activeRequest}
showPasswords={settings.showPasswords}
useBulkHeaderEditor={settings.useBulkHeaderEditor}
editorFontSize={settings.editorFontSize}
editorLineWrapping={settings.editorLineWrapping}
environmentId={activeEnvironment ? activeEnvironment._id : 'n/a'}
handleCreateRequest={handleCreateRequestForWorkspace}
updateRequestBody={body => models.request.update(activeRequest, {body})}
updateRequestUrl={url => handleUpdateRequestUrl(activeRequest, url)}
updateRequestMethod={method => models.request.update(activeRequest, {method})}
updateRequestParameters={parameters => models.request.update(activeRequest, {parameters})}
updateRequestAuthentication={authentication => models.request.update(activeRequest, {authentication})}
updateRequestHeaders={headers => models.request.update(activeRequest, {headers})}
updateRequestMimeType={mimeType => updateMimeType(activeRequest, mimeType)}
updateSettingsShowPasswords={showPasswords => models.settings.update(settings, {showPasswords})}
updateSettingsUseBulkHeaderEditor={useBulkHeaderEditor => models.settings.update(settings, {useBulkHeaderEditor})}
handleSend={handleSendRequestWithEnvironment.bind(
null,
activeRequest ? activeRequest._id : 'n/a',
activeEnvironment ? activeEnvironment._id : 'n/a'
)}
/>
<div className="drag drag--pane">
<div onMouseDown={handleStartDragPane} onDoubleClick={handleResetDragPane}></div>
</div>
<ResponsePane
ref={handleSetResponsePaneRef}
request={activeRequest}
editorFontSize={settings.editorFontSize}
editorLineWrapping={settings.editorLineWrapping}
previewMode={responsePreviewMode}
filter={responseFilter}
loadStartTime={loadStartTime}
showCookiesModal={this._handleShowCookiesModal}
handleSetPreviewMode={this._handleSetPreviewMode}
handleSetFilter={this._handleSetResponseFilter}
/>
<AlertModal ref={registerModal}/>
<CookiesModal ref={registerModal}/>
<ChangelogModal ref={registerModal}/>
<SyncLogsModal ref={registerModal}/>
<LoginModal ref={registerModal}/>
<PromptModal ref={registerModal}/>
<SignupModal ref={registerModal}/>
<PaymentModal ref={registerModal}/>
<PaymentNotificationModal ref={registerModal}/>
<EnvironmentEditModal
ref={registerModal}
onChange={models.requestGroup.update}
/>
<GenerateCodeModal
ref={registerModal}
environmentId={activeEnvironment ? activeEnvironment._id : 'n/a'}
/>
<SettingsModal
ref={registerModal}
handleExportWorkspaceToFile={this._handleExportWorkspaceToFile}
handleExportAllToFile={handleExportFile}
handleImportFile={this._handleImportFile}
settings={settings}
/>
<RequestSwitcherModal
ref={registerModal}
workspaceId={activeWorkspace._id}
activeRequestParentId={activeRequest ? activeRequest.parentId : activeWorkspace._id}
activateRequest={handleActivateRequest}
handleSetActiveWorkspace={handleSetActiveWorkspace}
/>
<WorkspaceEnvironmentsEditModal
ref={registerModal}
onChange={models.workspace.update}/>
</div>
<RequestPane
ref={handleSetRequestPaneRef}
handleImportFile={handleImportFile}
request={activeRequest}
showPasswords={settings.showPasswords}
useBulkHeaderEditor={settings.useBulkHeaderEditor}
editorFontSize={settings.editorFontSize}
editorLineWrapping={settings.editorLineWrapping}
environmentId={activeEnvironment ? activeEnvironment._id : 'n/a'}
handleCreateRequest={handleCreateRequest.bind(null, activeRequest ? activeRequest.parentId : activeWorkspace._id)}
updateRequestBody={body => models.request.update(activeRequest, {body})}
updateRequestUrl={url => handleUpdateRequestUrl(activeRequest, url)}
updateRequestMethod={method => models.request.update(activeRequest, {method})}
updateRequestParameters={parameters => models.request.update(activeRequest, {parameters})}
updateRequestAuthentication={authentication => models.request.update(activeRequest, {authentication})}
updateRequestHeaders={headers => models.request.update(activeRequest, {headers})}
updateRequestMimeType={mimeType => updateMimeType(activeRequest, mimeType)}
updateSettingsShowPasswords={showPasswords => models.settings.update(settings, {showPasswords})}
updateSettingsUseBulkHeaderEditor={useBulkHeaderEditor => models.settings.update(settings, {useBulkHeaderEditor})}
handleSend={handleSendRequestWithEnvironment.bind(
null,
activeRequest ? activeRequest._id : 'n/a',
activeEnvironment ? activeEnvironment._id : 'n/a'
)}
/>
<div className="drag drag--pane">
<div onMouseDown={handleStartDragPane}
onDoubleClick={handleResetDragPane}></div>
</div>
<ResponsePane
ref={handleSetResponsePaneRef}
request={activeRequest}
editorFontSize={settings.editorFontSize}
editorLineWrapping={settings.editorLineWrapping}
previewMode={responsePreviewMode}
filter={responseFilter}
loadStartTime={loadStartTime}
showCookiesModal={() => showModal(CookiesModal, activeWorkspace)}
handleSetPreviewMode={handleSetResponsePreviewMode.bind(null, activeRequestId)}
handleSetFilter={handleSetResponseFilter.bind(null, activeRequestId)}
/>
<PromptModal ref={m => registerModal(m)}/>
<AlertModal ref={m => registerModal(m)}/>
<ChangelogModal ref={m => registerModal(m)}/>
<SyncLogsModal ref={m => registerModal(m)}/>
<LoginModal ref={m => registerModal(m)}/>
<SignupModal ref={m => registerModal(m)}/>
<PaymentModal ref={m => registerModal(m)}/>
<PaymentNotificationModal ref={m => registerModal(m)}/>
<GenerateCodeModal
ref={m => registerModal(m)}
environmentId={activeEnvironment ? activeEnvironment._id : 'n/a'}
/>
<SettingsModal
ref={m => registerModal(m)}
handleExportWorkspaceToFile={handleExportWorkspaceToFile}
handleExportAllToFile={handleExportFile}
handleImportFile={handleImportFile}
settings={settings}
/>
<RequestSwitcherModal
ref={m => registerModal(m)}
workspaceId={activeWorkspace._id}
activeRequestParentId={activeRequest ? activeRequest.parentId : activeWorkspace._id}
activateRequest={r => handleSetActiveRequest(activeWorkspace._id, r._id)}
handleSetActiveWorkspace={handleSetActiveWorkspace}
/>
<EnvironmentEditModal
ref={m => registerModal(m)}
onChange={rg => models.requestGroup.update(rg)}/>
<WorkspaceEnvironmentsEditModal
ref={m => registerModal(m)}
onChange={w => models.workspace.update(w)}/>
<CookiesModal ref={m => registerModal(m)}/>
</div>
)
};
)
}
}
Wrapper.propTypes = {
// Helper Functions
handleSetActiveRequest: PropTypes.func.isRequired,
handleActivateRequest: PropTypes.func.isRequired,
handleSetSidebarFilter: PropTypes.func.isRequired,
handleSetSidebarHidden: PropTypes.func.isRequired,
handleSetSidebarWidth: PropTypes.func.isRequired,
@ -204,6 +238,7 @@ Wrapper.propTypes = {
handleMoveRequestGroup: PropTypes.func.isRequired,
handleCreateRequest: PropTypes.func.isRequired,
handleCreateRequestGroup: PropTypes.func.isRequired,
handleCreateRequestForWorkspace: PropTypes.func.isRequired,
handleSetRequestPaneRef: PropTypes.func.isRequired,
handleSetResponsePaneRef: PropTypes.func.isRequired,
handleSetResponsePreviewMode: PropTypes.func.isRequired,

View File

@ -22,7 +22,7 @@ class RequestGroupActionsDropdown extends Component {
}
async _requestCreate () {
this.props.handleCreateRequest();
this.props.handleCreateRequest(this.props.requestGroup._id);
trackEvent('Request', 'Create', 'Folder Action');
}
@ -33,7 +33,7 @@ class RequestGroupActionsDropdown extends Component {
}
async _requestGroupCreate () {
this.props.handleCreateRequestGroup();
this.props.handleCreateRequestGroup(this.props.requestGroup._id);
trackEvent('Folder', 'Create', 'Folder Action');
}
@ -63,7 +63,7 @@ class RequestGroupActionsDropdown extends Component {
onClick={e => showModal(EnvironmentEditModal, requestGroup)}>
<i className="fa fa-code"></i> Environment
</DropdownItem>
<DropdownItem buttonClass={PromptButton} addIcon={true} onClick={e => {
<DropdownItem buttonClass={PromptButton} addIcon={true} onClick={() => {
models.requestGroup.remove(requestGroup);
trackEvent('Folder', 'Delete', 'Folder Action');
}}>

View File

@ -39,19 +39,19 @@ class GenerateCodeModal extends Component {
try {
target = JSON.parse(localStorage.getItem('insomnia::generateCode::target'));
} catch (e) {
target = DEFAULT_TARGET;
}
try {
client = JSON.parse(localStorage.getItem('insomnia::generateCode::client'));
} catch (e) {
client = DEFAULT_CLIENT;
}
console.log(client, target);
this.state = {
cmd: '',
request: null,
target: target,
client: client,
target: target || DEFAULT_TARGET,
client: client || DEFAULT_CLIENT,
};
}
@ -117,7 +117,7 @@ class GenerateCodeModal extends Component {
<div className="pad">
<Dropdown outline={true}>
<DropdownButton className="btn btn--super-compact btn--outlined">
{target.title}
{target ? target.title : 'n/a'}
<i className="fa fa-caret-down"></i>
</DropdownButton>
{targets.map(target => (
@ -129,7 +129,7 @@ class GenerateCodeModal extends Component {
&nbsp;&nbsp;
<Dropdown outline={true}>
<DropdownButton className="btn btn--super-compact btn--outlined">
{client.title}
{client ? client.title : 'n/a'}
<i className="fa fa-caret-down"></i>
</DropdownButton>
{clients.map(client => (

View File

@ -81,7 +81,7 @@ class RequestSwitcherModal extends Component {
return;
}
this.props.activateRequest(request);
this.props.activateRequest(request._id);
this.modal.hide();
}

View File

@ -175,10 +175,9 @@ class SignupModal extends Component {
inner = [
<ModalHeader key="header">Account Created</ModalHeader>,
<ModalBody key="body" className="pad">
<h1>Please verify your account</h1>
<h1>Thanks for signing up!</h1>
<p>
A verification email has been sent to your email address. Once
you have received it, you may login.
A verification email has been sent to your email address.
</p>
</ModalBody>,
<ModalFooter key="footer">

View File

@ -1,125 +1,29 @@
import React, {Component, PropTypes} from 'react';
import classnames from 'classnames';
import EnvironmentsDropdown from '../dropdowns/EnvironmentsDropdown';
import SidebarRequestRow from './SidebarRequestRow';
import SidebarRequestGroupRow from './SidebarRequestGroupRow';
import SidebarFilter from './SidebarFilter';
import SidebarChildren from './SidebarChildren';
import SyncButton from '../dropdowns/SyncDropdown';
import WorkspaceDropdown from '../dropdowns/WorkspaceDropdown';
import {
SIDEBAR_SKINNY_REMS,
COLLAPSE_SIDEBAR_REMS
} from '../../../common/constants';
import {SIDEBAR_SKINNY_REMS, COLLAPSE_SIDEBAR_REMS} from '../../../common/constants';
class Sidebar extends Component {
_filterChildren (filter, children, extra = null) {
filter = filter || '';
return children.filter(child => {
if (child.doc.type !== 'Request') {
return true;
}
_handleChangeEnvironment = (id) => {
const {workspace, handleSetActiveEnvironment} = this.props;
handleSetActiveEnvironment(workspace._id, id);
};
const request = child.doc;
_handleCreateRequestInWorkspace = () => {
const {workspace, handleCreateRequest} = this.props;
handleCreateRequest(workspace._id);
};
const otherMatches = extra || '';
const toMatch = `${request.method}${request.name}${otherMatches}`.toLowerCase();
const matchTokens = filter.toLowerCase().split(' ');
for (let i = 0; i < matchTokens.length; i++) {
let token = `${matchTokens[i]}`;
if (toMatch.indexOf(token) === -1) {
// Filter failed. Don't render children
return false;
}
}
return true;
})
}
_renderChildren (children, requestGroup) {
const {
filter,
handleCreateRequest,
handleCreateRequestGroup,
handleSetRequestGroupCollapsed,
moveRequest,
moveRequestGroup,
handleActivateRequest,
activeRequest,
workspace,
} = this.props;
const filteredChildren = this._filterChildren(
filter,
children,
requestGroup && requestGroup.name
);
const activeRequestId = activeRequest ? activeRequest._id : 'n/a';
return filteredChildren.map(child => {
if (child.doc.type === 'Request') {
return (
<SidebarRequestRow
key={child.doc._id}
moveRequest={moveRequest}
handleActivateRequest={handleActivateRequest}
requestCreate={handleCreateRequest.bind(null, workspace._id)}
isActive={child.doc._id === activeRequestId}
request={child.doc}
workspace={workspace}
/>
)
}
// We have a RequestGroup!
const requestGroup = child.doc;
function hasActiveChild (children) {
for (const c of children) {
if (c.children.length) {
return hasActiveChild(c.children);
} else if (c.doc._id === activeRequestId) {
return true;
}
}
// Didn't find anything, so return
return false;
}
const isActive = hasActiveChild(child.children);
const children = this._renderChildren(child.children, requestGroup);
// Don't render the row if there are no children while filtering
if (filter && !children.length) {
return null;
}
return (
<SidebarRequestGroupRow
handleActivateRequest={handleActivateRequest}
key={requestGroup._id}
isActive={isActive}
moveRequestGroup={moveRequestGroup}
moveRequest={moveRequest}
handleSetRequestGroupCollapsed={handleSetRequestGroupCollapsed}
isCollapsed={child.collapsed}
handleCreateRequest={handleCreateRequest.bind(null, requestGroup._id)}
handleCreateRequestGroup={handleCreateRequestGroup.bind(null, requestGroup._id)}
numChildren={child.children.length}
workspace={workspace}
requestGroup={requestGroup}
children={children}
/>
)
})
}
_handleCreateRequestGroupInWorkspace = () => {
const {workspace, handleCreateRequestGroup} = this.props;
handleCreateRequestGroup(workspace._id);
};
render () {
const {
@ -127,19 +31,23 @@ class Sidebar extends Component {
filter,
children,
hidden,
handleCreateRequest,
handleCreateRequestGroup,
width,
workspace,
workspaces,
environments,
activeEnvironment,
handleSetActiveEnvironment,
handleSetActiveWorkspace,
handleImportFile,
handleExportFile,
handleChangeFilter,
isLoading,
handleCreateRequest,
handleCreateRequestGroup,
handleSetRequestGroupCollapsed,
moveRequest,
moveRequestGroup,
handleActivateRequest,
activeRequest,
} = this.props;
return (
@ -160,13 +68,12 @@ class Sidebar extends Component {
<div className="sidebar__menu">
<EnvironmentsDropdown
handleChangeEnvironment={id => handleSetActiveEnvironment(workspace._id, id)}
handleChangeEnvironment={this._handleChangeEnvironment}
activeEnvironment={activeEnvironment}
environments={environments}
workspace={workspace}
/>
<button className="btn btn--super-compact"
onClick={e => showCookiesModal()}>
<button className="btn btn--super-compact" onClick={showCookiesModal}>
<div className="sidebar__menu__thing">
<span>Cookies</span>
</div>
@ -174,15 +81,24 @@ class Sidebar extends Component {
</div>
<SidebarFilter
onChange={filter => handleChangeFilter(filter)}
requestCreate={handleCreateRequest.bind(null, workspace._id)}
requestGroupCreate={handleCreateRequestGroup.bind(null, workspace._id)}
filter={filter}
onChange={handleChangeFilter}
requestCreate={this._handleCreateRequestInWorkspace}
requestGroupCreate={this._handleCreateRequestGroupInWorkspace}
filter={filter || ''}
/>
<ul className="sidebar__list sidebar__list-root">
{this._renderChildren(children)}
</ul>
<SidebarChildren
children={children}
handleActivateRequest={handleActivateRequest}
handleCreateRequest={handleCreateRequest}
handleCreateRequestGroup={handleCreateRequestGroup}
handleSetRequestGroupCollapsed={handleSetRequestGroupCollapsed}
moveRequest={moveRequest}
moveRequestGroup={moveRequestGroup}
filter={filter}
workspace={workspace}
activeRequest={activeRequest}
/>
<SyncButton
className="sidebar__footer"

View File

@ -0,0 +1,155 @@
import React, {Component, PropTypes} from 'react';
import SidebarRequestRow from './SidebarRequestRow';
import SidebarRequestGroupRow from './SidebarRequestGroupRow';
class SidebarChildren extends Component {
shouldComponentUpdate (nextProps) {
for (const k of Object.keys(nextProps)) {
const curr = this.props[k];
const next = nextProps[k];
if (curr !== next) {
console.log('DIFFERENT', k, curr, next);
return true;
}
}
return false;
}
_filterChildren (filter, children, extra = null) {
filter = filter || '';
return children.filter(child => {
if (child.doc.type !== 'Request') {
return true;
}
const request = child.doc;
const otherMatches = extra || '';
const toMatch = `${request.method}${request.name}${otherMatches}`.toLowerCase();
const matchTokens = filter.toLowerCase().split(' ');
for (let i = 0; i < matchTokens.length; i++) {
let token = `${matchTokens[i]}`;
if (toMatch.indexOf(token) === -1) {
// Filter failed. Don't render children
return false;
}
}
return true;
})
}
_renderChildren (children, requestGroup) {
const {
filter,
handleCreateRequest,
handleCreateRequestGroup,
handleSetRequestGroupCollapsed,
moveRequest,
moveRequestGroup,
handleActivateRequest,
activeRequest,
workspace,
} = this.props;
const filteredChildren = this._filterChildren(
filter,
children,
requestGroup && requestGroup.name
);
const activeRequestId = activeRequest ? activeRequest._id : 'n/a';
return filteredChildren.map(child => {
if (child.doc.type === 'Request') {
return (
<SidebarRequestRow
key={child.doc._id}
moveRequest={moveRequest}
handleActivateRequest={handleActivateRequest}
requestCreate={handleCreateRequest}
isActive={child.doc._id === activeRequestId}
request={child.doc}
workspace={workspace}
/>
)
}
// We have a RequestGroup!
const requestGroup = child.doc;
function hasActiveChild (children) {
for (const c of children) {
if (c.children.length) {
return hasActiveChild(c.children);
} else if (c.doc._id === activeRequestId) {
return true;
}
}
// Didn't find anything, so return
return false;
}
const isActive = hasActiveChild(child.children);
const children = this._renderChildren(child.children, requestGroup);
// Don't render the row if there are no children while filtering
if (filter && !children.length) {
return null;
}
return (
<SidebarRequestGroupRow
handleActivateRequest={handleActivateRequest}
key={requestGroup._id}
isActive={isActive}
moveRequestGroup={moveRequestGroup}
moveRequest={moveRequest}
handleSetRequestGroupCollapsed={handleSetRequestGroupCollapsed}
isCollapsed={child.collapsed}
handleCreateRequest={handleCreateRequest}
handleCreateRequestGroup={handleCreateRequestGroup}
numChildren={child.children.length}
workspace={workspace}
requestGroup={requestGroup}
children={children}
/>
)
})
}
render () {
const {children} = this.props;
return (
<ul className="sidebar__list sidebar__list-root">
{this._renderChildren(children)}
</ul>
)
}
}
SidebarChildren.propTypes = {
// Required
handleActivateRequest: PropTypes.func.isRequired,
handleCreateRequest: PropTypes.func.isRequired,
handleCreateRequestGroup: PropTypes.func.isRequired,
handleSetRequestGroupCollapsed: PropTypes.func.isRequired,
moveRequest: PropTypes.func.isRequired,
moveRequestGroup: PropTypes.func.isRequired,
children: PropTypes.arrayOf(PropTypes.object).isRequired,
filter: PropTypes.string.isRequired,
workspace: PropTypes.object.isRequired,
// Optional
activeRequest: PropTypes.object,
};
export default SidebarChildren;

View File

@ -5,7 +5,9 @@ import {trackEvent} from '../../../analytics/index';
class SidebarFilter extends Component {
_onChange (value) {
_handleOnChange = (e) => {
const value = e.target.value;
clearTimeout(this._triggerTimeout);
this._triggerTimeout = setTimeout(() => {
this.props.onChange(value);
@ -16,36 +18,38 @@ class SidebarFilter extends Component {
this._analyticsTimeout = setTimeout(() => {
trackEvent('Sidebar', 'Filter', value ? 'Change' : 'Clear');
}, 2000);
}
};
_handleRequestGroupCreate = () => {
this.props.requestGroupCreate();
trackEvent('Folder', 'Create', 'Sidebar Filter');
};
_handleRequestCreate = () => {
this.props.requestCreate();
trackEvent('Request', 'Create', 'Sidebar Filter');
};
render () {
const {filter, requestCreate, requestGroupCreate} = this.props;
return (
<div className="sidebar__filter">
<div className="form-control form-control--outlined">
<input
type="text"
placeholder="Filter"
defaultValue={filter}
onChange={e => this._onChange(e.target.value)}
defaultValue={this.props.filter}
onChange={this._handleOnChange}
/>
</div>
<Dropdown right={true}>
<DropdownButton className="btn btn--compact">
<i className="fa fa-plus-circle"></i>
</DropdownButton>
<DropdownItem onClick={e => {
requestCreate();
trackEvent('Request', 'Create', 'Sidebar Filter');
}}>
<DropdownItem onClick={this._handleRequestCreate}>
<i className="fa fa-plus-circle"></i> New Request
<DropdownHint char="N"></DropdownHint>
</DropdownItem>
<DropdownItem onClick={e => {
requestGroupCreate();
trackEvent('Folder', 'Create', 'Sidebar Filter');
}}>
<DropdownItem onClick={this._handleRequestGroupCreate}>
<i className="fa fa-folder"></i> New Folder
</DropdownItem>
</Dropdown>
@ -60,9 +64,7 @@ SidebarFilter.propTypes = {
onChange: PropTypes.func.isRequired,
requestCreate: PropTypes.func.isRequired,
requestGroupCreate: PropTypes.func.isRequired,
// Optional
filter: PropTypes.string
filter: PropTypes.string.isRequired
};
export default SidebarFilter;

View File

@ -1,8 +1,7 @@
import React, {PropTypes, Component} from 'react';
import ReactDOM from 'react-dom';
import {DragSource, DropTarget} from 'react-dnd'
import {DragSource, DropTarget} from 'react-dnd';
import classnames from 'classnames';
import RequestGroupActionsDropdown from '../dropdowns/RequestGroupActionsDropdown';
import SidebarRequestRow from './SidebarRequestRow';
import {trackEvent} from '../../../analytics/index';
@ -33,7 +32,6 @@ class SidebarRequestGroupRow extends Component {
requestGroup,
isCollapsed,
isActive,
handleActivateRequest,
handleCreateRequest,
handleCreateRequestGroup,
isDragging,
@ -67,7 +65,6 @@ class SidebarRequestGroupRow extends Component {
<div className="sidebar__actions">
<RequestGroupActionsDropdown
handleActivateRequest={handleActivateRequest}
handleCreateRequest={handleCreateRequest}
handleCreateRequestGroup={handleCreateRequestGroup}
workspace={workspace}

View File

@ -14,7 +14,8 @@ class SidebarRequestRow extends Component {
state = {dragDirection: 0};
_handleRequestCreateFromEmpty = () => {
this.props.requestCreate();
const parentId = this.props.requestGroup._id;
this.props.requestCreate(parentId);
trackEvent('Request', 'Create', 'Empty Folder');
};
@ -25,7 +26,7 @@ class SidebarRequestRow extends Component {
return;
}
handleActivateRequest(request);
handleActivateRequest(request._id);
trackEvent('Request', 'Activate', 'Sidebar');
};

View File

@ -24,6 +24,7 @@ import * as requestGroupMetaActions from '../redux/modules/requestGroupMeta';
import * as db from '../../common/database';
import * as models from '../../models';
import {trackEvent, trackLegacyEvent} from '../../analytics';
import {selectEntitiesLists, selectActiveWorkspace, selectSidebarChildren} from '../redux/selectors';
class App extends Component {
@ -130,6 +131,15 @@ class App extends Component {
handleSetActiveRequest(activeWorkspace._id, request._id);
};
_requestCreateForWorkspace = () => {
this._requestCreate(this.props.activeWorkspace._id);
};
_handleActivateRequest = async (requestId) => {
const {activeWorkspace, handleSetActiveRequest} = this.props;
handleSetActiveRequest(activeWorkspace._id, requestId);
};
_handleUrlChange = async (request, url) => {
// Allow user to paste any import file into the url. If it results in
// only one item, it will overwrite the current request.
@ -317,6 +327,8 @@ class App extends Component {
<div className="app">
<Wrapper
key={this.state.forceRefreshCounter}
handleCreateRequestForWorkspace={this._requestCreateForWorkspace}
handleActivateRequest={this._handleActivateRequest}
handleSetRequestPaneRef={this._setRequestPaneRef}
handleSetResponsePaneRef={this._setResponsePaneRef}
handleSetSidebarRef={this._setSidebarRef}
@ -335,7 +347,7 @@ class App extends Component {
}
}
function mapStateToProps (state) {
function mapStateToProps (state, props) {
const {
entities,
global,
@ -364,15 +376,18 @@ function mapStateToProps (state) {
} = 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]];
const entitiesLists = selectEntitiesLists(state, props);
const {
workspaces,
environments,
requests,
requestGroups
} = entitiesLists;
const settings = entitiesLists.settings[0];
// Workspace stuff
const activeWorkspace = entities.workspaces[global.activeWorkspaceId] || workspaces[0];
const activeWorkspace = selectActiveWorkspace(state, props);
const activeWorkspaceId = activeWorkspace._id;
const sidebarHidden = !!sidebarHiddens[activeWorkspaceId];
const sidebarFilter = sidebarFilters[activeWorkspaceId] || '';
@ -391,11 +406,7 @@ function mapStateToProps (state) {
// Find other meta things
const loadStartTime = loadingRequestIds[activeRequestId] || -1;
const sidebarChildren = _generateSidebarTree(
activeWorkspace._id,
requests.concat(requestGroups),
requestGroupMeta.collapsed,
);
const sidebarChildren = selectSidebarChildren(state, props);
return Object.assign({}, state, {
settings,
@ -450,33 +461,6 @@ function mapDispatchToProps (dispatch) {
};
}
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;
}
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;
}
}
async function _moveRequestGroup (requestGroupToMove, requestGroupToTarget, targetOffset) {
// Oh God, this function is awful...

View File

@ -6,8 +6,8 @@ export default function configureStore () {
const middleware = [thunkMiddleware];
if (__DEV__) {
// const createLogger = require('redux-logger');
// middleware.push(createLogger({collapsed: true}));
const createLogger = require('redux-logger');
middleware.push(createLogger({collapsed: true}));
}
const store = createStore(reducer, applyMiddleware(...middleware));

69
app/ui/redux/selectors.js Normal file
View File

@ -0,0 +1,69 @@
import {createSelector} from 'reselect';
import * as models from '../../models/index';
// ~~~~~~~~~ //
// Selectors //
// ~~~~~~~~~ //
export const selectEntitiesLists = createSelector(
state => state.entities,
entities => {
const entitiesLists = {};
for (const k of Object.keys(entities)) {
const entityMap = entities[k];
entitiesLists[k] = Object.keys(entityMap).map(id => entityMap[id]);
}
return entitiesLists;
}
);
export const selectActiveWorkspace = createSelector(
state => selectEntitiesLists(state).workspaces,
state => state.entities,
state => state.global.activeWorkspaceId,
(workspaces, entities, activeWorkspaceId) => {
return entities.workspaces[activeWorkspaceId] || workspaces[0];
}
);
export const selectRequestsAndRequestGroups = createSelector(
selectEntitiesLists,
entities => [...entities.requests, ...entities.requestGroups]
);
export const selectSidebarChildren = createSelector(
state => state.requestGroupMeta.collapsed,
selectRequestsAndRequestGroups,
selectActiveWorkspace,
(collapsed, docs, activeWorkspace) => {
function next (parentId) {
const children = docs
.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;
}
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: next(c._id,),
collapsed: !!collapsed[c._id],
}));
} else {
return children;
}
}
return next(activeWorkspace._id, false);
}
);

View File

@ -126,6 +126,7 @@
"redux-logger": "^2.6.1",
"redux-thunk": "^2.0.1",
"request": "^2.74.0",
"reselect": "^2.5.4",
"sjcl": "^1.0.6",
"srp": "git@github.com:getinsomnia/srp-js.git#6ebd8c3acfbcf69645e4c6e6f8f6b55138ff22c3",
"tough-cookie": "^2.3.1",