From c647c1e1523fe1a777fe69f55d74bc183fde7c97 Mon Sep 17 00:00:00 2001 From: Gregory Schier Date: Thu, 8 Feb 2018 20:34:25 +0800 Subject: [PATCH] Persist editor scroll positions (#746) --- .../ui/components/codemirror/code-editor.js | 51 ++++++++++++++++++- .../ui/components/editors/body/body-editor.js | 3 +- .../editors/body/graph-ql-editor.js | 8 ++- .../ui/components/editors/body/raw-editor.js | 13 +++-- .../app/ui/components/response-pane.js | 40 +++++++-------- .../ui/components/viewers/response-viewer.js | 2 + 6 files changed, 87 insertions(+), 30 deletions(-) diff --git a/packages/insomnia-app/app/ui/components/codemirror/code-editor.js b/packages/insomnia-app/app/ui/components/codemirror/code-editor.js index f529dff56..baa460961 100644 --- a/packages/insomnia-app/app/ui/components/codemirror/code-editor.js +++ b/packages/insomnia-app/app/ui/components/codemirror/code-editor.js @@ -24,6 +24,9 @@ const TAB_KEY = 9; const TAB_SIZE = 4; const MAX_SIZE_FOR_LINTING = 1000000; // Around 1MB +// Global object used for storing and persisting editor states +const editorStates = {}; + const BASE_CODEMIRROR_OPTIONS = { lineNumbers: true, placeholder: 'Start Typing...', @@ -71,6 +74,7 @@ class CodeEditor extends React.Component { }; this._originalCode = ''; + this._uniquenessKey = props.uniquenessKey; } componentWillUnmount () { @@ -79,8 +83,18 @@ class CodeEditor extends React.Component { } } + componentDidMount () { + this._restoreState(); + } + componentDidUpdate () { this._codemirrorSetOptions(); + const {uniquenessKey, defaultValue} = this.props; + if (uniquenessKey && uniquenessKey !== this._uniquenessKey) { + this._codemirrorSetValue(defaultValue); + this._restoreState(); + } + this._uniquenessKey = uniquenessKey; } shouldComponentUpdate (nextProps) { @@ -202,6 +216,34 @@ class CodeEditor extends React.Component { } } + _persistState () { + const {uniquenessKey} = this.props; + + if (!uniquenessKey || !this.codeMirror) { + return; + } + + editorStates[uniquenessKey] = { + scroll: this.codeMirror.getScrollInfo(), + selections: this.codeMirror.listSelections(), + cursor: this.codeMirror.getCursor() + }; + } + + _restoreState () { + const {uniquenessKey} = this.props; + if (!editorStates.hasOwnProperty(uniquenessKey)) { + return; + } + + const {scroll, selections, cursor} = editorStates[uniquenessKey]; + this.codeMirror.scrollTo(scroll.left, scroll.top); + + // NOTE: These won't be visible unless the editor is focused + this.codeMirror.setCursor(cursor.line, cursor.ch, {scroll: false}); + this.codeMirror.setSelections(selections, null, {scroll: false}); + } + _setFilterInputRef (n) { this._filterInput = n; } @@ -230,6 +272,7 @@ class CodeEditor extends React.Component { this.codeMirror.on('focus', this._codemirrorFocus); this.codeMirror.on('blur', this._codemirrorBlur); this.codeMirror.on('paste', this._codemirrorPaste); + this.codeMirror.on('scroll', this._codemirrorScroll); // Prevent these things if we're type === "password" this.codeMirror.on('copy', this._codemirrorPreventWhenTypePassword); @@ -539,11 +582,16 @@ class CodeEditor extends React.Component { } _codemirrorBlur (doc, e) { + this._persistState(); if (this.props.onBlur) { this.props.onBlur(e); } } + _codemirrorScroll () { + this._persistState(); + } + _codemirrorValueBeforeChange (doc, change) { // If we're in single-line mode, merge all changed lines into one if (this.props.singleLine && change.text && change.text.length > 1) { @@ -812,7 +860,8 @@ CodeEditor.propTypes = { dynamicHeight: PropTypes.bool, hintOptions: PropTypes.object, lintOptions: PropTypes.object, - infoOptions: PropTypes.object + infoOptions: PropTypes.object, + uniquenessKey: PropTypes.any }; export default CodeEditor; diff --git a/packages/insomnia-app/app/ui/components/editors/body/body-editor.js b/packages/insomnia-app/app/ui/components/editors/body/body-editor.js index 57172cd6a..a38a0871a 100644 --- a/packages/insomnia-app/app/ui/components/editors/body/body-editor.js +++ b/packages/insomnia-app/app/ui/components/editors/body/body-editor.js @@ -150,6 +150,7 @@ class BodyEditor extends React.PureComponent { return ( { const contentType = getContentTypeFromHeaders(request.headers) || mimeType; return ( { render, getRenderContext, settings, - className + className, + uniquenessKey } = this.props; const { @@ -291,6 +293,7 @@ class GraphQLEditor extends React.PureComponent { {
{ - - - +
- +
diff --git a/packages/insomnia-app/app/ui/components/viewers/response-viewer.js b/packages/insomnia-app/app/ui/components/viewers/response-viewer.js index 99fe5e1aa..8fc14286c 100644 --- a/packages/insomnia-app/app/ui/components/viewers/response-viewer.js +++ b/packages/insomnia-app/app/ui/components/viewers/response-viewer.js @@ -317,6 +317,7 @@ class ResponseViewer extends React.Component { const charset = (match && match.length >= 2) ? match[1] : 'utf-8'; return ( @@ -344,6 +345,7 @@ class ResponseViewer extends React.Component { return (