add user session reclaiming

In its current state the implementation should allow a user to resume
their session if the websocket connection is reset, for whatever reason.
This could be expanded to allow session sharing (multiple agents logging
in to the same client), or manual session resume via some sort of
password (encode uid & key to some pass-phrase kinda thing, or QR code).
This commit is contained in:
ducklet 2021-01-31 00:19:35 +01:00
parent 476f3d7a49
commit 4908b1fc6e
10 changed files with 376 additions and 77 deletions

View file

@ -3,6 +3,7 @@
/* global document, window */
const crypto = window.crypto
const location = document.location
const performance = window.performance
const storage = window.sessionStorage
// TODOs
@ -18,17 +19,16 @@ const buzzer_key = {
const q = (selector, root) => (root || document).querySelector(selector)
const on = (event, cb) => document.addEventListener(event, cb)
function node(type, { appendTo, cls, text, data, ...attrs } = {}) {
let elem = document.createElement(type)
function node(type, { appendTo, cls, text, data, style, ...attrs } = {}) {
let elem = typeof type === "string" ? document.createElement(type) : type
if (cls) {
elem.className = cls
}
if (text) {
elem.textContent = text
}
for (const name in data ?? {}) {
elem.dataset[name] = data[name]
}
Object.assign(elem.dataset, data ?? {})
Object.assign(elem.style, style ?? {})
for (const name in attrs) {
elem.setAttribute(name, attrs[name])
}
@ -53,11 +53,13 @@ let socket,
session_key
function hide(e) {
q(`#${e}`).style.display = "none"
e = typeof e === "string" ? q(`#${e}`) : e
e.style.display = "none"
}
function show(e) {
q(`#${e}`).style.display = "block"
e = typeof e === "string" ? q(`#${e}`) : e
e.style.display = "block"
}
function session_id() {
@ -97,8 +99,9 @@ function redraw_clients(me, clients) {
return
}
clear(ul)
const player_tpl = q("template#player").content.firstElementChild
for (const c of clients) {
node("li", {
node(player_tpl.cloneNode(), {
text: c.name,
data: { cid: c.id },
appendTo: ul,
@ -180,18 +183,41 @@ function setup_ui() {
}
})
q("#username").addEventListener("change", (event) => {
send("name", event.target.value)
const username_el = q("#username")
if (storage["my_name"]) {
username_el.value = storage["my_name"]
}
username_el.addEventListener("change", (event) => {
set_name(event.target.value)
})
ul = q("#info ul")
}
function set_name(name) {
storage["my_name"] = name
send("name", name)
}
function session_url(sid) {
return `${config.wsurl}/${sid}`
}
function session_id_from_url(url) {
const wsurl = new URL(config.wsurl)
const match = RegExp(`${wsurl.pathname}/([^/]+)$`).exec(url)
return !match ? null : match[1]
}
function setup_ws() {
const sid = session_id()
socket = new WebSocket(`${config.wsurl}/quiz/${sid}`)
const credentials = { id: storage["my_uid"], key: storage["my_key"] }
socket = new WebSocket(`${session_url(sid)}`)
socket.addEventListener("open", function (event) {
send("name", q("#username").value)
if (sid === storage["my_sid"]) {
send("login", credentials)
}
set_name(q("#username").value)
})
socket.addEventListener("message", function (event) {
const msg = JSON.parse(event.data)
@ -199,9 +225,10 @@ function setup_ws() {
servertime = msg.value
toffset_ms = performance.now()
} else if (msg.type === "id") {
me = { id: msg.id, key: msg.key }
storage["my_id"] = me.id
me = { id: msg.id, key: msg.key, path: msg.path }
storage["my_uid"] = me.id
storage["my_key"] = me.key
storage["my_sid"] = session_id_from_url(me.path)
redraw_clients(me, clients)
} else if (msg.type === "session_key") {
session_key = { path: msg.path, key: msg.key }
@ -217,7 +244,7 @@ function setup_ws() {
clients = msg.value
redraw_clients(me, clients)
} else if (msg.type === "client") {
const client = msg.value
const client = { name: msg.name, id: msg.id, active: msg.active }
for (const c of clients) {
if (c.id === client.id) {
c.name = client.name
@ -227,6 +254,11 @@ function setup_ws() {
}
clients.push(client)
redraw_clients(me, clients)
} else if (msg.type === "error") {
console.error(`Error: ${msg.reason}`)
const errorbox = q("#error")
q("code", errorbox).textContent = JSON.stringify(msg, null, 2)
show(errorbox)
} else {
console.error(`Unknown message: ${event.data}`)
}