mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2025-03-28 05:41:58 -04:00
working username step of join flow
This commit is contained in:
parent
1061c92e62
commit
15ddd0e86c
1 changed files with 223 additions and 6 deletions
|
@ -1,9 +1,12 @@
|
|||
/* eslint-disable react/no-multi-comp */
|
||||
const bindAll = require('lodash.bindall');
|
||||
const React = require('react');
|
||||
const PropTypes = require('prop-types');
|
||||
import {Formik} from 'formik';
|
||||
const {injectIntl, intlShape} = require('react-intl');
|
||||
|
||||
const api = require('../../lib/api');
|
||||
const FormikInput = require('../../components/formik-forms/formik-input.jsx');
|
||||
const JoinFlowStep = require('./join-flow-step.jsx');
|
||||
|
||||
/*
|
||||
|
@ -13,6 +16,157 @@ const JoinFlowStep = require('./join-flow-step.jsx');
|
|||
class UsernameStep extends React.Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
bindAll(this, [
|
||||
'handleChangeShowPassword',
|
||||
'validateUsernameLocally',
|
||||
'validateUsernameRemotely',
|
||||
'handleValidSubmit',
|
||||
'validatePassword',
|
||||
'validatePasswordConfirmLocally',
|
||||
'validateUsername',
|
||||
'validateForm'
|
||||
]);
|
||||
this.state = {
|
||||
showPassword: props.showPassword,
|
||||
waiting: false,
|
||||
validUsername: ''
|
||||
};
|
||||
}
|
||||
handleChangeShowPassword () {
|
||||
this.setState({showPassword: !this.state.showPassword});
|
||||
}
|
||||
validateUsernameLocally (username, require) {
|
||||
let error;
|
||||
if (username.length === 0 && !require) { // allow blank username if not submitting yet
|
||||
return null;
|
||||
}
|
||||
if (!username || username === '') {
|
||||
error = this.props.intl.formatMessage({id: 'form.validationRequiredNice'});
|
||||
} else if (username.length < 3) {
|
||||
error = this.props.intl.formatMessage({id: 'registration.validationUsernameMinLength'});
|
||||
} else if (username.length > 20) {
|
||||
error = this.props.intl.formatMessage({id: 'registration.validationUsernameMaxLength'});
|
||||
} else if (!/^[\w-]+$/i.test(username)) {
|
||||
error = this.props.intl.formatMessage({id: 'registration.validationUsernameRegexp'});
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
validateUsernameRemotely (username, require) {
|
||||
if (username.length === 0 && !require) { // allow blank username if not submitting yet
|
||||
return null;
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
api({
|
||||
uri: `/accounts/checkusername/${username}/`
|
||||
}, (err, body, res) => {
|
||||
if (err || res.statusCode !== 200) {
|
||||
reject('api error');
|
||||
}
|
||||
switch (body.msg) {
|
||||
case 'valid username':
|
||||
this.setState({validUsername: 'pass'}, () => {
|
||||
resolve(null);
|
||||
});
|
||||
break;
|
||||
case 'username exists':
|
||||
reject(this.props.intl.formatMessage({
|
||||
id: 'registration.validationUsernameExists'
|
||||
}));
|
||||
break;
|
||||
case 'bad username':
|
||||
reject(this.props.intl.formatMessage({
|
||||
id: 'registration.validationUsernameVulgar'
|
||||
}));
|
||||
break;
|
||||
case 'invalid username':
|
||||
default:
|
||||
reject(this.props.intl.formatMessage({
|
||||
id: 'registration.validationUsernameInvalid'
|
||||
}));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
validateUsername (username, require) {
|
||||
const localResult = this.validateUsernameLocally(username, require);
|
||||
// if error, return that
|
||||
if (localResult) {
|
||||
return new Promise((resolve, reject) => {
|
||||
reject(localResult);
|
||||
});
|
||||
}
|
||||
return this.validateUsernameRemotely(username, require);
|
||||
}
|
||||
validatePasswordLocally (password, require) {
|
||||
let error;
|
||||
if (password.length === 0) {
|
||||
if (require) {
|
||||
return this.props.intl.formatMessage({id: 'form.validationRequiredNice'});
|
||||
}
|
||||
// not submitting yet, so allow blank password, and don't throw error
|
||||
return null;
|
||||
}
|
||||
if (password.length < 6) {
|
||||
error = this.props.intl.formatMessage({id: 'registration.validationPasswordLength'});
|
||||
}
|
||||
if (password === 'password') {
|
||||
error = this.props.intl.formatMessage({id: 'registration.validationPasswordNotEquals'});
|
||||
}
|
||||
return error;
|
||||
}
|
||||
validatePassword (password, require) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const localResult = this.validatePasswordLocally(password, require);
|
||||
if (localResult) {
|
||||
reject(localResult);
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
validatePasswordConfirmLocally (password, passwordConfirm, require) {
|
||||
let error;
|
||||
if (passwordConfirm.length === 0 && !require) { // allow blank password if not submitting yet
|
||||
return null;
|
||||
}
|
||||
if (password !== passwordConfirm) {
|
||||
error = this.props.intl.formatMessage({id: 'general.error'});
|
||||
}
|
||||
return error;
|
||||
}
|
||||
validateForm (values) {
|
||||
return this.validateUsername(values.username, true).then(() => (
|
||||
this.validatePassword(values.password, true).then(() => {
|
||||
if (values.password === values.username) {
|
||||
throw new Error({
|
||||
password: this.props.intl.formatMessage({id: 'registration.validationPasswordNotUsername'})
|
||||
});
|
||||
}
|
||||
const passwordConfirmFieldError =
|
||||
this.validatePasswordConfirmLocally(values.password, values.passwordConfirm, true);
|
||||
if (passwordConfirmFieldError) {
|
||||
throw new Error({
|
||||
passwordConfirm: passwordConfirmFieldError
|
||||
});
|
||||
}
|
||||
return null; // not necessary, but just to be clear
|
||||
}, passReject => {
|
||||
throw new Error({password: passReject});
|
||||
})
|
||||
), error => { // username error
|
||||
throw new Error({username: error});
|
||||
});
|
||||
}
|
||||
handleValidSubmit (formData) {
|
||||
this.setState({waiting: true});
|
||||
this.validateUsername(formData.username, true).then(result => {
|
||||
this.setState({waiting: false});
|
||||
this.props.onNextStep(formData);
|
||||
}, error => {
|
||||
console.log('username validation ERROR promise value:');
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
render () {
|
||||
return (
|
||||
|
@ -22,14 +176,72 @@ class UsernameStep extends React.Component {
|
|||
password: '',
|
||||
passwordConfirm: ''
|
||||
}}
|
||||
validate={this.validateForm}
|
||||
validateOnBlur={false}
|
||||
validateOnChange={false}
|
||||
onSubmit={this.handleValidSubmit}
|
||||
>
|
||||
<JoinFlowStep
|
||||
description={this.props.intl.formatMessage({id: 'registration.usernameStepDescription'})}
|
||||
title={this.props.intl.formatMessage({id: 'general.joinScratch'})}
|
||||
waiting={this.props.waiting}
|
||||
/>
|
||||
{props => {
|
||||
const {
|
||||
errors,
|
||||
validateField,
|
||||
values
|
||||
} = props;
|
||||
return (
|
||||
<JoinFlowStep
|
||||
description={this.props.intl.formatMessage({id: 'registration.usernameStepDescription'})}
|
||||
title={this.props.intl.formatMessage({id: 'general.joinScratch'})}
|
||||
waiting={this.props.waiting || this.state.waiting}
|
||||
>
|
||||
<div>
|
||||
<div className="username-label">
|
||||
<b>
|
||||
{this.props.intl.formatMessage({id: 'registration.createUsername'})}
|
||||
</b>
|
||||
</div>
|
||||
<FormikInput
|
||||
className={this.state.validUsername}
|
||||
error={errors.username}
|
||||
id="username"
|
||||
name="username"
|
||||
validate={this.validateUsername}
|
||||
onBlur={() => validateField('username')}
|
||||
/>
|
||||
<b>
|
||||
{this.props.intl.formatMessage({id: 'general.password'})}
|
||||
</b>
|
||||
<div
|
||||
onClick={this.handleChangeShowPassword}
|
||||
>
|
||||
{this.state.showPassword ? 'Hide password' : (
|
||||
this.props.intl.formatMessage({id: 'registration.showPassword'})
|
||||
)}
|
||||
</div>
|
||||
<FormikInput
|
||||
className={this.state.validUsername}
|
||||
error={errors.password}
|
||||
id="password"
|
||||
name="password"
|
||||
type={this.state.showPassword ? 'text' : 'password'}
|
||||
validate={this.validatePassword}
|
||||
onBlur={() => validateField('password')}
|
||||
/>
|
||||
<b>
|
||||
{this.props.intl.formatMessage({id: 'general.error'})}
|
||||
</b>
|
||||
<FormikInput
|
||||
className={this.state.validUsername}
|
||||
error={errors.passwordConfirm}
|
||||
id="passwordConfirm"
|
||||
name="passwordConfirm"
|
||||
type={this.state.showPassword ? 'text' : 'password'}
|
||||
validate={() => this.validatePasswordConfirmLocally(values.password, values.passwordConfirm)}
|
||||
onBlur={() => validateField('passwordConfirm')}
|
||||
/>
|
||||
</div>
|
||||
</JoinFlowStep>
|
||||
);
|
||||
}}
|
||||
</Formik>
|
||||
);
|
||||
}
|
||||
|
@ -38,11 +250,16 @@ class UsernameStep extends React.Component {
|
|||
|
||||
UsernameStep.propTypes = {
|
||||
intl: intlShape,
|
||||
onNextStep: PropTypes.func,
|
||||
showPassword: PropTypes.bool,
|
||||
waiting: PropTypes.bool
|
||||
};
|
||||
|
||||
UsernameStep.defaultProps = {
|
||||
showPassword: false,
|
||||
waiting: false
|
||||
};
|
||||
|
||||
module.exports.UsernameStep = injectIntl(UsernameStep);
|
||||
const IntlUsernameStep = injectIntl(UsernameStep);
|
||||
|
||||
module.exports.UsernameStep = IntlUsernameStep;
|
||||
|
|
Loading…
Add table
Reference in a new issue