diff options
author | Richard van der Hoff <richard@matrix.org> | 2021-02-01 17:28:37 +0000 |
---|---|---|
committer | Richard van der Hoff <richard@matrix.org> | 2021-02-01 17:28:37 +0000 |
commit | 18ab35284a2270efe01815911885e45b0f743453 (patch) | |
tree | a0b6c0322233525e91b84e2b4c08800cfa9d7c18 /synapse/res | |
parent | Add phone home stats for encrypted messages. (#9283) (diff) | |
parent | Replace username picker with a template (#9275) (diff) | |
download | synapse-18ab35284a2270efe01815911885e45b0f743453.tar.xz |
Merge branch 'social_login' into develop
Diffstat (limited to 'synapse/res')
-rw-r--r-- | synapse/res/templates/sso.css | 83 | ||||
-rw-r--r-- | synapse/res/templates/sso_auth_account_details.html | 115 | ||||
-rw-r--r-- | synapse/res/templates/sso_auth_account_details.js | 76 | ||||
-rw-r--r-- | synapse/res/templates/sso_redirect_confirm.html | 32 | ||||
-rw-r--r-- | synapse/res/username_picker/index.html | 19 | ||||
-rw-r--r-- | synapse/res/username_picker/script.js | 95 | ||||
-rw-r--r-- | synapse/res/username_picker/style.css | 27 |
7 files changed, 301 insertions, 146 deletions
diff --git a/synapse/res/templates/sso.css b/synapse/res/templates/sso.css new file mode 100644 index 0000000000..ff9dc94032 --- /dev/null +++ b/synapse/res/templates/sso.css @@ -0,0 +1,83 @@ +body { + font-family: "Inter", "Helvetica", "Arial", sans-serif; + font-size: 14px; + color: #17191C; +} + +header { + max-width: 480px; + width: 100%; + margin: 24px auto; + text-align: center; +} + +header p { + color: #737D8C; + line-height: 24px; +} + +h1 { + font-size: 24px; +} + +h2 { + font-size: 14px; +} + +h2 img { + vertical-align: middle; + margin-right: 8px; + width: 24px; + height: 24px; +} + +label { + cursor: pointer; +} + +main { + max-width: 360px; + width: 100%; + margin: 24px auto; +} + +.primary-button { + border: none; + text-decoration: none; + padding: 12px; + color: white; + background-color: #418DED; + font-weight: bold; + display: block; + border-radius: 12px; + width: 100%; + margin: 16px 0; + cursor: pointer; + text-align: center; +} + +.profile { + display: flex; + justify-content: center; + margin: 24px 0; +} + +.profile .avatar { + width: 36px; + height: 36px; + border-radius: 100%; + display: block; + margin-right: 8px; +} + +.profile .display-name { + font-weight: bold; + margin-bottom: 4px; +} +.profile .user-id { + color: #737D8C; +} + +.profile .display-name, .profile .user-id { + line-height: 18px; +} \ No newline at end of file diff --git a/synapse/res/templates/sso_auth_account_details.html b/synapse/res/templates/sso_auth_account_details.html new file mode 100644 index 0000000000..f22b09aec1 --- /dev/null +++ b/synapse/res/templates/sso_auth_account_details.html @@ -0,0 +1,115 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <title>Synapse Login</title> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, user-scalable=no"> + <style type="text/css"> + {% include "sso.css" without context %} + + .username_input { + display: flex; + border: 2px solid #418DED; + border-radius: 8px; + padding: 12px; + position: relative; + margin: 16px 0; + align-items: center; + font-size: 12px; + } + + .username_input label { + position: absolute; + top: -8px; + left: 14px; + font-size: 80%; + background: white; + padding: 2px; + } + + .username_input input { + flex: 1; + display: block; + min-width: 0; + border: none; + } + + .username_input div { + color: #8D99A5; + } + + .idp-pick-details { + border: 1px solid #E9ECF1; + border-radius: 8px; + margin: 24px 0; + } + + .idp-pick-details h2 { + margin: 0; + padding: 8px 12px; + } + + .idp-pick-details .idp-detail { + border-top: 1px solid #E9ECF1; + padding: 12px; + } + + .idp-pick-details .use, .idp-pick-details .idp-value { + color: #737D8C; + } + + .idp-pick-details .idp-value { + margin: 0; + margin-top: 8px; + } + + .idp-pick-details .avatar { + width: 53px; + height: 53px; + border-radius: 100%; + display: block; + margin-top: 8px; + } + </style> + </head> + <body> + <header> + <h1>Your account is nearly ready</h1> + <p>Check your details before creating an account on {{ server_name }}</p> + </header> + <main> + <form method="post" class="form__input" id="form"> + <div class="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\-=_\/\.]+"> + <div class="postfix">:{{ server_name }}</div> + </div> + <input type="submit" value="Continue" class="primary-button"> + {% if user_attributes %} + <section class="idp-pick-details"> + <h2><img src="{{ idp.idp_icon | mxc_to_http(24, 24) }}"/>Information from {{ idp.idp_name }}</h2> + {% if user_attributes.avatar_url %} + <div class="idp-detail idp-avatar"> + <img src="{{ user_attributes.avatar_url }}" class="avatar" /> + </div> + {% endif %} + {% if user_attributes.display_name %} + <div class="idp-detail"> + <p class="idp-value">{{ user_attributes.display_name }}</p> + </div> + {% endif %} + {% for email in user_attributes.emails %} + <div class="idp-detail"> + <p class="idp-value">{{ email }}</p> + </div> + {% endfor %} + </section> + {% endif %} + </form> + </main> + <script type="text/javascript"> + {% include "sso_auth_account_details.js" without context %} + </script> + </body> +</html> diff --git a/synapse/res/templates/sso_auth_account_details.js b/synapse/res/templates/sso_auth_account_details.js new file mode 100644 index 0000000000..deef419bb6 --- /dev/null +++ b/synapse/res/templates/sso_auth_account_details.js @@ -0,0 +1,76 @@ +const usernameField = document.getElementById("field-username"); + +function throttle(fn, wait) { + let timeout; + return function() { + const args = Array.from(arguments); + if (timeout) { + clearTimeout(timeout); + } + timeout = setTimeout(fn.bind.apply(fn, [null].concat(args)), wait); + } +} + +function checkUsernameAvailable(username) { + let check_uri = 'check?username=' + encodeURIComponent(username); + return fetch(check_uri, { + // include the cookie + "credentials": "same-origin", + }).then((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) => { + if(json.error) { + return {message: json.error}; + } else if(json.available) { + return {available: true}; + } else { + return {message: username + " is not available, please choose another."}; + } + }); +} + +function validateUsername(username) { + usernameField.setCustomValidity(""); + if (usernameField.validity.valueMissing) { + usernameField.setCustomValidity("Please provide a username"); + return; + } + if (usernameField.validity.patternMismatch) { + usernameField.setCustomValidity("Invalid username, please only use " + allowedCharactersString); + return; + } + usernameField.setCustomValidity("Checking if username is available …"); + throttledCheckUsernameAvailable(username); +} + +const throttledCheckUsernameAvailable = throttle(function(username) { + const handleError = function(err) { + // don't prevent form submission on error + usernameField.setCustomValidity(""); + console.log(err.message); + }; + try { + checkUsernameAvailable(username).then(function(result) { + if (!result.available) { + usernameField.setCustomValidity(result.message); + usernameField.reportValidity(); + } else { + usernameField.setCustomValidity(""); + } + }, handleError); + } catch (err) { + handleError(err); + } +}, 500); + +usernameField.addEventListener("input", function(evt) { + validateUsername(usernameField.value); +}); +usernameField.addEventListener("change", function(evt) { + validateUsername(usernameField.value); +}); diff --git a/synapse/res/templates/sso_redirect_confirm.html b/synapse/res/templates/sso_redirect_confirm.html index a45a50916c..d1328a6969 100644 --- a/synapse/res/templates/sso_redirect_confirm.html +++ b/synapse/res/templates/sso_redirect_confirm.html @@ -3,12 +3,34 @@ <head> <meta charset="UTF-8"> <title>SSO redirect confirmation</title> + <meta name="viewport" content="width=device-width, user-scalable=no"> + <style type="text/css"> + {% include "sso.css" without context %} + </style> </head> <body> - <p>The application at <span style="font-weight:bold">{{ display_url }}</span> is requesting full access to your <span style="font-weight:bold">{{ server_name }}</span> Matrix account.</p> - <p>If you don't recognise this address, you should ignore this and close this tab.</p> - <p> - <a href="{{ redirect_url }}">I trust this address</a> - </p> + <header> + {% if new_user %} + <h1>Your account is now ready</h1> + <p>You've made your account on {{ server_name }}.</p> + {% else %} + <h1>Log in</h1> + {% endif %} + <p>Continue to confirm you trust <strong>{{ display_url }}</strong>.</p> + </header> + <main> + {% if user_profile.avatar_url %} + <div class="profile"> + <img src="{{ user_profile.avatar_url | mxc_to_http(64, 64) }}" class="avatar" /> + <div class="profile-details"> + {% if user_profile.display_name %} + <div class="display-name">{{ user_profile.display_name }}</div> + {% endif %} + <div class="user-id">{{ user_id }}</div> + </div> + </div> + {% endif %} + <a href="{{ redirect_url }}" class="primary-button">Continue</a> + </main> </body> </html> diff --git a/synapse/res/username_picker/index.html b/synapse/res/username_picker/index.html deleted file mode 100644 index 37ea8bb6d8..0000000000 --- a/synapse/res/username_picker/index.html +++ /dev/null @@ -1,19 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> - <head> - <title>Synapse Login</title> - <link rel="stylesheet" href="style.css" type="text/css" /> - </head> - <body> - <div class="card"> - <form method="post" class="form__input" id="form" action="submit"> - <label for="field-username">Please pick your username:</label> - <input type="text" name="username" id="field-username" autofocus=""> - <input type="submit" class="button button--full-width" id="button-submit" value="Submit"> - </form> - <!-- this is used for feedback --> - <div role=alert class="tooltip hidden" id="message"></div> - <script src="script.js"></script> - </div> - </body> -</html> diff --git a/synapse/res/username_picker/script.js b/synapse/res/username_picker/script.js deleted file mode 100644 index 416a7c6f41..0000000000 --- a/synapse/res/username_picker/script.js +++ /dev/null @@ -1,95 +0,0 @@ -let inputField = document.getElementById("field-username"); -let inputForm = document.getElementById("form"); -let submitButton = document.getElementById("button-submit"); -let message = document.getElementById("message"); - -// Submit username and receive response -function showMessage(messageText) { - // Unhide the message text - message.classList.remove("hidden"); - - message.textContent = messageText; -}; - -function doSubmit() { - showMessage("Success. Please wait a moment for your browser to redirect."); - - // remove the event handler before re-submitting the form. - delete inputForm.onsubmit; - inputForm.submit(); -} - -function onResponse(response) { - // Display message - showMessage(response); - - // Enable submit button and input field - submitButton.classList.remove('button--disabled'); - submitButton.value = "Submit"; -}; - -let allowedUsernameCharacters = RegExp("[^a-z0-9\\.\\_\\=\\-\\/]"); -function usernameIsValid(username) { - return !allowedUsernameCharacters.test(username); -} -let allowedCharactersString = "lowercase letters, digits, ., _, -, /, ="; - -function buildQueryString(params) { - return Object.keys(params) - .map(k => encodeURIComponent(k) + '=' + encodeURIComponent(params[k])) - .join('&'); -} - -function submitUsername(username) { - if(username.length == 0) { - onResponse("Please enter a username."); - return; - } - if(!usernameIsValid(username)) { - onResponse("Invalid username. Only the following characters are allowed: " + allowedCharactersString); - return; - } - - // if this browser doesn't support fetch, skip the availability check. - if(!window.fetch) { - doSubmit(); - return; - } - - let check_uri = 'check?' + buildQueryString({"username": username}); - fetch(check_uri, { - // include the cookie - "credentials": "same-origin", - }).then((response) => { - if(!response.ok) { - // for non-200 responses, raise the body of the response as an exception - return response.text().then((text) => { throw text; }); - } else { - return response.json(); - } - }).then((json) => { - if(json.error) { - throw json.error; - } else if(json.available) { - doSubmit(); - } else { - onResponse("This username is not available, please choose another."); - } - }).catch((err) => { - onResponse("Error checking username availability: " + err); - }); -} - -function clickSubmit() { - event.preventDefault(); - if(submitButton.classList.contains('button--disabled')) { return; } - - // Disable submit button and input field - submitButton.classList.add('button--disabled'); - - // Submit username - submitButton.value = "Checking..."; - submitUsername(inputField.value); -}; - -inputForm.onsubmit = clickSubmit; diff --git a/synapse/res/username_picker/style.css b/synapse/res/username_picker/style.css deleted file mode 100644 index 745bd4c684..0000000000 --- a/synapse/res/username_picker/style.css +++ /dev/null @@ -1,27 +0,0 @@ -input[type="text"] { - font-size: 100%; - background-color: #ededf0; - border: 1px solid #fff; - border-radius: .2em; - padding: .5em .9em; - display: block; - width: 26em; -} - -.button--disabled { - border-color: #fff; - background-color: transparent; - color: #000; - text-transform: none; -} - -.hidden { - display: none; -} - -.tooltip { - background-color: #f9f9fa; - padding: 1em; - margin: 1em 0; -} - |