From 265d774b4f149f0fafe6f6e77b6e384967710c98 Mon Sep 17 00:00:00 2001 From: Cadence Ember Date: Wed, 21 Oct 2020 19:33:36 +1300 Subject: [PATCH] Some login functionality --- spec.js | 5 ++ src/js/login.js | 114 ++++++++++++++++++++++++++++++++++++++++++++ src/login.pug | 13 ++--- src/sass/login.sass | 31 +++++++----- 4 files changed, 145 insertions(+), 18 deletions(-) create mode 100644 src/js/login.js diff --git a/spec.js b/spec.js index 600af14..a488e5e 100644 --- a/spec.js +++ b/spec.js @@ -84,6 +84,11 @@ module.exports = [ source: "/js/functions.js", target: "/static/functions.js", }, + { + type: "js", + source: "/js/login.js", + target: "/static/login.js", + }, { type: "file", source: "/assets/fonts/whitney-500.woff", diff --git a/src/js/login.js b/src/js/login.js new file mode 100644 index 0000000..d3baf3a --- /dev/null +++ b/src/js/login.js @@ -0,0 +1,114 @@ +import {q, ElemJS} from $to_relative "/js/basic.js" + +const password = q("#password") +const homeserver = q("#homeserver") + +class Username extends ElemJS { + constructor() { + super(q("#username")) + + this.on("change", this.updateServer.bind(this)) + } + + isValid() { + return !!this.element.value.match(/^@?[a-z0-9._=\/-]+(?::[a-zA-Z0-9.:\[\]-]+)?$/) + } + + getUsername() { + return this.element.value.match(/^@?([a-z0-9._=\/-]+)/)[1] + } + + getServer() { + const server = this.element.value.match(/^@?[a-z0-9._=\?-]+:([a-zA-Z0-9.:\[\]-]+)$/) + if (server && server[1]) return server[1] + else return null + } + + updateServer() { + if (!this.isValid()) return + if (this.getServer()) homeserver.value = this.getServer() + } +} + +const username = new Username() + +class Form extends ElemJS { + constructor() { + super(q("#form")) + + this.processing = false + + this.on("submit", this.submit.bind(this)) + } + + async submit() { + if (this.processing) return + this.processing = true + if (!username.isValid()) return this.cancel("Username is not valid.") + + // Resolve homeserver address + let currentAddress = homeserver.value + let ok = false + while (!ok) { + if (!currentAddress.match(/^https?:\/\//)) currentAddress = "https://" + currentAddress + currentAddress = currentAddress.replace(/\/*$/, "") + this.status(`Looking up homeserver... trying ${currentAddress}`) + try { + // check if we found the actual matrix server + const exists = await fetch(`${currentAddress}/_matrix/client/r0`).then(res => res.json()) + if (exists.errcode === "M_UNRECOGNIZED") { + ok = true + break + } + // find the next matrix server in the chain + const root = await fetch(`${currentAddress}/.well-known/matrix/client`).then(res => res.json()) + let nextAddress = root["m.homeserver"].base_url + nextAddress = nextAddress.replace(/\/*$/, "") + if (currentAddress === nextAddress) { + ok = true + } + currentAddress = nextAddress + } catch (e) { + return this.cancel(`Failed to look up server ${currentAddress}`) + } + } + + // Request access token + this.status("Logging in...") + const root = await fetch(`${currentAddress}/_matrix/client/r0/login`, { + method: "POST", + body: JSON.stringify({ + type: "m.login.password", + user: username.getUsername(), + password: password.value + }) + }).then(res => res.json()) + + if (!root.access_token) { + if (root.error) { + this.cancel(`Server said: ${root.error}`) + } else { + this.cancel("Login mysteriously failed.") + console.error(root) + } + return + } + + localStorage.setItem("mx_user_id", root.user_id) + localStorage.setItem("domain", currentAddress) + localStorage.setItem("access_token", root.access_token) + + location.assign("./") + } + + status(message) { + //TODO: display the message + } + + cancel(message) { + this.processing = false + //TODO: display the message + } +} + +const form = new Form() diff --git a/src/login.pug b/src/login.pug index 13acefd..4e86f06 100644 --- a/src/login.pug +++ b/src/login.pug @@ -2,19 +2,20 @@ doctype html html head meta(charset="utf-8") - link(rel="stylesheet" type="text/css" href=getStatic("/sass/login.sass")) title Carbon meta(name="viewport" content="width=device-width, initial-scale=1") + link(rel="stylesheet" type="text/css" href=getStatic("/sass/login.sass")) + script(type="module" src=getStatic("/js/login.js")) body main.main .center-login-container h1 Welcome to the Matrix! - form.login-form + form.login-form(method="post" onsubmit="return false")#form .data-input .form-input-container - label(for="login") Username - input(type="text" name="login" autocomplete="username" required)#login + label(for="username") Username + input(type="text" name="username" autocomplete="username" placeholder="@username:server.tld" pattern="^@?[a-z0-9._=/-]+(?::[a-zA-Z0-9.:\\[\\]-]+)?$" required)#username .form-input-container label(for="password") Password @@ -22,7 +23,7 @@ html .form-input-container label(for="homeserver") Homeserver - input(type="text" name="homeserver" value="matrix.org" required)#homeserver + input(type="text" name="homeserver" value="matrix.org" placeholder="matrix.org" required)#homeserver .form-input-container - input(type="submit" value="Login") + input(type="submit" value="Log in")#submit diff --git a/src/sass/login.sass b/src/sass/login.sass index 92f443f..77144bc 100644 --- a/src/sass/login.sass +++ b/src/sass/login.sass @@ -10,10 +10,10 @@ flex-flow: column justify-content: center align-items: center - width: min(100vw, 30rem) + width: min(100vw, 450px) padding: max(1rem,3vw) 2rem - height: 27rem - box-shadow: -2px 2px 10px c.$darkest + margin: 8px + box-shadow: 0px 2px 10px c.$darkest background-color: c.$darker border-radius: 5px @@ -34,23 +34,30 @@ flex-direction: column margin: 1em 0 -input +input, button + font-family: inherit + font-size: 17px + background-color: c.$mild + color: #eee width: 100% - height: 2.4rem border-radius: 5px box-sizing: border-box - transition: background-color 1s, border-color 1s - padding: 0px 1ch + transition: background-color 0.15s ease-out, border-color 0.15s ease-out + padding: 4px 9px border: 0px input[type="text"],input[type="password"] border: 3px solid transparent margin: 0.4em 0px - &:hover - border-color: c.$mild -input[type="submit"]:hover - background-color: c.$mild + &:hover, &:focus + border-color: c.$milder + +button, input[type="submit"] + padding: 7px + + &:hover + background-color: c.$milder label - font-size: 1.2em + font-size: 18px