From 4cbd5fa816be4c6f8eb55404b0a27b4f241300a0 Mon Sep 17 00:00:00 2001 From: Julien Giovaresco Date: Thu, 9 Sep 2021 03:35:01 +0200 Subject: [PATCH] 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 Co-authored-by: James Gatz --- .../dropdowns/preview-mode-dropdown.tsx | 10 +++++++ .../app/ui/components/panes/response-pane.tsx | 14 ++++++++- .../ui/components/viewers/response-viewer.tsx | 30 +++++++++++-------- 3 files changed, 41 insertions(+), 13 deletions(-) diff --git a/packages/insomnia-app/app/ui/components/dropdowns/preview-mode-dropdown.tsx b/packages/insomnia-app/app/ui/components/dropdowns/preview-mode-dropdown.tsx index b5334757f1..d20ab1394b 100644 --- a/packages/insomnia-app/app/ui/components/dropdowns/preview-mode-dropdown.tsx +++ b/packages/insomnia-app/app/ui/components/dropdowns/preview-mode-dropdown.tsx @@ -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 { 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 { Preview Mode {PREVIEW_MODES.map(this.renderPreviewMode)} Actions + + + Copy raw response + Save Raw Response diff --git a/packages/insomnia-app/app/ui/components/panes/response-pane.tsx b/packages/insomnia-app/app/ui/components/panes/response-pane.tsx index 77e1ec7de0..77caabd356 100644 --- a/packages/insomnia-app/app/ui/components/panes/response-pane.tsx +++ b/packages/insomnia-app/app/ui/components/panes/response-pane.tsx @@ -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 { } } + 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 { previewMode={previewMode} updatePreviewMode={handleSetPreviewMode} showPrettifyOption={response.contentType.includes('json')} + copyToClipboard={this._handleCopyResponseToClipboard} /> diff --git a/packages/insomnia-app/app/ui/components/viewers/response-viewer.tsx b/packages/insomnia-app/app/ui/components/viewers/response-viewer.tsx index deb859a762..724b9fc73d 100644 --- a/packages/insomnia-app/app/ui/components/viewers/response-viewer.tsx +++ b/packages/insomnia-app/app/ui/components/viewers/response-viewer.tsx @@ -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 { blockingBecauseTooLarge: false, bodyBuffer: null, error: '', + hugeResponse: false, + largeResponse: false, }; refresh() { @@ -91,13 +95,15 @@ class ResponseViewer extends Component { } _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 { 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 { ); } - const wayTooLarge = bytes > HUGE_RESPONSE_MB * 1024 * 1024; - const { blockingBecauseTooLarge } = this.state; + const { blockingBecauseTooLarge, hugeResponse } = this.state; if (blockingBecauseTooLarge) { return (
- {wayTooLarge ? ( + {hugeResponse ? (

Responses over {HUGE_RESPONSE_MB}MB cannot be shown