diff --git a/src/components/structures/auth/Registration.js b/src/components/structures/auth/Registration.js index 0d36e592f8..b19be60fbe 100644 --- a/src/components/structures/auth/Registration.js +++ b/src/components/structures/auth/Registration.js @@ -344,12 +344,6 @@ module.exports = React.createClass({ case "RegistrationForm.ERR_MISSING_PHONE_NUMBER": errMsg = _t('A phone number is required to register on this homeserver.'); break; - case "RegistrationForm.ERR_USERNAME_INVALID": - errMsg = _t("A username can only contain lower case letters, numbers and '=_-./'"); - break; - case "RegistrationForm.ERR_USERNAME_BLANK": - errMsg = _t('You need to enter a username.'); - break; default: console.error("Unknown error code: %s", errCode); errMsg = _t('An unknown error occurred.'); diff --git a/src/components/views/auth/RegistrationForm.js b/src/components/views/auth/RegistrationForm.js index c680d058c5..1f30af3710 100644 --- a/src/components/views/auth/RegistrationForm.js +++ b/src/components/views/auth/RegistrationForm.js @@ -94,7 +94,6 @@ module.exports = React.createClass({ this.validateField(FIELD_EMAIL, ev.type); this.validateField(FIELD_PASSWORD_CONFIRM, ev.type); this.validateField(FIELD_PASSWORD, ev.type); - this.validateField(FIELD_USERNAME, ev.type); const allFieldsValid = this.verifyFieldsBeforeSubmit(); if (!allFieldsValid) { @@ -142,23 +141,38 @@ module.exports = React.createClass({ }, verifyFieldsBeforeSubmit() { - if (this.allFieldsValid()) { - return true; - } - - const invalidField = this.findFirstInvalidField([ + const fieldIDsInDisplayOrder = [ FIELD_USERNAME, FIELD_PASSWORD, FIELD_PASSWORD_CONFIRM, FIELD_EMAIL, FIELD_PHONE_NUMBER, - ]); + ]; + + // Run all fields with stricter validation that no longer allows empty + // values for required fields. + for (const fieldID of fieldIDsInDisplayOrder) { + const field = this[fieldID]; + if (!field) { + continue; + } + field.validate({ allowEmpty: false }); + } + + if (this.allFieldsValid()) { + return true; + } + + const invalidField = this.findFirstInvalidField(fieldIDsInDisplayOrder); if (!invalidField) { return true; } + // Focus the first invalid field and show feedback in the stricter mode + // that no longer allows empty values for required fields. invalidField.focus(); + invalidField.validate({ allowEmpty: false, focused: true }); return false; }, @@ -215,21 +229,6 @@ module.exports = React.createClass({ } else this.markFieldError(fieldID, phoneNumberValid, "RegistrationForm.ERR_PHONE_NUMBER_INVALID"); break; } - case FIELD_USERNAME: { - const username = this.state.username; - if (allowEmpty && username === '') { - this.markFieldError(fieldID, true); - } else if (username == '') { - this.markFieldError( - fieldID, - false, - "RegistrationForm.ERR_USERNAME_BLANK", - ); - } else { - this.markFieldError(fieldID, true); - } - break; - } case FIELD_PASSWORD: if (allowEmpty && pwd1 === "") { this.markFieldError(fieldID, true); @@ -358,9 +357,14 @@ module.exports = React.createClass({ validateUsernameRules: withValidation({ description: () => _t("Use letters, numbers, dashes and underscores only"), rules: [ + { + key: "required", + test: ({ value, allowEmpty }) => allowEmpty || !!value, + invalid: () => _t("Enter username"), + }, { key: "safeLocalpart", - regex: SAFE_LOCALPART_REGEX, + test: ({ value }) => !value || SAFE_LOCALPART_REGEX.test(value), invalid: () => _t("Some characters not allowed"), }, ], @@ -393,7 +397,6 @@ module.exports = React.createClass({ renderUsername() { const Field = sdk.getComponent('elements.Field'); return this[FIELD_USERNAME] = field} type="text" diff --git a/src/components/views/elements/Field.js b/src/components/views/elements/Field.js index dfe3a51697..6eba832523 100644 --- a/src/components/views/elements/Field.js +++ b/src/components/views/elements/Field.js @@ -87,14 +87,15 @@ export default class Field extends React.PureComponent { this.input.focus(); } - validate({ focused }) { + validate({ focused, allowEmpty = true }) { if (!this.props.onValidate) { return; } - const { value } = this.input; + const value = this.input ? this.input.value : null; const { valid, feedback } = this.props.onValidate({ value, focused, + allowEmpty, }); this.setState({ valid, diff --git a/src/components/views/elements/Validation.js b/src/components/views/elements/Validation.js index 8fc584edce..8142dfcd65 100644 --- a/src/components/views/elements/Validation.js +++ b/src/components/views/elements/Validation.js @@ -26,7 +26,7 @@ import classNames from 'classnames'; * An array of rules describing how to check to input value. Each rule in an object * and may have the following properties: * - `key`: A unique ID for the rule. Required. - * - `regex`: A regex used to determine the rule's current validity. Required. + * - `test`: A function used to determine the rule's current validity. Required. * - `valid`: Function returning text to show when the rule is valid. Only shown if set. * - `invalid`: Function returning text to show when the rule is invalid. Only shown if set. * @returns {Function} @@ -34,9 +34,9 @@ import classNames from 'classnames'; * the overall validity and a feedback UI that can be rendered for more detail. */ export default function withValidation({ description, rules }) { - return function onValidate({ value, focused }) { + return function onValidate({ value, focused, allowEmpty = true }) { // TODO: Re-run only after ~200ms of inactivity - if (!value) { + if (!value && allowEmpty) { return { valid: null, feedback: null, @@ -47,10 +47,10 @@ export default function withValidation({ description, rules }) { let valid = true; if (rules && rules.length) { for (const rule of rules) { - if (!rule.key || !rule.regex) { + if (!rule.key || !rule.test) { continue; } - const ruleValid = rule.regex.test(value); + const ruleValid = rule.test({ value, allowEmpty }); valid = valid && ruleValid; if (ruleValid && rule.valid) { // If the rule's result is valid and has text to show for diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 51967c39f1..bafb516af7 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1324,6 +1324,7 @@ "If you don't specify an email address, you won't be able to reset your password. Are you sure?": "If you don't specify an email address, you won't be able to reset your password. Are you sure?", "Use letters, numbers, dashes and underscores only": "Use letters, numbers, dashes and underscores only", "Some characters not allowed": "Some characters not allowed", + "Enter username": "Enter username", "Create your Matrix account": "Create your Matrix account", "Create your Matrix account on %(serverName)s": "Create your Matrix account on %(serverName)s", "Email (optional)": "Email (optional)", @@ -1524,7 +1525,6 @@ "This doesn't look like a valid phone number.": "This doesn't look like a valid phone number.", "An email address is required to register on this homeserver.": "An email address is required to register on this homeserver.", "A phone number is required to register on this homeserver.": "A phone number is required to register on this homeserver.", - "You need to enter a username.": "You need to enter a username.", "An unknown error occurred.": "An unknown error occurred.", "Create your account": "Create your account", "Commands": "Commands",