diff --git a/changelog.d/9297.feature b/changelog.d/9297.feature
new file mode 100644
index 0000000000..a2d0b27da4
--- /dev/null
+++ b/changelog.d/9297.feature
@@ -0,0 +1 @@
+Further improvements to the user experience of registration via single sign-on.
diff --git a/synapse/res/templates/sso_auth_account_details.html b/synapse/res/templates/sso_auth_account_details.html
index 105063825a..36850a2d6a 100644
--- a/synapse/res/templates/sso_auth_account_details.html
+++ b/synapse/res/templates/sso_auth_account_details.html
@@ -18,6 +18,19 @@
font-size: 12px;
}
+ .username_input.invalid {
+ border-color: #FE2928;
+ }
+
+ .username_input.invalid input, .username_input.invalid label {
+ color: #FE2928;
+ }
+
+ .username_input div, .username_input input {
+ line-height: 18px;
+ font-size: 14px;
+ }
+
.username_input label {
position: absolute;
top: -8px;
@@ -78,6 +91,15 @@
display: block;
margin-top: 8px;
}
+
+ output {
+ padding: 0 14px;
+ display: block;
+ }
+
+ output.error {
+ color: #FE2928;
+ }
</style>
</head>
<body>
@@ -87,12 +109,13 @@
</header>
<main>
<form method="post" class="form__input" id="form">
- <div class="username_input">
+ <div class="username_input" id="username_input">
<label for="field-username">Username</label>
<div class="prefix">@</div>
- <input type="text" name="username" id="field-username" autofocus required pattern="[a-z0-9\-=_\/\.]+">
+ <input type="text" name="username" id="field-username" autofocus>
<div class="postfix">:{{ server_name }}</div>
</div>
+ <output for="username_input" id="field-username-output"></output>
<input type="submit" value="Continue" class="primary-button">
{% if user_attributes %}
<section class="idp-pick-details">
diff --git a/synapse/res/templates/sso_auth_account_details.js b/synapse/res/templates/sso_auth_account_details.js
index deef419bb6..3c45df9078 100644
--- a/synapse/res/templates/sso_auth_account_details.js
+++ b/synapse/res/templates/sso_auth_account_details.js
@@ -1,14 +1,24 @@
const usernameField = document.getElementById("field-username");
+const usernameOutput = document.getElementById("field-username-output");
+const form = document.getElementById("form");
+
+// needed to validate on change event when no input was changed
+let needsValidation = true;
+let isValid = false;
function throttle(fn, wait) {
let timeout;
- return function() {
+ const throttleFn = function() {
const args = Array.from(arguments);
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(fn.bind.apply(fn, [null].concat(args)), wait);
- }
+ };
+ throttleFn.cancelQueued = function() {
+ clearTimeout(timeout);
+ };
+ return throttleFn;
}
function checkUsernameAvailable(username) {
@@ -16,14 +26,14 @@ function checkUsernameAvailable(username) {
return fetch(check_uri, {
// include the cookie
"credentials": "same-origin",
- }).then((response) => {
+ }).then(function(response) {
if(!response.ok) {
// for non-200 responses, raise the body of the response as an exception
return response.text().then((text) => { throw new Error(text); });
} else {
return response.json();
}
- }).then((json) => {
+ }).then(function(json) {
if(json.error) {
return {message: json.error};
} else if(json.available) {
@@ -34,33 +44,49 @@ function checkUsernameAvailable(username) {
});
}
+const allowedUsernameCharacters = new RegExp("^[a-z0-9\\.\\_\\-\\/\\=]+$");
+const allowedCharactersString = "lowercase letters, digits, ., _, -, /, =";
+
+function reportError(error) {
+ throttledCheckUsernameAvailable.cancelQueued();
+ usernameOutput.innerText = error;
+ usernameOutput.classList.add("error");
+ usernameField.parentElement.classList.add("invalid");
+ usernameField.focus();
+}
+
function validateUsername(username) {
- usernameField.setCustomValidity("");
- if (usernameField.validity.valueMissing) {
- usernameField.setCustomValidity("Please provide a username");
- return;
+ isValid = false;
+ needsValidation = false;
+ usernameOutput.innerText = "";
+ usernameField.parentElement.classList.remove("invalid");
+ usernameOutput.classList.remove("error");
+ if (!username) {
+ return reportError("Please provide a username");
}
- if (usernameField.validity.patternMismatch) {
- usernameField.setCustomValidity("Invalid username, please only use " + allowedCharactersString);
- return;
+ if (username.length > 255) {
+ return reportError("Too long, please choose something shorter");
}
- usernameField.setCustomValidity("Checking if username is available …");
+ if (!allowedUsernameCharacters.test(username)) {
+ return reportError("Invalid username, please only use " + allowedCharactersString);
+ }
+ usernameOutput.innerText = "Checking if username is available …";
throttledCheckUsernameAvailable(username);
}
const throttledCheckUsernameAvailable = throttle(function(username) {
- const handleError = function(err) {
+ const handleError = function(err) {
// don't prevent form submission on error
- usernameField.setCustomValidity("");
- console.log(err.message);
+ usernameOutput.innerText = "";
+ isValid = true;
};
try {
checkUsernameAvailable(username).then(function(result) {
if (!result.available) {
- usernameField.setCustomValidity(result.message);
- usernameField.reportValidity();
+ reportError(result.message);
} else {
- usernameField.setCustomValidity("");
+ isValid = true;
+ usernameOutput.innerText = "";
}
}, handleError);
} catch (err) {
@@ -68,9 +94,23 @@ const throttledCheckUsernameAvailable = throttle(function(username) {
}
}, 500);
+form.addEventListener("submit", function(evt) {
+ if (needsValidation) {
+ validateUsername(usernameField.value);
+ evt.preventDefault();
+ return;
+ }
+ if (!isValid) {
+ evt.preventDefault();
+ usernameField.focus();
+ return;
+ }
+});
usernameField.addEventListener("input", function(evt) {
validateUsername(usernameField.value);
});
usernameField.addEventListener("change", function(evt) {
- validateUsername(usernameField.value);
+ if (needsValidation) {
+ validateUsername(usernameField.value);
+ }
});
|