2017-06-01 02:04:27 +00:00
|
|
|
import React, {PropTypes, PureComponent} from 'react';
|
2017-03-03 01:44:07 +00:00
|
|
|
import autobind from 'autobind-decorator';
|
2017-02-01 20:21:14 +00:00
|
|
|
import {remote} from 'electron';
|
2016-11-27 21:42:38 +00:00
|
|
|
import {DEBOUNCE_MILLIS, isMac} from '../../common/constants';
|
2017-06-01 02:04:27 +00:00
|
|
|
import {Dropdown, DropdownButton, DropdownDivider, DropdownHint, DropdownItem} from './base/dropdown';
|
2016-11-10 05:47:20 +00:00
|
|
|
import {trackEvent} from '../../analytics';
|
2017-06-01 02:04:27 +00:00
|
|
|
import {showPrompt} from './modals/index';
|
2017-03-08 05:52:17 +00:00
|
|
|
import MethodDropdown from './dropdowns/method-dropdown';
|
|
|
|
import PromptButton from './base/prompt-button';
|
|
|
|
import OneLineEditor from './codemirror/one-line-editor';
|
2016-11-07 20:24:38 +00:00
|
|
|
|
2017-03-03 01:44:07 +00:00
|
|
|
@autobind
|
2017-02-28 21:32:23 +00:00
|
|
|
class RequestUrlBar extends PureComponent {
|
2017-03-03 01:44:07 +00:00
|
|
|
constructor (props) {
|
|
|
|
super(props);
|
|
|
|
this.state = {
|
|
|
|
currentInterval: null,
|
|
|
|
currentTimeout: null,
|
|
|
|
downloadPath: null
|
|
|
|
};
|
2016-11-28 07:12:17 +00:00
|
|
|
|
2017-03-03 01:44:07 +00:00
|
|
|
this._urlChangeDebounceTimeout = null;
|
|
|
|
this._lastPastedText = null;
|
|
|
|
}
|
2017-02-01 20:21:14 +00:00
|
|
|
|
2017-03-03 01:44:07 +00:00
|
|
|
_setDropdownRef (n) {
|
|
|
|
this._dropdown = n;
|
|
|
|
}
|
2017-02-08 00:31:48 +00:00
|
|
|
|
2017-03-03 01:44:07 +00:00
|
|
|
_setInputRef (n) {
|
|
|
|
this._input = n;
|
|
|
|
}
|
|
|
|
|
|
|
|
_handleMetaClickSend (e) {
|
2017-02-08 00:31:48 +00:00
|
|
|
e.preventDefault();
|
|
|
|
this._dropdown.show();
|
2017-03-03 01:44:07 +00:00
|
|
|
}
|
2017-02-08 00:31:48 +00:00
|
|
|
|
2017-03-03 01:44:07 +00:00
|
|
|
_handleFormSubmit (e) {
|
2016-11-07 20:24:38 +00:00
|
|
|
e.preventDefault();
|
2016-12-21 23:37:48 +00:00
|
|
|
e.stopPropagation();
|
2017-02-01 20:21:14 +00:00
|
|
|
|
|
|
|
this._handleSend();
|
2017-03-03 01:44:07 +00:00
|
|
|
}
|
2016-11-27 21:42:38 +00:00
|
|
|
|
2017-03-03 01:44:07 +00:00
|
|
|
_handleMethodChange (method) {
|
2016-11-27 21:42:38 +00:00
|
|
|
this.props.onMethodChange(method);
|
|
|
|
trackEvent('Request', 'Method Change', method);
|
2017-03-03 01:44:07 +00:00
|
|
|
}
|
2016-11-27 21:42:38 +00:00
|
|
|
|
2017-03-03 01:44:07 +00:00
|
|
|
_handleUrlChange (url) {
|
2017-02-01 20:21:14 +00:00
|
|
|
clearTimeout(this._urlChangeDebounceTimeout);
|
2017-02-02 18:30:13 +00:00
|
|
|
this._urlChangeDebounceTimeout = setTimeout(async () => {
|
2017-03-03 01:44:07 +00:00
|
|
|
const pastedText = this._lastPastedText;
|
2017-02-02 18:30:13 +00:00
|
|
|
|
|
|
|
// If no pasted text in the queue, just fire the regular change handler
|
|
|
|
if (!pastedText) {
|
|
|
|
this.props.onUrlChange(url);
|
2017-04-04 23:06:43 +00:00
|
|
|
return;
|
2017-02-02 18:30:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Reset pasted text cache
|
|
|
|
this._lastPastedText = null;
|
|
|
|
|
|
|
|
// Attempt to import the pasted text
|
|
|
|
const importedRequest = await this.props.handleImport(pastedText);
|
|
|
|
|
|
|
|
// Update depending on whether something was imported
|
2017-04-04 23:06:43 +00:00
|
|
|
if (!importedRequest) {
|
2017-02-02 18:30:13 +00:00
|
|
|
this.props.onUrlChange(url);
|
|
|
|
}
|
2016-11-07 20:24:38 +00:00
|
|
|
}, DEBOUNCE_MILLIS);
|
2017-03-03 01:44:07 +00:00
|
|
|
}
|
2016-11-07 20:24:38 +00:00
|
|
|
|
2017-03-03 01:44:07 +00:00
|
|
|
_handleUrlPaste (e) {
|
2017-02-02 18:30:13 +00:00
|
|
|
// NOTE: We're not actually doing the import here to avoid races with onChange
|
|
|
|
this._lastPastedText = e.clipboardData.getData('text/plain');
|
2017-03-03 01:44:07 +00:00
|
|
|
}
|
2017-01-28 06:09:01 +00:00
|
|
|
|
2017-03-03 01:44:07 +00:00
|
|
|
_handleGenerateCode () {
|
2016-11-28 07:12:17 +00:00
|
|
|
this.props.handleGenerateCode();
|
|
|
|
trackEvent('Request', 'Generate Code', 'Send Action');
|
2017-03-03 01:44:07 +00:00
|
|
|
}
|
2016-11-28 07:12:17 +00:00
|
|
|
|
2017-03-03 01:44:07 +00:00
|
|
|
_handleSetDownloadLocation () {
|
2017-02-01 20:21:14 +00:00
|
|
|
const options = {
|
|
|
|
title: 'Select Download Location',
|
|
|
|
buttonLabel: 'Select',
|
2017-03-03 20:09:08 +00:00
|
|
|
properties: ['openDirectory']
|
2017-02-01 20:21:14 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
remote.dialog.showOpenDialog(options, paths => {
|
|
|
|
if (!paths || paths.length === 0) {
|
|
|
|
trackEvent('Response', 'Download Select Cancel');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.setState({downloadPath: paths[0]});
|
|
|
|
});
|
2017-03-03 01:44:07 +00:00
|
|
|
}
|
2017-02-01 20:21:14 +00:00
|
|
|
|
2017-03-03 01:44:07 +00:00
|
|
|
_handleClearDownloadLocation () {
|
2017-02-01 20:21:14 +00:00
|
|
|
this.setState({downloadPath: null});
|
2017-03-03 01:44:07 +00:00
|
|
|
}
|
2017-02-01 20:21:14 +00:00
|
|
|
|
2017-03-03 01:44:07 +00:00
|
|
|
_handleKeyDown (e) {
|
2016-11-28 07:12:17 +00:00
|
|
|
if (!this._input) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// meta+l
|
|
|
|
const metaPressed = isMac() ? e.metaKey : e.ctrlKey;
|
|
|
|
if (metaPressed && e.keyCode === 76) {
|
|
|
|
e.preventDefault();
|
|
|
|
this._input.focus();
|
2017-03-03 21:10:35 +00:00
|
|
|
this._input.selectAll();
|
2016-11-28 07:12:17 +00:00
|
|
|
}
|
2017-03-03 01:44:07 +00:00
|
|
|
}
|
2016-11-28 07:12:17 +00:00
|
|
|
|
2017-03-03 01:44:07 +00:00
|
|
|
_handleSend () {
|
2016-11-28 07:12:17 +00:00
|
|
|
// Don't stop interval because duh, it needs to keep going!
|
2017-01-28 06:09:01 +00:00
|
|
|
// XXX this._handleStopInterval(); XXX
|
2016-11-28 07:12:17 +00:00
|
|
|
|
|
|
|
this._handleStopTimeout();
|
2017-02-01 20:21:14 +00:00
|
|
|
|
|
|
|
const {downloadPath} = this.state;
|
|
|
|
if (downloadPath) {
|
|
|
|
this.props.handleSendAndDownload(downloadPath);
|
|
|
|
} else {
|
|
|
|
this.props.handleSend();
|
|
|
|
}
|
2017-03-03 01:44:07 +00:00
|
|
|
}
|
2016-11-28 07:12:17 +00:00
|
|
|
|
2017-06-01 02:04:27 +00:00
|
|
|
_handleSendAfterDelay () {
|
|
|
|
showPrompt({
|
2016-11-28 07:12:17 +00:00
|
|
|
inputType: 'decimal',
|
|
|
|
headerName: 'Send After Delay',
|
|
|
|
label: 'Delay in seconds',
|
|
|
|
defaultValue: 3,
|
2017-06-01 02:04:27 +00:00
|
|
|
submitName: 'Start',
|
|
|
|
onComplete: seconds => {
|
|
|
|
this._handleStopTimeout();
|
|
|
|
this._sendTimeout = setTimeout(this._handleSend, seconds * 1000);
|
|
|
|
this.setState({currentTimeout: seconds});
|
2016-11-28 07:12:17 +00:00
|
|
|
|
2017-06-01 02:04:27 +00:00
|
|
|
trackEvent('Request', 'Send on Delay', 'Send Action', seconds);
|
|
|
|
}
|
|
|
|
});
|
2017-03-03 20:09:08 +00:00
|
|
|
}
|
2016-11-28 07:12:17 +00:00
|
|
|
|
2017-06-01 02:04:27 +00:00
|
|
|
_handleSendOnInterval () {
|
|
|
|
showPrompt({
|
2016-11-28 07:12:17 +00:00
|
|
|
inputType: 'decimal',
|
|
|
|
headerName: 'Send on Interval',
|
|
|
|
label: 'Interval in seconds',
|
|
|
|
defaultValue: 3,
|
2017-06-01 02:04:27 +00:00
|
|
|
submitName: 'Start',
|
|
|
|
onComplete: seconds => {
|
|
|
|
this._handleStopInterval();
|
|
|
|
this._sendInterval = setInterval(this._handleSend, seconds * 1000);
|
|
|
|
this.setState({currentInterval: seconds});
|
2016-11-28 07:12:17 +00:00
|
|
|
|
2017-06-01 02:04:27 +00:00
|
|
|
trackEvent('Request', 'Send on Interval', 'Send Action', seconds);
|
|
|
|
}
|
|
|
|
});
|
2017-03-03 01:44:07 +00:00
|
|
|
}
|
2016-11-28 07:12:17 +00:00
|
|
|
|
2017-03-03 01:44:07 +00:00
|
|
|
_handleStopInterval () {
|
2016-11-28 07:12:17 +00:00
|
|
|
clearTimeout(this._sendInterval);
|
|
|
|
if (this.state.currentInterval) {
|
|
|
|
this.setState({currentInterval: null});
|
2016-12-21 23:37:48 +00:00
|
|
|
trackEvent('Request', 'Stop Send Interval');
|
2016-11-28 07:12:17 +00:00
|
|
|
}
|
2017-03-03 01:44:07 +00:00
|
|
|
}
|
2016-11-28 07:12:17 +00:00
|
|
|
|
2017-03-03 01:44:07 +00:00
|
|
|
_handleStopTimeout () {
|
2016-11-28 07:12:17 +00:00
|
|
|
clearTimeout(this._sendTimeout);
|
|
|
|
if (this.state.currentTimeout) {
|
|
|
|
this.setState({currentTimeout: null});
|
2017-02-27 22:54:56 +00:00
|
|
|
trackEvent('Request', 'Stop Send Timeout');
|
2016-11-28 07:12:17 +00:00
|
|
|
}
|
2017-03-03 20:09:08 +00:00
|
|
|
}
|
2016-11-28 07:12:17 +00:00
|
|
|
|
2017-04-10 20:07:33 +00:00
|
|
|
_handleResetTimeouts () {
|
|
|
|
this._handleStopTimeout();
|
|
|
|
this._handleStopInterval();
|
|
|
|
}
|
|
|
|
|
2017-03-03 01:44:07 +00:00
|
|
|
_handleClickSend (e) {
|
2016-12-21 23:37:48 +00:00
|
|
|
const metaPressed = isMac() ? e.metaKey : e.ctrlKey;
|
|
|
|
|
|
|
|
// If we're pressing a meta key, let the dropdown open
|
|
|
|
if (metaPressed) {
|
|
|
|
e.preventDefault(); // Don't submit the form
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we're not pressing a meta key, cancel dropdown and send the request
|
|
|
|
e.stopPropagation(); // Don't trigger the dropdown
|
|
|
|
this._handleFormSubmit(e);
|
2017-03-03 01:44:07 +00:00
|
|
|
}
|
2016-12-21 23:37:48 +00:00
|
|
|
|
2016-11-07 20:24:38 +00:00
|
|
|
componentDidMount () {
|
2016-11-28 07:12:17 +00:00
|
|
|
document.body.addEventListener('keydown', this._handleKeyDown);
|
2016-11-07 20:24:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
componentWillUnmount () {
|
2016-11-28 07:12:17 +00:00
|
|
|
document.body.removeEventListener('keydown', this._handleKeyDown);
|
|
|
|
}
|
|
|
|
|
2017-04-10 20:07:33 +00:00
|
|
|
componentWillReceiveProps (nextProps) {
|
|
|
|
if (nextProps.requestId !== this.props.requestId) {
|
|
|
|
this._handleResetTimeouts();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-28 07:12:17 +00:00
|
|
|
renderSendButton () {
|
2017-02-01 20:21:14 +00:00
|
|
|
const {currentInterval, currentTimeout, downloadPath} = this.state;
|
2016-11-28 07:12:17 +00:00
|
|
|
|
|
|
|
let cancelButton = null;
|
|
|
|
if (currentInterval) {
|
|
|
|
cancelButton = (
|
|
|
|
<button type="button"
|
|
|
|
key="cancel-interval"
|
|
|
|
className="urlbar__send-btn danger"
|
|
|
|
onClick={this._handleStopInterval}>
|
|
|
|
Stop
|
|
|
|
</button>
|
2017-03-03 20:09:08 +00:00
|
|
|
);
|
2016-11-28 07:12:17 +00:00
|
|
|
} else if (currentTimeout) {
|
|
|
|
cancelButton = (
|
|
|
|
<button type="button"
|
|
|
|
key="cancel-timeout"
|
|
|
|
className="urlbar__send-btn danger"
|
|
|
|
onClick={this._handleStopTimeout}>
|
|
|
|
Cancel
|
|
|
|
</button>
|
2017-03-03 20:09:08 +00:00
|
|
|
);
|
2016-11-28 07:12:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let sendButton;
|
|
|
|
if (!cancelButton) {
|
2016-12-21 23:37:48 +00:00
|
|
|
sendButton = (
|
2017-03-01 21:15:56 +00:00
|
|
|
<Dropdown key="dropdown" className="tall" right ref={this._setDropdownRef}>
|
2016-12-21 23:37:48 +00:00
|
|
|
<DropdownButton className="urlbar__send-btn"
|
2017-02-08 00:31:48 +00:00
|
|
|
onContextMenu={this._handleMetaClickSend}
|
2016-12-21 23:37:48 +00:00
|
|
|
onClick={this._handleClickSend}
|
|
|
|
type="submit">
|
2017-03-03 20:09:08 +00:00
|
|
|
{downloadPath ? 'Download' : 'Send'}
|
2016-11-28 07:12:17 +00:00
|
|
|
</DropdownButton>
|
2016-12-21 23:37:48 +00:00
|
|
|
<DropdownDivider>Basic</DropdownDivider>
|
2016-11-28 07:12:17 +00:00
|
|
|
<DropdownItem type="submit">
|
|
|
|
<i className="fa fa-arrow-circle-o-right"/> Send Now
|
|
|
|
<DropdownHint char="Enter"/>
|
|
|
|
</DropdownItem>
|
|
|
|
<DropdownItem onClick={this._handleGenerateCode}>
|
2016-11-29 20:55:31 +00:00
|
|
|
<i className="fa fa-code"/> Generate Client Code
|
2016-11-28 07:12:17 +00:00
|
|
|
</DropdownItem>
|
2016-12-21 23:37:48 +00:00
|
|
|
<DropdownDivider>Advanced</DropdownDivider>
|
2016-11-28 07:12:17 +00:00
|
|
|
<DropdownItem onClick={this._handleSendAfterDelay}>
|
|
|
|
<i className="fa fa-clock-o"/> Send After Delay
|
|
|
|
</DropdownItem>
|
|
|
|
<DropdownItem onClick={this._handleSendOnInterval}>
|
2016-11-30 23:11:36 +00:00
|
|
|
<i className="fa fa-repeat"/> Repeat on Interval
|
2016-11-28 07:12:17 +00:00
|
|
|
</DropdownItem>
|
2017-02-01 20:21:14 +00:00
|
|
|
{downloadPath ? (
|
2017-04-04 23:06:43 +00:00
|
|
|
<DropdownItem stayOpenAfterClick addIcon
|
|
|
|
buttonClass={PromptButton}
|
|
|
|
onClick={this._handleClearDownloadLocation}>
|
|
|
|
<i className="fa fa-stop-circle"/> Stop Auto-Download
|
|
|
|
</DropdownItem>
|
|
|
|
) : (
|
|
|
|
<DropdownItem onClick={this._handleSetDownloadLocation}>
|
|
|
|
<i className="fa fa-download"/> Download After Send
|
|
|
|
</DropdownItem>
|
|
|
|
)}
|
2016-11-28 07:12:17 +00:00
|
|
|
</Dropdown>
|
2017-03-03 20:09:08 +00:00
|
|
|
);
|
2016-11-28 07:12:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return [
|
|
|
|
cancelButton,
|
2017-03-03 20:09:08 +00:00
|
|
|
sendButton
|
|
|
|
];
|
2016-11-07 20:24:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
render () {
|
2017-03-23 22:10:42 +00:00
|
|
|
const {
|
|
|
|
url,
|
|
|
|
method,
|
|
|
|
handleRender,
|
|
|
|
handleGetRenderContext,
|
2017-04-10 20:07:33 +00:00
|
|
|
handleAutocompleteUrls,
|
|
|
|
uniquenessKey
|
2017-03-23 22:10:42 +00:00
|
|
|
} = this.props;
|
|
|
|
|
2016-11-07 20:24:38 +00:00
|
|
|
return (
|
|
|
|
<div className="urlbar">
|
2016-11-27 21:42:38 +00:00
|
|
|
<MethodDropdown onChange={this._handleMethodChange} method={method}>
|
|
|
|
{method} <i className="fa fa-caret-down"/>
|
|
|
|
</MethodDropdown>
|
|
|
|
<form onSubmit={this._handleFormSubmit}>
|
2017-02-27 21:00:13 +00:00
|
|
|
<OneLineEditor
|
2017-04-10 20:07:33 +00:00
|
|
|
key={uniquenessKey}
|
2017-02-28 21:32:23 +00:00
|
|
|
ref={this._setInputRef}
|
2017-01-28 06:09:01 +00:00
|
|
|
onPaste={this._handleUrlPaste}
|
2017-03-01 21:15:56 +00:00
|
|
|
forceEditor
|
2016-11-29 20:55:31 +00:00
|
|
|
type="text"
|
2017-02-27 21:00:13 +00:00
|
|
|
render={handleRender}
|
2017-03-23 22:10:42 +00:00
|
|
|
getAutocompleteConstants={handleAutocompleteUrls}
|
2017-03-12 00:31:23 +00:00
|
|
|
getRenderContext={handleGetRenderContext}
|
2016-11-29 20:55:31 +00:00
|
|
|
placeholder="https://api.myproduct.com/v1/users"
|
|
|
|
defaultValue={url}
|
|
|
|
onChange={this._handleUrlChange}/>
|
2016-11-28 07:12:17 +00:00
|
|
|
{this.renderSendButton()}
|
2016-11-07 20:24:38 +00:00
|
|
|
</form>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
RequestUrlBar.propTypes = {
|
2016-11-16 17:18:39 +00:00
|
|
|
handleSend: PropTypes.func.isRequired,
|
2017-02-01 20:21:14 +00:00
|
|
|
handleSendAndDownload: PropTypes.func.isRequired,
|
2017-02-27 21:00:13 +00:00
|
|
|
handleRender: PropTypes.func.isRequired,
|
2017-03-12 00:31:23 +00:00
|
|
|
handleGetRenderContext: PropTypes.func.isRequired,
|
2017-02-02 18:30:13 +00:00
|
|
|
handleImport: PropTypes.func.isRequired,
|
2017-03-23 22:10:42 +00:00
|
|
|
handleAutocompleteUrls: PropTypes.func.isRequired,
|
2016-11-07 20:24:38 +00:00
|
|
|
onUrlChange: PropTypes.func.isRequired,
|
|
|
|
onMethodChange: PropTypes.func.isRequired,
|
2016-11-28 07:12:17 +00:00
|
|
|
handleGenerateCode: PropTypes.func.isRequired,
|
2016-11-07 20:24:38 +00:00
|
|
|
url: PropTypes.string.isRequired,
|
2017-04-10 20:07:33 +00:00
|
|
|
method: PropTypes.string.isRequired,
|
|
|
|
requestId: PropTypes.string.isRequired,
|
|
|
|
uniquenessKey: PropTypes.string.isRequired
|
2016-11-07 20:24:38 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
export default RequestUrlBar;
|