insomnia/packages/insomnia-app/app/ui/components/modals/request-settings-modal.js
2018-12-12 12:36:11 -05:00

371 lines
11 KiB
JavaScript

// @flow
import * as React from 'react';
import autobind from 'autobind-decorator';
import Modal from '../base/modal';
import ModalBody from '../base/modal-body';
import ModalHeader from '../base/modal-header';
import HelpTooltip from '../help-tooltip';
import * as models from '../../../models';
import DebouncedInput from '../base/debounced-input';
import MarkdownEditor from '../markdown-editor';
import * as db from '../../../common/database';
import type { Workspace } from '../../../models/workspace';
import type { Request } from '../../../models/request';
type Props = {
editorFontSize: number,
editorIndentSize: number,
editorKeyMap: string,
editorLineWrapping: boolean,
nunjucksPowerUserMode: boolean,
isVariableUncovered: boolean,
handleRender: Function,
handleGetRenderContext: Function,
workspaces: Array<Workspace>,
};
type State = {
request: Request | null,
showDescription: boolean,
defaultPreviewMode: boolean,
activeWorkspaceIdToCopyTo: string | null,
workspace: Workspace | null,
justCopied: boolean,
justMoved: boolean,
};
@autobind
class RequestSettingsModal extends React.PureComponent<Props, State> {
modal: ?Modal;
_editor: ?MarkdownEditor;
constructor(props: Props) {
super(props);
this.state = {
request: null,
showDescription: false,
defaultPreviewMode: false,
activeWorkspaceIdToCopyTo: null,
workspace: null,
workspaces: [],
justCopied: false,
justMoved: false,
};
}
_setModalRef(n: ?Modal) {
this.modal = n;
}
_setEditorRef(n: ?MarkdownEditor) {
this._editor = n;
}
async _updateRequestSettingBoolean(e: SyntheticEvent<HTMLInputElement>) {
if (!this.state.request) {
// Should never happen
return;
}
const value = e.currentTarget.checked;
const setting = e.currentTarget.name;
const request = await models.request.update(this.state.request, {
[setting]: value,
});
this.setState({ request });
}
async _handleNameChange(name: string) {
if (!this.state.request) {
return;
}
const request = await models.request.update(this.state.request, { name });
this.setState({ request });
}
async _handleDescriptionChange(description: string) {
if (!this.state.request) {
return;
}
const request = await models.request.update(this.state.request, {
description,
});
this.setState({ request, defaultPreviewMode: false });
}
_handleAddDescription() {
this.setState({ showDescription: true });
}
_handleUpdateMoveCopyWorkspace(e: SyntheticEvent<HTMLSelectElement>) {
const { value } = e.currentTarget;
const workspaceId = value === '__NULL__' ? null : value;
this.setState({ activeWorkspaceIdToCopyTo: workspaceId });
}
async _handleMoveToWorkspace() {
const { activeWorkspaceIdToCopyTo, request } = this.state;
if (!request) {
return;
}
const workspace = await models.workspace.getById(activeWorkspaceIdToCopyTo || 'n/a');
if (!workspace) {
return;
}
await models.request.update(request, {
sortKey: -1e9, // Move to top of sort order
parentId: activeWorkspaceIdToCopyTo,
});
this.setState({ justMoved: true });
setTimeout(() => {
this.setState({ justMoved: false });
}, 2000);
}
async _handleCopyToWorkspace() {
const { activeWorkspaceIdToCopyTo, request } = this.state;
if (!request) {
return;
}
const workspace = await models.workspace.getById(activeWorkspaceIdToCopyTo || 'n/a');
if (!workspace) {
return;
}
const newRequest = await models.request.duplicate(request);
await models.request.update(newRequest, {
sortKey: -1e9, // Move to top of sort order
name: request.name, // Because duplicate will add (Copy) suffix
parentId: activeWorkspaceIdToCopyTo,
});
this.setState({ justCopied: true });
setTimeout(() => {
this.setState({ justCopied: false });
}, 2000);
}
async show({ request, forceEditMode }: { request: Request, forceEditMode: boolean }) {
const { workspaces } = this.props;
const hasDescription = !!request.description;
// Find workspaces for use with moving workspace
const ancestors = await db.withAncestors(request);
const doc = ancestors.find(doc => doc.type === models.workspace.type);
const workspaceId = doc ? doc._id : 'should-never-happen';
const workspace = workspaces.find(w => w._id === workspaceId);
this.setState(
{
request,
workspace: workspace,
activeWorkspaceIdToCopyTo: null,
showDescription: forceEditMode || hasDescription,
defaultPreviewMode: hasDescription && !forceEditMode,
},
() => {
this.modal && this.modal.show();
if (forceEditMode) {
setTimeout(() => {
this._editor && this._editor.focus();
}, 400);
}
},
);
}
hide() {
this.modal && this.modal.hide();
}
renderCheckboxInput(setting: string) {
const { request } = this.state;
if (!request) {
return;
}
return (
<input
type="checkbox"
name={setting}
checked={request[setting]}
onChange={this._updateRequestSettingBoolean}
/>
);
}
renderModalBody(request: Request) {
const {
editorLineWrapping,
editorFontSize,
editorIndentSize,
editorKeyMap,
handleRender,
handleGetRenderContext,
nunjucksPowerUserMode,
isVariableUncovered,
workspaces,
} = this.props;
const {
showDescription,
defaultPreviewMode,
activeWorkspaceIdToCopyTo,
justMoved,
justCopied,
workspace,
} = this.state;
return (
<div>
<div className="form-control form-control--outlined">
<label>
Name{' '}
<span className="txt-sm faint italic">(also rename by double-clicking in sidebar)</span>
<DebouncedInput
delay={500}
type="text"
placeholder="My Request"
defaultValue={request.name}
onChange={this._handleNameChange}
/>
</label>
</div>
{showDescription ? (
<MarkdownEditor
ref={this._setEditorRef}
className="margin-top"
defaultPreviewMode={defaultPreviewMode}
fontSize={editorFontSize}
indentSize={editorIndentSize}
keyMap={editorKeyMap}
placeholder="Write a description"
lineWrapping={editorLineWrapping}
handleRender={handleRender}
handleGetRenderContext={handleGetRenderContext}
nunjucksPowerUserMode={nunjucksPowerUserMode}
isVariableUncovered={isVariableUncovered}
defaultValue={request.description}
onChange={this._handleDescriptionChange}
/>
) : (
<button
onClick={this._handleAddDescription}
className="btn btn--outlined btn--super-duper-compact">
Add Description
</button>
)}
<div className="pad-top">
<div className="form-control form-control--thin">
<label>
Send cookies automatically
{this.renderCheckboxInput('settingSendCookies')}
</label>
</div>
<div className="form-control form-control--thin">
<label>
Store cookies automatically
{this.renderCheckboxInput('settingStoreCookies')}
</label>
</div>
<div className="form-control form-control--thin">
<label>
Automatically encode special characters in URL
{this.renderCheckboxInput('settingEncodeUrl')}
<HelpTooltip position="top" className="space-left">
Automatically encode special characters at send time (does not apply to query
parameters editor)
</HelpTooltip>
</label>
</div>
<div className="form-control form-control--thin">
<label>
Skip rendering of request body
{this.renderCheckboxInput('settingDisableRenderRequestBody')}
<HelpTooltip position="top" className="space-left">
Disable rendering of environment variables and tags for the request body
</HelpTooltip>
</label>
</div>
<div className="form-control form-control--thin">
<label>
Rebuild path dot sequences
<HelpTooltip position="top" className="space-left">
This instructs libcurl to squash sequences of "/../" or "/./" that may exist in the
URL's path part and that is supposed to be removed according to RFC 3986 section
5.2.4
</HelpTooltip>
{this.renderCheckboxInput('settingRebuildPath')}
</label>
</div>
<hr />
<div className="form-row">
<div className="form-control form-control--outlined">
<label>
Move/Copy to Workspace
<HelpTooltip position="top" className="space-left">
Copy or move the current request to a new workspace. It will be placed at the root
of the new workspace's folder structure.
</HelpTooltip>
<select
value={activeWorkspaceIdToCopyTo || '__NULL__'}
onChange={this._handleUpdateMoveCopyWorkspace}>
<option value="__NULL__">-- Select Workspace --</option>
{workspaces.map(w => {
if (workspace && workspace._id === w._id) {
return null;
}
return (
<option key={w._id} value={w._id}>
{w.name}
</option>
);
})}
</select>
</label>
</div>
<div className="form-control form-control--no-label width-auto">
<button
disabled={justCopied || !activeWorkspaceIdToCopyTo}
className="btn btn--clicky"
onClick={this._handleCopyToWorkspace}>
{justCopied ? 'Copied!' : 'Copy'}
</button>
</div>
<div className="form-control form-control--no-label width-auto">
<button
disabled={justMoved || !activeWorkspaceIdToCopyTo}
className="btn btn--clicky"
onClick={this._handleMoveToWorkspace}>
{justMoved ? 'Moved!' : 'Move'}
</button>
</div>
</div>
</div>
</div>
);
}
render() {
const { request } = this.state;
return (
<Modal ref={this._setModalRef} freshState>
<ModalHeader>
Request Settings{' '}
<span className="txt-sm selectable faint monospace">{request ? request._id : ''}</span>
</ModalHeader>
<ModalBody className="pad">{request ? this.renderModalBody(request) : null}</ModalBody>
</Modal>
);
}
}
export default RequestSettingsModal;