Changed environmente editor to JSON editor

This commit is contained in:
Gregory Schier 2016-07-22 14:35:49 -07:00
parent 7dce5ce3c3
commit 40aa0d037b
8 changed files with 197 additions and 150 deletions

View File

@ -1,50 +1,47 @@
import React, {PropTypes} from 'react'; import React, {PropTypes} from 'react';
import Link from './base/Link'; import Link from './base/Link';
import Editor from './base/Editor';
import Modal from './base/Modal'; import Modal from './base/Modal';
import ModalBody from './base/ModalBody'; import ModalBody from './base/ModalBody';
import ModalHeader from './base/ModalHeader'; import ModalHeader from './base/ModalHeader';
import ModalFooter from './base/ModalFooter'; import ModalFooter from './base/ModalFooter';
import KeyValueEditor from './base/KeyValueEditor';
import ModalComponent from './lib/ModalComponent'; import ModalComponent from './lib/ModalComponent';
class EnvironmentEditModal extends ModalComponent { class EnvironmentEditModal extends ModalComponent {
constructor (props) { constructor (props) {
super(props); super(props);
this.requestGroup = null;
this.state = { this.state = {
pairs: [], environmentJSON: '{}',
uniquenessKey: '' requestGroup: null
} }
} }
_saveChanges () { _saveChanges () {
const environment = this._mapPairsToData(this.state.pairs); const {requestGroup, environmentJSON} = this.state;
this.props.onChange(Object.assign({}, this.requestGroup, {environment}));
let environment;
try {
environment = JSON.parse(environmentJSON);
} catch (e) {
// That's OK. The user will (hopefully) fix the problem
console.warn('Failed to parse environment JSON', e);
return;
}
this.props.onChange(Object.assign({}, requestGroup, {environment}));
this.hide(); this.hide();
} }
_keyValueChange (pairs) { _handleChange (environmentJSON) {
this.setState({pairs}); this.setState({environmentJSON});
}
_mapPairsToData (pairs) {
return pairs.reduce((prev, curr) => {
return Object.assign({}, prev, {[curr.name]: curr.value});
}, {});
}
_mapDataToPairs (data) {
return Object.keys(data).map(key => ({name: key, value: data[key]}));
} }
_update (requestGroup) { _update (requestGroup) {
this.requestGroup = requestGroup; const environmentJSON = JSON.stringify(requestGroup.environment, null, '\t');
this.setState({ this.setState({environmentJSON, requestGroup});
pairs: this._mapDataToPairs(requestGroup.environment),
uniquenessKey: requestGroup._id
})
} }
show (requestGroup) { show (requestGroup) {
@ -58,26 +55,30 @@ class EnvironmentEditModal extends ModalComponent {
} }
render () { render () {
const {uniquenessKey, pairs} = this.state; const {environmentJSON} = this.state;
return ( return (
<Modal ref="modal" top={true} {...this.props}> <Modal ref="modal" top={true} {...this.props}>
<ModalHeader>Environment Variables</ModalHeader> <ModalHeader>Environment Variables</ModalHeader>
<ModalBody> <ModalBody>
<KeyValueEditor onChange={this._keyValueChange.bind(this)} <div className="pad-bottom">
uniquenessKey={uniquenessKey} <Editor
pairs={pairs} onChange={this._handleChange.bind(this)}
namePlaceholder="BASE_URL" value={environmentJSON}
valuePlaceholder="https://api.insomnia.com/v1"/> lightTheme={true}
mode="application/json"
/>
</div>
</ModalBody> </ModalBody>
<ModalFooter> <ModalFooter>
<div className="pull-right"> <div className="pull-right">
<button className="btn" onClick={this._saveChanges.bind(this)}>Save</button> <button className="btn" onClick={this._saveChanges.bind(this)}>Save</button>
</div> </div>
<div className="pad txt-sm"> <div className="pad faint italic txt-sm tall">
This data can be used for&nbsp; * this data can be used for&nbsp;
<Link href="https://mozilla.github.io/nunjucks/templating.html">Nunjucks Templating</Link>&nbsp; <Link href="https://mozilla.github.io/nunjucks/templating.html">
in your requests. Nunjucks Templating
</Link> in your requests
</div> </div>
</ModalFooter> </ModalFooter>
</Modal> </Modal>

View File

@ -3,10 +3,10 @@ import React, {Component, PropTypes} from 'react';
class ResponseBodyWebview extends Component { class ResponseBodyWebview extends Component {
_setBody () { _setBody () {
const {body, contentType, url} = this.props; const {body, contentType, url} = this.props;
const {webview} = this.refs; const {webView} = this.refs;
const newBody = body.replace('<head>', `<head><base href="${url}">`); const newBody = body.replace('<head>', `<head><base href="${url}">`);
webview.loadURL(`data:${contentType},${encodeURIComponent(newBody)}`); webView.loadURL(`data:${contentType},${encodeURIComponent(newBody)}`);
} }
componentDidUpdate () { componentDidUpdate () {
@ -26,19 +26,19 @@ class ResponseBodyWebview extends Component {
} }
componentDidMount () { componentDidMount () {
const {webview} = this.refs; const {webView} = this.refs;
const cb = () => { const cb = () => {
webview.removeEventListener('dom-ready', cb); webView.removeEventListener('dom-ready', cb);
this._setBody(); this._setBody();
}; };
webview.addEventListener('dom-ready', cb); webView.addEventListener('dom-ready', cb);
} }
render () { render () {
return ( return (
<webview ref="webview" src="about:blank"></webview> <webview ref="webView" src="about:blank"></webview>
); );
} }
} }

View File

@ -223,12 +223,16 @@ class Editor extends Component {
} }
render () { render () {
const {value, readOnly, fontSize} = this.props; const {value, readOnly, fontSize, lightTheme} = this.props;
const classes = classnames( const classes = classnames(
'editor', 'editor',
this.props.className, this.props.className,
{'editor--readonly': readOnly} {
'editor--readonly': readOnly,
'editor--light-theme': !!lightTheme,
'editor--dark-theme': !lightTheme
}
); );
return ( return (
@ -253,7 +257,8 @@ Editor.propTypes = {
fontSize: PropTypes.number, fontSize: PropTypes.number,
value: PropTypes.string, value: PropTypes.string,
prettify: PropTypes.bool, prettify: PropTypes.bool,
className: PropTypes.any className: PropTypes.any,
lightTheme: PropTypes.bool
}; };
export default Editor; export default Editor;

View File

@ -57,7 +57,7 @@ class RequestGroupActionsDropdown extends Component {
</button> </button>
</li> </li>
<li> <li>
<button onClick={e => EnvironmentEditModal.show()}> <button onClick={e => EnvironmentEditModal.show(requestGroup)}>
<i className="fa fa-code"></i> Environment <i className="fa fa-code"></i> Environment
</button> </button>
</li> </li>

View File

@ -21,7 +21,7 @@
.CodeMirror-scrollbar-filler, .CodeMirror-scrollbar-filler,
.CodeMirror-gutter-filler { .CodeMirror-gutter-filler {
// Let the background behind show through // Let the background behind show through
background-color: $bg-super-dark !important; background-color: transparent !important;
border: 0; border: 0;
} }
@ -82,25 +82,26 @@
} }
/* Based on Sublime Text's Monokai theme */ /* Based on Sublime Text's Monokai theme */
.editor:not(.editor--light-theme) {
.cm-s-default.CodeMirror { .cm-s-default.CodeMirror {
color: #f8f8f2; color: #f8f8f2;
} }
.cm-s-default div.CodeMirror-selected { .cm-s-default div.CodeMirror-selected {
background: #49483E; //background: #49483E;
background: $hl-xl;
} }
.cm-s-default .CodeMirror-line::selection, .cm-s-default .CodeMirror-line > span::selection, .cm-s-default .CodeMirror-line > span > span::selection { .cm-s-default .CodeMirror-line::selection, .cm-s-default .CodeMirror-line > span::selection, .cm-s-default .CodeMirror-line > span > span::selection {
background: rgba(73, 72, 62, .99); //background: rgba(73, 72, 62, .99);
} }
.cm-s-default .CodeMirror-line::-moz-selection, .cm-s-default .CodeMirror-line > span::-moz-selection, .cm-s-default .CodeMirror-line > span > span::-moz-selection { .cm-s-default .CodeMirror-line::-moz-selection, .cm-s-default .CodeMirror-line > span::-moz-selection, .cm-s-default .CodeMirror-line > span > span::-moz-selection {
background: rgba(73, 72, 62, .99); //background: rgba(73, 72, 62, .99);
} }
.cm-s-default .CodeMirror-gutters { .cm-s-default .CodeMirror-gutters {
background: #272822; //background: #272822;
border-right: 0px; border-right: 0px;
} }
@ -186,10 +187,38 @@
} }
.cm-s-default .CodeMirror-activeline-background { .cm-s-default .CodeMirror-activeline-background {
background: #373831; background: $hl-md;
} }
.cm-s-default .CodeMirror-matchingbracket { .cm-s-default .CodeMirror-matchingbracket {
text-decoration: underline; text-decoration: underline;
color: white !important; color: white !important;
} }
}
.editor--light-theme {
.cm-s-default span {
color: $hl;
}
.cm-s-default span.cm-string {
color: saturate(darken($notice, 30), 100);
}
.cm-s-default span.cm-property, .cm-s-default span.cm-attribute {
color: saturate(darken($success, 15), 100);
}
.cm-s-default span.cm-atom,
.cm-s-default span.cm-number {
color: saturate(darken($surprise, 30), 100);
}
.cm-s-default .CodeMirror-activeline-background {
background: $hl-md;
}
.cm-s-default .CodeMirror-matchingbracket {
text-decoration: underline;
}
}

View File

@ -230,7 +230,7 @@
} }
.form-control { .form-control {
padding: 0; padding: 0 $padding-xs 0 0;
width: 100%; width: 100%;
input { input {
padding: $padding-xs; padding: $padding-xs;

View File

@ -69,7 +69,11 @@ webview {
table { table {
td { td {
padding-top: 0.25em; padding-top: $padding-sm;
&:not(:last-child) {
padding-right: $padding-lg;
}
} }
th { th {
@ -156,6 +160,11 @@ i.fa {
padding-top: $padding-md; padding-top: $padding-md;
} }
.pad-bottom {
box-sizing: border-box;
padding-bottom: $padding-md;
}
.no-pad-bottom { .no-pad-bottom {
padding-bottom: 0; padding-bottom: 0;
} }

View File

@ -18,7 +18,10 @@ function buildRequestConfig (request, patch = {}) {
timeout: -1, timeout: -1,
// Unzip gzipped responses // Unzip gzipped responses
gzip: true gzip: true,
// Time the request
time: true
}; };
// Set the URL, including the query parameters // Set the URL, including the query parameters
@ -71,7 +74,7 @@ function actuallySend (request, settings) {
contentType: networkResponse.headers['content-type'], contentType: networkResponse.headers['content-type'],
url: config.url, // TODO: Handle redirects somehow url: config.url, // TODO: Handle redirects somehow
elapsedTime: networkResponse.elapsedTime, elapsedTime: networkResponse.elapsedTime,
bytes: networkResponse.connection.bytesRead, bytesRead: networkResponse.connection.bytesRead,
body: networkResponse.body, body: networkResponse.body,
headers: Object.keys(networkResponse.headers).map(name => { headers: Object.keys(networkResponse.headers).map(name => {
const value = networkResponse.headers[name]; const value = networkResponse.headers[name];