416 lines
16 KiB
JavaScript
416 lines
16 KiB
JavaScript
import Inferno from 'inferno';
|
|
import { Component } from 'inferno';
|
|
import PropTypes from 'prop-types';
|
|
import marked from 'marked';
|
|
import store from 'store';
|
|
|
|
import { Modal } from '../../structure/Modal';
|
|
import { SearchBox } from '../../management/SearchBox';
|
|
|
|
import { request } from '../../../Helpers';
|
|
|
|
export class LoginForm extends Component {
|
|
constructor (props) {
|
|
super(props);
|
|
|
|
PropTypes.checkPropTypes({
|
|
logIn: PropTypes.func.isRequired,
|
|
signUp: PropTypes.func.isRequired,
|
|
}, props, 'prop', 'LoginForm');
|
|
|
|
this.state = {
|
|
visibleTab: 'login',
|
|
loginEmail: '',
|
|
loginPassword: '',
|
|
loginEmailError: '',
|
|
loginPasswordError: '',
|
|
loginFormIsValid: true,
|
|
signupEmail: '',
|
|
signupUsername: '',
|
|
signupPublicName: '',
|
|
signupPassword: '',
|
|
signupConfirm: '',
|
|
signupAllowEmail: true,
|
|
signupEmailError: '',
|
|
signupUsernameError: '',
|
|
signupPasswordError: '',
|
|
signupConfirmError: '',
|
|
signupEmailChecking: false,
|
|
signupUsernameChecking: false,
|
|
signupEmailIsUnique: true,
|
|
signupUsernameIsUnique: true,
|
|
signupFormIsValid: true,
|
|
};
|
|
}
|
|
|
|
get loginFormIsValid () {
|
|
const {
|
|
loginEmailError,
|
|
loginPasswordError,
|
|
} = this.state;
|
|
return loginEmailError === '' && loginPasswordError === '';
|
|
}
|
|
|
|
get signupFormIsValid () {
|
|
const {
|
|
signupEmailError,
|
|
signupEmailChecking,
|
|
signupEmailIsUnique,
|
|
signupUsernameError,
|
|
signupUsernameChecking,
|
|
signupUsernameIsUnique,
|
|
signupPasswordError,
|
|
signupConfirmError,
|
|
} = this.state;
|
|
return !signupEmailChecking && !signupUsernameChecking
|
|
&& signupEmailIsUnique && signupUsernameIsUnique
|
|
&& signupEmailError === '' && signupUsernameError === ''
|
|
&& signupPasswordError === '' && signupConfirmError === '';
|
|
}
|
|
|
|
changeTab (tab) {
|
|
this.setState({ visibleTab: tab });
|
|
}
|
|
|
|
updateField (field, event) {
|
|
const {value, checked} = event.target;
|
|
const fieldUpdate = {};
|
|
const fieldErrors = this.validateField(field, value);
|
|
fieldUpdate[field] = (field === 'signupAllowEmail') ? checked : value;
|
|
this.setState(Object.assign(fieldUpdate, fieldErrors));
|
|
}
|
|
|
|
validateField (field, value) {
|
|
const fieldErrors = {};
|
|
const errorFieldName = `${field}Error`;
|
|
let isValid = true;
|
|
const requiredFields = ['loginEmail', 'loginPassword', 'signupEmail', 'signupPassword', 'signupConfirm'];
|
|
if (requiredFields.includes(field)) {
|
|
if (value === '') {
|
|
isValid = false;
|
|
fieldErrors[errorFieldName] = 'This field must not be blank';
|
|
} else if (field === 'signupEmail' && !/.+@.+/g.test(value)) {
|
|
isValid = false;
|
|
fieldErrors[errorFieldName] = 'The email address you entered looks wrong';
|
|
} else if (field === 'signupPassword' && value.length < 6) {
|
|
isValid = false;
|
|
fieldErrors[errorFieldName] = 'Please make your password at least 6 characters long';
|
|
} else if (field === 'signupConfirm' && value !== this.state.signupPassword) {
|
|
isValid = false;
|
|
fieldErrors[errorFieldName] = 'Your passwords must match';
|
|
}
|
|
}
|
|
|
|
if (field === 'signupUsername') {
|
|
if (value !== '' && /[^a-zA-Z0-9]+/g.test(value)) {
|
|
isValid = false;
|
|
fieldErrors[errorFieldName] = 'Please use only letters and numbers';
|
|
}
|
|
}
|
|
|
|
if (isValid) {
|
|
fieldErrors[errorFieldName] = '';
|
|
}
|
|
return fieldErrors;
|
|
}
|
|
|
|
validateSignupForm (callback) {
|
|
const fields = ['signupEmail', 'signupUsername', 'signupPassword', 'signupConfirm'];
|
|
let errors = {};
|
|
fields.forEach(field => {
|
|
const fieldErrors = this.validateField(field, this.state[field]);
|
|
errors = Object.assign(errors, fieldErrors);
|
|
});
|
|
errors.signupFormIsValid = !signupEmailChecking && !signupUsernameChecking
|
|
&& signupEmailIsUnique && signupUsernameIsUnique
|
|
&& Object.keys(errors).every(field => errors[field] === '');
|
|
this.setState(errors, callback);
|
|
}
|
|
|
|
validateLoginForm (callback) {
|
|
const fields = ['loginEmail','loginPassword'];
|
|
let errors = {};
|
|
fields.forEach(field => {
|
|
errors = Object.assign(errors, this.validateField(field, this.state[field]));
|
|
});
|
|
errors.loginFormIsValid = Object.keys(errors).every(field => {
|
|
return errors[field] === '';
|
|
});
|
|
this.setState(errors, callback);
|
|
}
|
|
|
|
checkFieldUnique (field, event) {
|
|
const {value} = event.target;
|
|
const fieldUpdate = {};
|
|
const errorFieldName = `${field}Error`;
|
|
if (field === 'signupEmail') {
|
|
this.setState({ signupEmailChecking: true }, () => {
|
|
request('check-email', { email: value }, (response) => {
|
|
const { data, error } = response;
|
|
fieldUpdate['signupEmailChecking'] = false;
|
|
if (error) {
|
|
console.error(data);
|
|
} else {
|
|
fieldUpdate['signupEmailIsUnique'] = !data;
|
|
}
|
|
}).then(() => {
|
|
this.setState(fieldUpdate);
|
|
});
|
|
});
|
|
} else if (field === 'signupUsername') {
|
|
this.setState({ signupUsernameChecking: true }, () => {
|
|
request('check-username', { username: value }, (response) => {
|
|
const { data, error } = response;
|
|
fieldUpdate['signupUsernameChecking'] = false;
|
|
if (error) {
|
|
console.error(data);
|
|
} else {
|
|
fieldUpdate['signupUsernameIsUnique'] = !data;
|
|
}
|
|
}).then(() => {
|
|
this.setState(fieldUpdate);
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
logIn () {
|
|
this.validateLoginForm(() => {
|
|
if (this.loginFormIsValid) {
|
|
const { loginEmail, loginPassword } = this.state;
|
|
this.props.logIn(loginEmail, loginPassword);
|
|
}
|
|
});
|
|
}
|
|
|
|
createAccount () {
|
|
this.validateSignupForm(() => {
|
|
if (this.signupFormIsValid) {
|
|
const {
|
|
signupEmail,
|
|
signupUsername,
|
|
signupPublicName,
|
|
signupPassword,
|
|
signupAllowEmail
|
|
} = this.state;
|
|
this.props.signUp(signupEmail, signupPassword, {
|
|
username: signupUsername,
|
|
publicName: signupPublicName,
|
|
allowEmail: signupAllowEmail,
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
render () {
|
|
return (
|
|
<div className='columns'>
|
|
<div className='column'>
|
|
<div className='tabs is-boxed'>
|
|
<ul>
|
|
<li className={this.state.visibleTab === 'login' ? 'is-active' : null}>
|
|
<a onClick={() => this.changeTab('login')}>
|
|
Log In
|
|
</a>
|
|
</li>
|
|
<li className={this.state.visibleTab === 'signup' ? 'is-active' : null}>
|
|
<a onClick={() => this.changeTab('signup')}>
|
|
Sign Up
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
{this.state.visibleTab === 'login'
|
|
? (
|
|
<div className="has-text-left">
|
|
<h3 className='title is-3'>
|
|
Log In
|
|
</h3>
|
|
<div className='field'>
|
|
<label className='label'>
|
|
Email/Username
|
|
</label>
|
|
<div className='control'>
|
|
<input className={`input ${this.state.loginEmailError !== '' && 'is-danger'}`}
|
|
type='email' onInput={(event) => this.updateField('loginEmail', event)} />
|
|
{
|
|
this.state.loginEmailError !== ''
|
|
? (
|
|
<div className='help is-danger'>
|
|
{this.state.loginEmailError}
|
|
</div>
|
|
) : null
|
|
}
|
|
</div>
|
|
</div>
|
|
<div className='field'>
|
|
<label className='label'>
|
|
Password
|
|
</label>
|
|
<div className='control'>
|
|
<input className={`input ${this.state.loginPasswordError !== '' && 'is-danger'}`}
|
|
type='password' onInput={(event) => this.updateField('loginPassword', event)}
|
|
onChange={this.validateLoginForm.bind(this)} />
|
|
{
|
|
this.state.loginPasswordError !== ''
|
|
? (
|
|
<div className='help is-danger'>
|
|
{this.state.loginPasswordError}
|
|
</div>
|
|
) : null
|
|
}
|
|
</div>
|
|
</div>
|
|
<div className='field'>
|
|
<a className='button is-success'
|
|
onClick={this.logIn.bind(this)}>
|
|
Log In
|
|
</a>
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<div className="has-text-left">
|
|
<h3 className='title is-3'>
|
|
Create a New Account
|
|
</h3>
|
|
<div className='columns'>
|
|
<div className='column'>
|
|
<div className='content is-small'>
|
|
<p>
|
|
Creating an account allows you to save and switch between as many dictionaries as you need
|
|
and access them from any device for free! If you have a dictionary you've been working on
|
|
loaded already, it will automatically be uploaded to your account when you log in for the
|
|
first time.
|
|
</p>
|
|
<p>
|
|
Plus if you allow us to send you emails, we'll make sure that you're the first to hear
|
|
about any new features that get added or if any of our policies change for any reason.
|
|
We'll never spam you or sell your information.
|
|
</p>
|
|
<p>
|
|
By creating an account, you are indicating that you agree to the <a>Terms of Service</a> and
|
|
that you understand Lexiconga's <a>Privacy Policy</a>.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div className='column'>
|
|
<div className='field'>
|
|
<label className='label'>
|
|
Email Address<sup>*</sup>
|
|
</label>
|
|
<div className={`control ${this.state.signupEmailChecking && 'is-loading'}`}>
|
|
<input className={`input ${(this.state.signupEmailError !== '' || !this.state.signupEmailIsUnique) && 'is-danger'}`}
|
|
type='email' value={this.state.signupEmail}
|
|
onInput={(event) => this.updateField('signupEmail', event)}
|
|
onBlur={(event) => this.checkFieldUnique('signupEmail', event)} />
|
|
{
|
|
(this.state.signupEmailError !== '' || !this.state.signupEmailIsUnique)
|
|
? (
|
|
<div className='help is-danger'>
|
|
{!this.state.signupEmailIsUnique && <p>This email address is already in use</p>}
|
|
{this.state.signupEmailError}
|
|
</div>
|
|
) : null
|
|
}
|
|
</div>
|
|
</div>
|
|
<div className='field'>
|
|
<label className='label'>
|
|
Username
|
|
</label>
|
|
<div className='help'>
|
|
This is your unique identifier that appears in the URL if you ever decide to share your dictionaries publicly.
|
|
</div>
|
|
<div className={`control ${this.state.signupUsernameChecking && 'is-loading'}`}>
|
|
<input className={`input ${!this.state.signupUsernameIsUnique && 'is-danger'}`}
|
|
type='text' value={this.state.signupUsername}
|
|
onInput={(event) => this.updateField('signupUsername', event)}
|
|
onBlur={(event) => this.checkFieldUnique('signupUsername', event)} />
|
|
{
|
|
(this.state.signupUsernameError !== '' || !this.state.signupUsernameIsUnique)
|
|
? (
|
|
<div className='help is-danger'>
|
|
{!this.state.signupUsernameIsUnique && <p>This username address is already in use</p>}
|
|
{this.state.signupUsernameError}
|
|
</div>
|
|
) : null
|
|
}
|
|
</div>
|
|
</div>
|
|
<div className='field'>
|
|
<label className='label'>
|
|
Public Name
|
|
</label>
|
|
<div className='help'>
|
|
This is the name we greet you with and what we display if you ever decide to share your dictionaries publicly.
|
|
</div>
|
|
<div className='control'>
|
|
<input className='input'
|
|
type='text' value={this.state.signupPublicName}
|
|
onInput={(event) => this.updateField('signupPublicName', event)} />
|
|
</div>
|
|
</div>
|
|
<div className='field'>
|
|
<label className='label'>
|
|
Password<sup>*</sup>
|
|
</label>
|
|
<div className='control'>
|
|
<input className={`input ${this.state.signupPasswordError !== '' && 'is-danger'}`}
|
|
type='password' value={this.state.signupPassword}
|
|
onInput={(event) => this.updateField('signupPassword', event)} />
|
|
{
|
|
this.state.signupPasswordError !== ''
|
|
? (
|
|
<div className='help is-danger'>
|
|
{this.state.signupPasswordError}
|
|
</div>
|
|
) : null
|
|
}
|
|
</div>
|
|
</div>
|
|
<div className='field'>
|
|
<label className='label'>
|
|
Confirm Password<sup>*</sup>
|
|
</label>
|
|
<div className='control'>
|
|
<input className={`input ${this.state.signupConfirmError !== '' && 'is-danger'}`}
|
|
type='password' value={this.state.signupConfirm}
|
|
onInput={(event) => this.updateField('signupConfirm', event)} />
|
|
{
|
|
this.state.signupConfirmError !== ''
|
|
? (
|
|
<div className='help is-danger'>
|
|
{this.state.signupConfirmError}
|
|
</div>
|
|
) : null
|
|
}
|
|
</div>
|
|
</div>
|
|
<div className='field'>
|
|
<div className='control'>
|
|
<label className='checkbox'>
|
|
<input type='checkbox' checked={this.state.signupAllowEmail}
|
|
onClick={(event) => this.updateField('signupAllowEmail', event)} />
|
|
Allow Emails
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div className='field'>
|
|
<div className='control'>
|
|
<a className='button is-success'
|
|
onClick={this.createAccount.bind(this)}>
|
|
Create Account
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
}
|