const bindAll = require('lodash.bindall'); const connect = require('react-redux').connect; const defaults = require('lodash.defaultsdeep'); const PropTypes = require('prop-types'); const React = require('react'); const api = require('../../lib/api'); const injectIntl = require('../../lib/intl.jsx').injectIntl; const intlShape = require('../../lib/intl.jsx').intlShape; const sessionActions = require('../../redux/session.js'); const Progression = require('../progression/progression.jsx'); const UsernameStep = require('./username-step.jsx'); const BirthDateStep = require('./birthdate-step.jsx'); const GenderStep = require('./gender-step.jsx'); const CountryStep = require('./country-step.jsx'); const EmailStep = require('./email-step.jsx'); const WelcomeStep = require('./welcome-step.jsx'); const RegistrationErrorStep = require('./registration-error-step.jsx'); class JoinFlow extends React.Component { constructor (props) { super(props); bindAll(this, [ 'handleAdvanceStep', 'handlePrepareToRegister', 'handleRegistrationResponse', 'handleSubmitRegistration' ]); this.state = { formData: {}, registrationError: null, step: 0, waiting: false }; } handlePrepareToRegister (newFormData) { newFormData = newFormData || {}; const newState = { formData: defaults({}, newFormData, this.state.formData) }; this.setState(newState, () => { this.handleSubmitRegistration(this.state.formData); }); } handleRegistrationResponse (err, body, res) { // example of failing response: // [ // { // "msg": "This field is required.", // "errors": { // "username": ["This field is required."], // "recaptcha": ["Incorrect, please try again."] // }, // "success": false // } // ] this.setState({waiting: false}, () => { let errStr = ''; if (!err && res.statusCode === 200) { if (body && body[0]) { if (body[0].success) { this.props.refreshSession(); this.setState({ step: this.state.step + 1 }); return; } if (body[0].errors) { // body can include zero or more error objects, each // with its own key and description. Here we assemble // all of them into a single string, errStr. const errorKeys = Object.keys(body[0].errors); errorKeys.forEach(key => { const val = body[0].errors[key]; if (val && val[0]) { if (errStr.length) errStr += '; '; errStr += `${key}: ${val[0]}`; } }); } if (!errStr.length && body[0].msg) errStr = body[0].msg; } } this.setState({ registrationError: errStr || `${this.props.intl.formatMessage({ id: 'registration.generalError' })} (${res.statusCode})` }); }); } handleSubmitRegistration (formData) { this.setState({waiting: true}, () => { api({ host: '', uri: '/accounts/register_new_user/', method: 'post', useCsrf: true, formData: { 'username': formData.username, 'email': formData.email, 'password': formData.password, 'birth_month': formData.birth_month, 'birth_year': formData.birth_year, 'g-recaptcha-response': formData['g-recaptcha-response'], 'gender': formData.gender, 'country': formData.country, 'subscribe': true, 'is_robot': formData.yesno // no need to include csrfmiddlewaretoken; will be provided in // X-CSRFToken header, which scratchr2 looks for in // scratchr2/middleware/csrf.py line 237. } }, (err, body, res) => { this.handleRegistrationResponse(err, body, res); }); }); } handleAdvanceStep (newFormData) { newFormData = newFormData || {}; this.setState({ formData: defaults({}, newFormData, this.state.formData), step: this.state.step + 1 }); } render () { return ( {this.state.registrationError ? ( this.handleSubmitRegistration(this.state.formData)} /* eslint-enable react/jsx-no-bind */ /> ) : ( )} ); } } JoinFlow.propTypes = { intl: intlShape, onCompleteRegistration: PropTypes.func, refreshSession: PropTypes.func }; const IntlJoinFlow = injectIntl(JoinFlow); const mapDispatchToProps = dispatch => ({ refreshSession: () => { dispatch(sessionActions.refreshSession()); } }); // Allow incoming props to override redux-provided props. Used to mock in tests. const mergeProps = (stateProps, dispatchProps, ownProps) => Object.assign( {}, stateProps, dispatchProps, ownProps ); const ConnectedJoinFlow = connect( () => ({}), mapDispatchToProps, mergeProps )(IntlJoinFlow); module.exports = ConnectedJoinFlow;