Fixed signup and environments

This commit is contained in:
Gregory Schier 2016-11-17 10:45:54 -08:00
parent 0308ffb050
commit 5abf581f7f
10 changed files with 189 additions and 101 deletions

View File

@ -64,8 +64,19 @@ export function deconstructToParams (qs, strict = true) {
for (let stringPair of stringPairs) {
const tmp = stringPair.split('=');
const name = decodeURIComponent(tmp[0] || '');
const value = decodeURIComponent(tmp[1] || '');
let name = '';
try {
name = decodeURIComponent(tmp[0] || '');
} catch (e) {
console.warn(`[querystring] Failed to decode name: ${tmp[0]}`, e);
}
let value = '';
try {
value = decodeURIComponent(tmp[0] || '');
} catch (e) {
console.warn(`[querystring] Failed to decode value: ${tmp[1]}`, e);
}
if (strict && !name) {
continue;

View File

@ -29,9 +29,9 @@ export function exportHarWithRequest (renderedRequest, addContentLength = false)
};
}
export async function exportHar (requestId, addContentLength = false) {
export async function exportHar (requestId, environmentId, addContentLength = false) {
const request = await models.request.getById(requestId);
const renderedRequest = await getRenderedRequest(request);
const renderedRequest = await getRenderedRequest(request, environmentId);
return exportHarWithRequest(renderedRequest, addContentLength);
}

View File

@ -221,7 +221,14 @@ export function isLoggedIn () {
/** Log out and delete session data */
export async function logout () {
await util.post('/auth/logout');
try {
await util.post('/auth/logout');
} catch (e) {
// Not a huge deal if this fails, but we don't want it to prevent the
// user from signing out.
console.warn('Failed to logout', e);
}
unsetSessionData();
trackEvent('Session', 'Logout');
}

View File

@ -15,7 +15,8 @@ class RenderedQueryString extends Component {
_update (props, delay = false) {
clearTimeout(this._askTimeout);
this._askTimeout = setTimeout(async () => {
const {url, parameters} = await getRenderedRequest(props.request);
const {request, environmentId} = props;
const {url, parameters} = await getRenderedRequest(request, environmentId);
const qs = querystring.buildFromParams(parameters);
const fullUrl = querystring.joinURL(url, qs);
this.setState({string: util.prepareUrlForSending(fullUrl)});
@ -53,6 +54,7 @@ class RenderedQueryString extends Component {
RenderedQueryString.propTypes = {
request: PropTypes.object.isRequired,
environmentId: PropTypes.string.isRequired,
// Optional
placeholder: PropTypes.string

View File

@ -14,7 +14,8 @@ class RequestPane extends Component {
render () {
const {
request,
handleImportFileToWorkspace,
environmentId,
handleImportFile,
showPasswords,
editorFontSize,
editorLineWrapping,
@ -147,6 +148,7 @@ class RequestPane extends Component {
<code className="txt-sm block">
<RenderedQueryString
request={request}
environmentId={environmentId}
placeholder="http://myproduct.com?name=Gregory"
/>
</code>
@ -200,6 +202,7 @@ RequestPane.propTypes = {
showPasswords: PropTypes.bool.isRequired,
editorFontSize: PropTypes.number.isRequired,
editorLineWrapping: PropTypes.bool.isRequired,
environmentId: PropTypes.string.isRequired,
// Optional
request: PropTypes.object,

View File

@ -18,7 +18,6 @@ import Sidebar from './sidebar/Sidebar';
import RequestPane from './RequestPane';
import ResponsePane from './ResponsePane';
import * as models from '../../models/index';
import {PREVIEW_MODE_FRIENDLY} from '../../common/constants';
const Wrapper = props => {
@ -115,6 +114,7 @@ const Wrapper = props => {
useBulkHeaderEditor={settings.useBulkHeaderEditor}
editorFontSize={settings.editorFontSize}
editorLineWrapping={settings.editorLineWrapping}
environmentId={activeEnvironment ? activeEnvironment._id : 'n/a'}
handleCreateRequest={handleCreateRequest.bind(null, activeRequest ? activeRequest.parentId : activeWorkspace._id)}
updateRequestBody={body => models.request.update(activeRequest, {body})}
updateRequestUrl={url => handleUpdateRequestUrl(activeRequest, url)}
@ -157,7 +157,10 @@ const Wrapper = props => {
<LoginModal ref={m => registerModal(m)}/>
<SignupModal ref={m => registerModal(m)}/>
<PaymentModal ref={m => registerModal(m)}/>
<GenerateCodeModal ref={m => registerModal(m)}/>
<GenerateCodeModal
ref={m => registerModal(m)}
environmentId={activeEnvironment ? activeEnvironment._id : 'n/a'}
/>
<SettingsModal
ref={m => registerModal(m)}
handleExportWorkspaceToFile={handleExportWorkspaceToFile}

View File

@ -1,4 +1,4 @@
import React, {Component} from 'react';
import React, {Component, PropTypes} from 'react';
import HTTPSnippet, {availableTargets} from 'httpsnippet';
import CopyButton from '../base/CopyButton';
@ -57,7 +57,8 @@ class GenerateCodeModal extends Component {
// Some clients need a content-length for the request to succeed
const addContentLength = (TO_ADD_CONTENT_LENGTH[target.key] || []).find(c => c === client.key);
const har = await exportHar(request._id, addContentLength);
const {environmentId} = this.props;
const har = await exportHar(request._id, environmentId, addContentLength);
const snippet = new HTTPSnippet(har);
const cmd = snippet.convert(target.key, client.key);
@ -132,6 +133,8 @@ class GenerateCodeModal extends Component {
}
}
GenerateCodeModal.propTypes = {};
GenerateCodeModal.propTypes = {
environmentId: PropTypes.string.isRequired,
};
export default GenerateCodeModal;

View File

@ -66,7 +66,7 @@ class LoginModal extends Component {
<form onSubmit={this._handleLogin.bind(this)}>
<Modal ref={m => this.modal = m} {...this.props}>
<ModalHeader>{title || "Login to Your Account"}</ModalHeader>
<ModalBody className="pad changelog">
<ModalBody className="pad">
{message ? (
<p className="notice info">{message}</p>
) : null}

View File

@ -1,4 +1,6 @@
import React, {Component} from 'react';
import classnames from 'classnames';
import Link from '../base/Link';
import Modal from '../base/Modal';
import ModalBody from '../base/ModalBody';
import ModalHeader from '../base/ModalHeader';
@ -9,11 +11,15 @@ import LoginModal from './LoginModal';
import * as sync from '../../../sync';
import {trackEvent} from '../../../analytics';
const STEP_BASIC_INFO = 'basic';
const STEP_CONFIRM_PASSWORD = 'confirm';
const STEP_LOGIN_INFO = 'done';
class SignupModal extends Component {
constructor (props) {
super(props);
this.state = {
step: 1,
step: STEP_BASIC_INFO,
error: '',
loading: false
}
@ -21,6 +27,12 @@ class SignupModal extends Component {
async _handleSignup (e) {
e.preventDefault();
if (this.state.step === STEP_BASIC_INFO) {
this.setState({step: STEP_CONFIRM_PASSWORD});
return;
}
this.setState({error: '', loading: true});
const email = this._emailInput.value;
@ -30,7 +42,7 @@ class SignupModal extends Component {
try {
await session.signup(firstName, lastName, email, password);
this.setState({step: 2, loading: false});
this.setState({step: STEP_LOGIN_INFO, loading: false});
sync.init();
} catch (e) {
this.setState({error: e.message, loading: false});
@ -45,101 +57,147 @@ class SignupModal extends Component {
trackEvent('Auth', 'Switch', 'To Login');
}
_checkPasswordsMatch () {
if (this._passwordInput.value !== this._passwordConfirmInput.value) {
this._passwordConfirmInput.setCustomValidity('Password didn\'t match')
} else {
this._passwordConfirmInput.setCustomValidity('')
}
}
show () {
this.setState({step: 1});
this.setState({step: STEP_BASIC_INFO});
this.modal.show();
setTimeout(() => this._nameFirstInput.focus(), 200);
}
render () {
const {step} = this.state;
if (step === 1) {
return (
<form onSubmit={this._handleSignup.bind(this)}>
<Modal ref={m => this.modal = m} {...this.props}>
<ModalHeader>Sign Up For a New Account</ModalHeader>
<ModalBody className="pad">
<label htmlFor="signup-name-first">First Name</label>
<div className="form-control form-control--outlined">
<input type="text"
required="required"
id="signup-name-first"
name="signup-name-first"
placeholder="Jane"
ref={n => this._nameFirstInput = n}/>
</div>
<label htmlFor="signup-name-last">Last Name</label>
<div className="form-control form-control--outlined">
<input type="text"
id="signup-name-last"
name="signup-name-last"
placeholder="Doe"
ref={n => this._nameLastInput = n}/>
</div>
<label htmlFor="signup-email">Email Address</label>
<div className="form-control form-control--outlined">
<input type="email"
required="required"
id="signup-email"
name="signup-email"
placeholder="me@mydomain.com"
ref={n => this._emailInput = n}/>
</div>
<label htmlFor="signup-password">Password <span
className="faint">(minimum 6 characters)</span></label>
<div className="form-control form-control--outlined">
<input type="password"
required="required"
pattern=".{6,}"
id="signup-password"
name="signup-password"
placeholder="•••••••••••••"
ref={n => this._passwordInput = n}/>
</div>
<p className="italic faint pad-top-sm">
NOTE: your password is used for end-to-end encryption so try not
to lose it
let inner = null;
if (step === STEP_BASIC_INFO || step === STEP_CONFIRM_PASSWORD) {
inner = [
<ModalHeader key="header">Sign Up For a New Account</ModalHeader>,
<ModalBody key="body" className="pad">
<div className={classnames({hide: step !== STEP_BASIC_INFO})}>
<label htmlFor="signup-name-first">First Name</label>
<div className="form-control form-control--outlined">
<input type="text"
required="required"
id="signup-name-first"
name="signup-name-first"
placeholder="Jane"
ref={n => this._nameFirstInput = n}/>
</div>
<label htmlFor="signup-name-last">Last Name</label>
<div className="form-control form-control--outlined">
<input type="text"
id="signup-name-last"
name="signup-name-last"
placeholder="Doe"
ref={n => this._nameLastInput = n}/>
</div>
<label htmlFor="signup-email">Email Address</label>
<div className="form-control form-control--outlined">
<input type="email"
required="required"
id="signup-email"
name="signup-email"
placeholder="me@mydomain.com"
ref={n => this._emailInput = n}/>
</div>
<label htmlFor="signup-password">
Password <span className="faint">(minimum 6 characters)</span>
</label>
<div className="form-control form-control--outlined">
<input type="password"
required="required"
pattern=".{6,}"
id="signup-password"
name="signup-password"
placeholder="•••••••••••••"
ref={n => this._passwordInput = n}/>
</div>
</div>
{step === STEP_CONFIRM_PASSWORD ? (
<div className="text-center pad">
<p className="text-center txt-lg">
Keep your password safe because it cannot be recovered
<br/>
<span className="txt-sm faint italic">
<Link href="https://insomnia.rest/documentation/plus/">Read More</Link>
{" "}
on how your password is used to encrypt your data
</span>
</p>
{this.state.error ? (
<div className="danger pad-top">** {this.state.error}</div>
) : null}
</ModalBody>
<ModalFooter>
<div className="margin-left">
Already have an account?
{" "}
<a href="#" onClick={this._handleLogin.bind(this)}>Login</a>
<div className="text-left">
<label htmlFor="signup-password-confirm" className="pad-left-half">
Confirm your Password
</label>
<div className="form-control form-control--outlined">
<input type="password"
required="required"
pattern=".{6,}"
id="signup-password-confirm"
name="signup-password-confirm"
placeholder="•••••••••••••"
autoFocus="autoFocus"
onChange={this._checkPasswordsMatch.bind(this)}
ref={n => this._passwordConfirmInput = n}/>
</div>
<button type="button"
className="pad-sm faint"
onClick={e => this.setState({step: STEP_BASIC_INFO})}>
<i className="fa fa-caret-left"></i>
Go back
</button>
</div>
<button type="submit" className="btn">
{this.state.loading ?
<i className="fa fa-spin fa-refresh margin-right-sm"></i> : null}
Create Account
</button>
</ModalFooter>
</Modal>
</form>
)
</div>
) : null}
{this.state.error ? (
<div className="danger pad-top">** {this.state.error}</div>
) : null}
</ModalBody>,
<ModalFooter key="footer">
<div className="margin-left">
Already have an account?
{" "}
<a href="#" onClick={this._handleLogin.bind(this)}>Login</a>
</div>
<button type="submit" className="btn">
{this.state.loading ?
<i className="fa fa-spin fa-refresh margin-right-sm"></i> : null}
Create Account
</button>
</ModalFooter>
]
} else {
return (
<Modal ref={m => this.modal = m} {...this.props}>
<ModalHeader>Account Created</ModalHeader>
<ModalBody className="pad">
<h1>Please verify your account</h1>
<p>
A verification email has been sent to your email address. Once
you have received it, you may login.
</p>
</ModalBody>
<ModalFooter>
<button type="submit"
className="btn"
onClick={e => this._handleLogin(e)}>
Proceed to Login
</button>
</ModalFooter>
</Modal>
)
inner = [
<ModalHeader key="header">Account Created</ModalHeader>,
<ModalBody key="body" className="pad">
<h1>Please verify your account</h1>
<p>
A verification email has been sent to your email address. Once
you have received it, you may login.
</p>
</ModalBody>,
<ModalFooter key="footer">
<button type="submit"
className="btn"
onClick={e => this._handleLogin(e)}>
Proceed to Login
</button>
</ModalFooter>
]
}
return (
<form onSubmit={this._handleSignup.bind(this)}>
<Modal ref={m => this.modal = m} {...this.props}>
{inner}
</Modal>
</form>
)
}
}

View File

@ -66,9 +66,10 @@ const SettingsSync = ({
</p>,
<p key="3" className="text-center italic">
$5 per month or $50 per year
<div className="txt-sm faint pad-top-sm">
<br/>
<span className="txt-sm faint pad-top-sm">
14-day trial (credit card required) cancel at any time
</div>
</span>
</p>
]}
</div>