From 319e68ddb6399fa3ae5e5d53aea7e2b1389ba14e Mon Sep 17 00:00:00 2001 From: anaplian Date: Mon, 29 Oct 2018 23:33:37 +0000 Subject: [PATCH] Fix async validation in signup form (fixes #742) (#746) --- client/modules/User/pages/SignupView.jsx | 56 +++++++++++++++++------- client/styles/components/_forms.scss | 4 ++ server/controllers/user.controller.js | 10 ++++- 3 files changed, 52 insertions(+), 18 deletions(-) diff --git a/client/modules/User/pages/SignupView.jsx b/client/modules/User/pages/SignupView.jsx index 13ee2909..9fbbc930 100644 --- a/client/modules/User/pages/SignupView.jsx +++ b/client/modules/User/pages/SignupView.jsx @@ -59,10 +59,26 @@ class SignupView extends React.Component { } } +function asyncErrorsSelector(formName, state) { + const form = state.form[formName]; + if (!form) { + return {}; + } + + const fieldNames = Object.keys(form).filter(key => !key.startsWith('_')); + return fieldNames.reduce((asyncErrors, fieldName) => { + if (form[fieldName].asyncError) { + return { ...asyncErrors, [fieldName]: form[fieldName].asyncError }; + } + return asyncErrors; + }, {}); +} + function mapStateToProps(state) { return { user: state.user, - previousPath: state.ide.previousPath + previousPath: state.ide.previousPath, + asyncErrors: asyncErrorsSelector('signup', state) }; } @@ -71,21 +87,29 @@ function mapDispatchToProps(dispatch) { } function asyncValidate(formProps, dispatch, props) { - const fieldToValidate = props.form._active; - if (fieldToValidate) { - const queryParams = {}; - queryParams[fieldToValidate] = formProps[fieldToValidate]; - queryParams.check_type = fieldToValidate; - return axios.get('/api/signup/duplicate_check', { params: queryParams }) - .then((response) => { - if (response.data.exists) { - const error = {}; - error[fieldToValidate] = response.data.message; - throw error; - } - }); - } - return Promise.resolve(true).then(() => {}); + const errors = {}; + return Promise.resolve(true) + .then(() => { + const fieldToValidate = props.form._active; + if (fieldToValidate) { + const queryParams = {}; + queryParams[fieldToValidate] = formProps[fieldToValidate]; + queryParams.check_type = fieldToValidate; + return axios.get('/api/signup/duplicate_check', { params: queryParams }) + .then((response) => { + if (response.data.exists) { + errors[fieldToValidate] = response.data.message; + } + }); + } + return null; + }) + .then(() => { + const err = { ...errors, ...props.asyncErrors }; + if (Object.keys(err).length > 0) { + throw err; + } + }); } function onSubmitFail(errors) { diff --git a/client/styles/components/_forms.scss b/client/styles/components/_forms.scss index ea839663..213c3d3f 100644 --- a/client/styles/components/_forms.scss +++ b/client/styles/components/_forms.scss @@ -45,3 +45,7 @@ .form input[type="submit"] { @extend %forms-button; } + +.form input[type="submit"]:disabled { + cursor: not-allowed; +} diff --git a/server/controllers/user.controller.js b/server/controllers/user.controller.js index 3021d5c5..662baae3 100644 --- a/server/controllers/user.controller.js +++ b/server/controllers/user.controller.js @@ -38,7 +38,12 @@ export function createUser(req, res, next) { }); User.findOne( - { email: req.body.email }, + { + $or: [ + { email: req.body.email }, + { username: req.body.username } + ] + }, (err, existingUser) => { if (err) { res.status(404).send({ error: err }); @@ -46,7 +51,8 @@ export function createUser(req, res, next) { } if (existingUser) { - res.status(422).send({ error: 'Email is in use' }); + const fieldInUse = existingUser.email === req.body.email ? 'Email' : 'Username'; + res.status(422).send({ error: `${fieldInUse} is in use` }); return; } user.save((saveErr) => {