Fix select all with large response (#2694)

* Refactor to keep in state info about large & huge response

This commit adds 2 properties in the ResponseViewer state:
* largeResponse which will be true when a response body weight more that LARGE_RESPONSE_MB
* hugeResponse which will be true when a response body weight more that HUGE_RESPONSE_MB

* Prevent select-all during response focus hotkey handler on large response

* Add a CopyButton in raw & source response viewer

* Revert "Add a CopyButton in raw & source response viewer"

This reverts commit e094be21

* Add a Copy raw response action in Preview dropdown

This action allows the user to copy the raw response directly in the clipboard.
It is a useful shortcut to copy huge response where select-all might freeze the application.

* fix import

* Fix build

* remove empty line

Co-authored-by: Opender Singh <opender.singh@konghq.com>
Co-authored-by: James Gatz <jamesgatzos@gmail.com>
This commit is contained in:
Julien Giovaresco 2021-09-09 03:35:01 +02:00 committed by GitHub
parent 972770731c
commit 4cbd5fa816
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 41 additions and 13 deletions

View File

@ -7,6 +7,7 @@ import { Dropdown, DropdownButton, DropdownDivider, DropdownItem } from '../base
interface Props {
download: (pretty: boolean) => any;
fullDownload: (pretty: boolean) => any;
copyToClipboard: () => any;
updatePreviewMode: Function;
previewMode: string;
showPrettifyOption?: boolean;
@ -29,6 +30,11 @@ class PreviewModeDropdown extends PureComponent<Props> {
download(false);
}
async _handleCopyRawResponse() {
const { copyToClipboard } = this.props;
copyToClipboard();
}
renderPreviewMode(mode: string) {
const { previewMode } = this.props;
return (
@ -50,6 +56,10 @@ class PreviewModeDropdown extends PureComponent<Props> {
<DropdownDivider>Preview Mode</DropdownDivider>
{PREVIEW_MODES.map(this.renderPreviewMode)}
<DropdownDivider>Actions</DropdownDivider>
<DropdownItem onClick={this._handleCopyRawResponse}>
<i className="fa fa-copy" />
Copy raw response
</DropdownItem>
<DropdownItem onClick={this._handleDownloadNormal}>
<i className="fa fa-save" />
Save Raw Response

View File

@ -1,6 +1,6 @@
import { autoBindMethodsForReact } from 'class-autobind-decorator';
import classnames from 'classnames';
import { remote } from 'electron';
import { clipboard, remote } from 'electron';
import fs from 'fs';
import { json as jsonPrettify } from 'insomnia-prettify';
import mime from 'mime-types';
@ -167,6 +167,17 @@ class ResponsePane extends PureComponent<Props> {
}
}
async _handleCopyResponseToClipboard() {
if (!this.props.response) {
return;
}
const bodyBuffer = models.response.getBodyBuffer(this.props.response);
if (bodyBuffer) {
clipboard.writeText(bodyBuffer.toString('utf8'));
}
}
_handleTabSelect(index: number, lastIndex: number) {
if (this._responseViewer != null && index === 0 && index !== lastIndex) {
// Fix for CodeMirror editor not updating its content.
@ -257,6 +268,7 @@ class ResponsePane extends PureComponent<Props> {
previewMode={previewMode}
updatePreviewMode={handleSetPreviewMode}
showPrettifyOption={response.contentType.includes('json')}
copyToClipboard={this._handleCopyResponseToClipboard}
/>
</Tab>
<Tab tabIndex="-1">

View File

@ -47,6 +47,8 @@ interface State {
blockingBecauseTooLarge: boolean;
bodyBuffer: Buffer | null;
error: string;
hugeResponse: boolean;
largeResponse: boolean;
}
@autoBindMethodsForReact(AUTOBIND_CFG)
@ -57,6 +59,8 @@ class ResponseViewer extends Component<Props, State> {
blockingBecauseTooLarge: false,
bodyBuffer: null,
error: '',
hugeResponse: false,
largeResponse: false,
};
refresh() {
@ -91,13 +95,15 @@ class ResponseViewer extends Component<Props, State> {
}
_maybeLoadResponseBody(props: Props, forceShow?: boolean) {
// Block the response if it's too large
const responseIsTooLarge = props.bytes > LARGE_RESPONSE_MB * 1024 * 1024;
const { bytes } = props;
const largeResponse = bytes > LARGE_RESPONSE_MB * 1024 * 1024;
const hugeResponse = bytes > HUGE_RESPONSE_MB * 1024 * 1024;
if (!forceShow && !alwaysShowLargeResponses && responseIsTooLarge) {
this.setState({
blockingBecauseTooLarge: true,
});
this.setState({ largeResponse, hugeResponse });
// Block the response if it's too large
if (!forceShow && !alwaysShowLargeResponses && largeResponse) {
this.setState({ blockingBecauseTooLarge: true });
} else {
try {
const bodyBuffer = props.getBody();
@ -193,13 +199,14 @@ class ResponseViewer extends Component<Props, State> {
this._selectableView?.focus();
this._selectableView?.selectAll();
if (!this.state.largeResponse) {
this._selectableView?.selectAll();
}
});
}
_renderView() {
const {
bytes,
disableHtmlPreviewJs,
disablePreviewLinks,
download,
@ -227,13 +234,12 @@ class ResponseViewer extends Component<Props, State> {
);
}
const wayTooLarge = bytes > HUGE_RESPONSE_MB * 1024 * 1024;
const { blockingBecauseTooLarge } = this.state;
const { blockingBecauseTooLarge, hugeResponse } = this.state;
if (blockingBecauseTooLarge) {
return (
<div className="response-pane__notify">
{wayTooLarge ? (
{hugeResponse ? (
<Fragment>
<p className="pad faint">Responses over {HUGE_RESPONSE_MB}MB cannot be shown</p>
<button onClick={download} className="inline-block btn btn--clicky">
@ -251,7 +257,7 @@ class ResponseViewer extends Component<Props, State> {
</button>
<button
onClick={this._handleDismissBlocker}
disabled={wayTooLarge}
disabled={hugeResponse}
className=" inline-block btn btn--clicky margin-xs"
>
Show Anyway