diff options
Diffstat (limited to 'static/js/chat.js')
| -rw-r--r-- | static/js/chat.js | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/static/js/chat.js b/static/js/chat.js new file mode 100644 index 0000000..69c169c --- /dev/null +++ b/static/js/chat.js @@ -0,0 +1,101 @@ +(function () { + const log = document.getElementById("log"); + const form = document.getElementById("chat-form"); + const input = document.getElementById("msg"); + const status = document.getElementById("status"); + const me = document.getElementById("me").textContent.trim(); + + let socket; + let reconnectDelay = 500; + + function el(tag, className, text) { + const node = document.createElement(tag); + if (className) node.className = className; + if (text !== undefined) node.textContent = text; + return node; + } + + function atBottom() { + return log.scrollHeight - log.scrollTop - log.clientHeight < 40; + } + + function append(node) { + const stick = atBottom(); + log.appendChild(node); + if (stick) log.scrollTop = log.scrollHeight; + } + + function fmtTime(ms) { + if (!ms) return ""; + const d = new Date(ms); + return d.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }); + } + + function renderChat(msg) { + const line = el("div", "chat-line"); + if (msg.nick === me) line.classList.add("mine"); + line.appendChild(el("span", "chat-nick", msg.nick)); + line.appendChild(el("span", "chat-body", " " + msg.body)); + line.appendChild(el("span", "chat-time", " " + fmtTime(msg.at))); + append(line); + } + + function renderSystem(msg) { + append(el("div", "chat-system", msg.body)); + } + + function handle(msg) { + switch (msg.type) { + case "history": + log.innerHTML = ""; + (msg.history || []).forEach(renderChat); + break; + case "chat": + renderChat(msg); + break; + case "system": + renderSystem(msg); + break; + } + } + + function connect() { + const proto = location.protocol === "https:" ? "wss" : "ws"; + socket = new WebSocket(`${proto}://${location.host}/ws`); + + socket.onopen = function () { + status.textContent = "connected ✓"; + status.className = "status ok"; + reconnectDelay = 500; + }; + + socket.onmessage = function (event) { + try { + handle(JSON.parse(event.data)); + } catch (e) { + console.error("bad message", e); + } + }; + + socket.onclose = function () { + status.textContent = "reconnecting…"; + status.className = "status"; + setTimeout(connect, reconnectDelay); + reconnectDelay = Math.min(reconnectDelay * 2, 8000); + }; + + socket.onerror = function () { + socket.close(); + }; + } + + form.addEventListener("submit", function (e) { + e.preventDefault(); + const body = input.value.trim(); + if (!body || !socket || socket.readyState !== WebSocket.OPEN) return; + socket.send(JSON.stringify({ type: "chat", body: body })); + input.value = ""; + }); + + connect(); +})(); |
