More prominent copy as curl (Closes #336)

This commit is contained in:
Gregory Schier
2017-06-29 11:27:29 -07:00
parent 7f41945eae
commit 8168610d8d
10 changed files with 61 additions and 7 deletions

View File

@@ -11,11 +11,17 @@ class CopyButton extends PureComponent {
};
}
_handleClick (e) {
async _handleClick (e) {
e.preventDefault();
e.stopPropagation();
clipboard.writeText(this.props.content);
const content = typeof this.props.content === 'string'
? this.props.content
: await this.props.content();
if (content) {
clipboard.writeText(content);
}
this.setState({showConfirmation: true});
@@ -53,7 +59,10 @@ class CopyButton extends PureComponent {
CopyButton.propTypes = {
// Required
content: PropTypes.string.isRequired,
content: PropTypes.oneOfType([
PropTypes.string.isRequired,
PropTypes.func.isRequired
]).isRequired,
// Optional
children: PropTypes.node,

View File

@@ -14,7 +14,10 @@ class Dropdown extends PureComponent {
this.state = {
open: false,
dropUp: false,
focused: false
focused: false,
// Use this to force new menu every time dropdown opens
uniquenessKey: 0
};
}
@@ -158,7 +161,7 @@ class Dropdown extends PureComponent {
const dropdownTop = this._node.getBoundingClientRect().top;
const dropUp = dropdownTop > bodyHeight - 200;
this.setState({open: true, dropUp});
this.setState({open: true, dropUp, uniquenessKey: this.state.uniquenessKey + 1});
this.props.onOpen && this.props.onOpen();
}
@@ -172,7 +175,7 @@ class Dropdown extends PureComponent {
render () {
const {right, outline, wide, className, style, children} = this.props;
const {dropUp, open} = this.state;
const {dropUp, open, uniquenessKey} = this.state;
const classes = classnames('dropdown', className, {
'dropdown--wide': wide,
@@ -214,7 +217,7 @@ class Dropdown extends PureComponent {
dropdownButtons[0],
<div key="item" className={menuClasses} ref={this._addDropdownMenuRef}>
<div className="dropdown__backdrop theme--overlay"></div>
<ul ref={this._addDropdownListRef}>
<ul ref={this._addDropdownListRef} key={uniquenessKey}>
{dropdownItems}
</ul>
</div>

View File

@@ -5,6 +5,7 @@ import {Dropdown, DropdownButton, DropdownHint, DropdownItem} from '../base/drop
import * as models from '../../../models';
import {trackEvent} from '../../../analytics/index';
import {DropdownDivider} from '../base/dropdown/index';
import CopyButton from '../base/copy-button';
@autobind
class RequestActionsDropdown extends PureComponent {
@@ -23,6 +24,11 @@ class RequestActionsDropdown extends PureComponent {
trackEvent('Request', 'Generate Code', 'Request Action');
}
_handleCopyAsCurl () {
trackEvent('Request', 'Copy As Curl', 'Request Action');
this.props.handleCopyAsCurl(this.props.request);
}
_handleRemove () {
const {request} = this.props;
models.request.remove(request);
@@ -52,6 +58,12 @@ class RequestActionsDropdown extends PureComponent {
<DropdownItem onClick={this._handleGenerateCode}>
<i className="fa fa-code"/> Generate Code
</DropdownItem>
<DropdownItem onClick={this._handleCopyAsCurl}
buttonClass={PromptButton}
addIcon
confirmMessage="Click Again to Copy">
<i className="fa fa-copy"/> Copy as Curl
</DropdownItem>
<DropdownItem buttonClass={PromptButton} onClick={this._handleRemove} addIcon>
<i className="fa fa-trash-o"/> Delete
</DropdownItem>
@@ -70,6 +82,7 @@ class RequestActionsDropdown extends PureComponent {
RequestActionsDropdown.propTypes = {
handleDuplicateRequest: PropTypes.func.isRequired,
handleGenerateCode: PropTypes.func.isRequired,
handleCopyAsCurl: PropTypes.func.isRequired,
handleShowSettings: PropTypes.func.isRequired,
request: PropTypes.object.isRequired
};

View File

@@ -133,6 +133,9 @@ class RequestCreateModal extends PureComponent {
</form>
</ModalBody>
<ModalFooter>
<div className="margin-left italic txt-sm tall">
* Tip: paste Curl command into URL afterwards to import it
</div>
<button className="btn" onClick={this._handleSubmit}>
Create
</button>

View File

@@ -11,6 +11,7 @@ class SidebarChildren extends PureComponent {
handleDuplicateRequest,
handleDuplicateRequestGroup,
handleGenerateCode,
handleCopyAsCurl,
moveDoc,
handleActivateRequest,
activeRequest,
@@ -32,6 +33,7 @@ class SidebarChildren extends PureComponent {
handleActivateRequest={handleActivateRequest}
handleDuplicateRequest={handleDuplicateRequest}
handleGenerateCode={handleGenerateCode}
handleCopyAsCurl={handleCopyAsCurl}
requestCreate={handleCreateRequest}
isActive={child.doc._id === activeRequestId}
request={child.doc}
@@ -100,6 +102,7 @@ SidebarChildren.propTypes = {
handleDuplicateRequest: PropTypes.func.isRequired,
handleDuplicateRequestGroup: PropTypes.func.isRequired,
handleGenerateCode: PropTypes.func.isRequired,
handleCopyAsCurl: PropTypes.func.isRequired,
moveDoc: PropTypes.func.isRequired,
childObjects: PropTypes.arrayOf(PropTypes.object).isRequired,
workspace: PropTypes.object.isRequired,

View File

@@ -104,6 +104,7 @@ class SidebarRequestGroupRow extends PureComponent {
handleActivateRequest={misc.nullFn}
handleDuplicateRequest={misc.nullFn}
handleGenerateCode={misc.nullFn}
handleCopyAsCurl={misc.nullFn}
moveDoc={moveDoc}
isActive={false}
request={null}

View File

@@ -71,6 +71,7 @@ class SidebarRequestRow extends PureComponent {
const {
handleDuplicateRequest,
handleGenerateCode,
handleCopyAsCurl,
connectDragSource,
connectDropTarget,
isDragging,
@@ -125,6 +126,7 @@ class SidebarRequestRow extends PureComponent {
ref={this._setRequestActionsDropdownRef}
handleDuplicateRequest={handleDuplicateRequest}
handleGenerateCode={handleGenerateCode}
handleCopyAsCurl={handleCopyAsCurl}
handleShowSettings={this._handleShowRequestSettings}
request={request}
requestGroup={requestGroup}
@@ -148,6 +150,7 @@ SidebarRequestRow.propTypes = {
handleActivateRequest: PropTypes.func.isRequired,
handleDuplicateRequest: PropTypes.func.isRequired,
handleGenerateCode: PropTypes.func.isRequired,
handleCopyAsCurl: PropTypes.func.isRequired,
requestCreate: PropTypes.func.isRequired,
moveDoc: PropTypes.func.isRequired,

View File

@@ -45,6 +45,7 @@ class Sidebar extends PureComponent {
handleDuplicateRequest,
handleDuplicateRequestGroup,
handleGenerateCode,
handleCopyAsCurl,
handleCreateRequestGroup,
handleSetRequestGroupCollapsed,
moveDoc,
@@ -98,6 +99,7 @@ class Sidebar extends PureComponent {
handleDuplicateRequest={handleDuplicateRequest}
handleDuplicateRequestGroup={handleDuplicateRequestGroup}
handleGenerateCode={handleGenerateCode}
handleCopyAsCurl={handleCopyAsCurl}
moveDoc={moveDoc}
workspace={workspace}
activeRequest={activeRequest}
@@ -128,6 +130,7 @@ Sidebar.propTypes = {
handleDuplicateRequest: PropTypes.func.isRequired,
handleDuplicateRequestGroup: PropTypes.func.isRequired,
handleGenerateCode: PropTypes.func.isRequired,
handleCopyAsCurl: PropTypes.func.isRequired,
showEnvironmentsModal: PropTypes.func.isRequired,
showCookiesModal: PropTypes.func.isRequired,

View File

@@ -271,6 +271,7 @@ class Wrapper extends PureComponent {
handleDuplicateWorkspace,
handleGenerateCodeForActiveRequest,
handleGenerateCode,
handleCopyAsCurl,
isLoading,
loadStartTime,
paneWidth,
@@ -309,6 +310,7 @@ class Wrapper extends PureComponent {
handleSetActiveWorkspace={handleSetActiveWorkspace}
handleDuplicateRequest={handleDuplicateRequest}
handleGenerateCode={handleGenerateCode}
handleCopyAsCurl={handleCopyAsCurl}
handleDuplicateRequestGroup={handleDuplicateRequestGroup}
handleSetActiveEnvironment={handleSetActiveEnvironment}
moveDoc={handleMoveDoc}
@@ -529,6 +531,7 @@ Wrapper.propTypes = {
handleCreateRequestGroup: PropTypes.func.isRequired,
handleGenerateCodeForActiveRequest: PropTypes.func.isRequired,
handleGenerateCode: PropTypes.func.isRequired,
handleCopyAsCurl: PropTypes.func.isRequired,
handleCreateRequestForWorkspace: PropTypes.func.isRequired,
handleSetRequestPaneRef: PropTypes.func.isRequired,
handleSetResponsePaneRef: PropTypes.func.isRequired,

View File

@@ -1,8 +1,10 @@
import React, {PropTypes, PureComponent} from 'react';
import autobind from 'autobind-decorator';
import fs from 'fs';
import {clipboard} from 'electron';
import {parse as urlParse} from 'url';
import {ipcRenderer, remote} from 'electron';
import HTTPSnippet, {availableTargets} from 'httpsnippet';
import ReactDOM from 'react-dom';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
@@ -32,6 +34,7 @@ import * as path from 'path';
import * as render from '../../common/render';
import {getKeys} from '../../templating/utils';
import {showPrompt} from '../components/modals/index';
import {exportHar} from '../../common/har';
const KEY_ENTER = 13;
const KEY_COMMA = 188;
@@ -298,6 +301,15 @@ class App extends PureComponent {
showModal(GenerateCodeModal, request);
}
async _handleCopyAsCurl (request) {
const {activeEnvironment} = this.props;
const environmentId = activeEnvironment ? activeEnvironment._id : 'n/a';
const har = await exportHar(request._id, environmentId);
const snippet = new HTTPSnippet(har);
const cmd = snippet.convert('shell', 'curl');
clipboard.writeText(cmd);
}
async _updateRequestGroupMetaByParentId (requestGroupId, patch) {
const requestGroupMeta = await models.requestGroupMeta.getByParentId(requestGroupId);
if (requestGroupMeta) {
@@ -801,6 +813,7 @@ class App extends PureComponent {
handleCreateRequestGroup={this._requestGroupCreate}
handleGenerateCode={this._handleGenerateCode}
handleGenerateCodeForActiveRequest={this._handleGenerateCodeForActiveRequest}
handleCopyAsCurl={this._handleCopyAsCurl}
handleSetResponsePreviewMode={this._handleSetResponsePreviewMode}
handleSetResponseFilter={this._handleSetResponseFilter}
handleSendRequestWithEnvironment={this._handleSendRequestWithEnvironment}