let socket; let username; let sessionToken; let nextId = 0; let pendingMessages = {}; async function init() { try { const res = await fetch("/api/session"); const data = await res.json(); if (data.authenticated) { username = data.username; sessionToken = data.token; showChat(); connectWebSocket(); } else { showLogin(); } } catch { showLogin(); } } function showLogin() { document.getElementById("auth-container").style.display = "block"; document.getElementById("login-form").style.display = "block"; document.getElementById("signup-form").style.display = "none"; document.getElementById("chat-app").style.display = "none"; document.getElementById("login-error").textContent = ""; } function showSignup() { document.getElementById("auth-container").style.display = "block"; document.getElementById("login-form").style.display = "none"; document.getElementById("signup-form").style.display = "block"; document.getElementById("chat-app").style.display = "none"; document.getElementById("signup-error").textContent = ""; } function showChat() { document.getElementById("auth-container").style.display = "none"; document.getElementById("chat-app").style.display = "block"; document.getElementById("display-username").textContent = username; document.getElementById("chat-window").innerHTML = ""; } async function handleLogin(e) { e.preventDefault(); const login = e.target.login.value; const password = e.target.password.value; const error = document.getElementById("login-error"); try { const res = await fetch("/api/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ login, password }), }); const data = await res.json(); if (res.ok) { username = data.username; sessionToken = data.token; showChat(); connectWebSocket(); } else { error.textContent = data.error; e.target.password.value = ""; } } catch { error.textContent = "Network error."; } } async function handleSignup(e) { e.preventDefault(); const email = e.target.email.value; const usernameVal = e.target.username.value; const password = e.target.password.value; const confirm = e.target.confirm_password.value; const error = document.getElementById("signup-error"); if (password !== confirm) { error.textContent = "Passwords do not match."; return; } try { const res = await fetch("/api/signup", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email, username: usernameVal, password }), }); const data = await res.json(); if (res.ok) { username = data.username; sessionToken = data.token; showChat(); connectWebSocket(); } else { error.textContent = data.error; } } catch { error.textContent = "Network error."; } } async function logout() { if (socket) { socket.close(); socket = null; } try { await fetch("/api/logout", { method: "POST" }); } catch {} username = ""; sessionToken = ""; showLogin(); } function connectWebSocket() { const protocol = window.location.protocol === "https:" ? "wss:" : "ws:"; const url = protocol + "//" + window.location.host + "/ws?token=" + encodeURIComponent(sessionToken); socket = new WebSocket(url); socket.onmessage = function (event) { const data = JSON.parse(event.data); const now = Date.now(); const win = document.getElementById("chat-window"); const el = document.createElement("div"); if (data.is_system) { el.style.fontStyle = "italic"; el.style.color = "#ffcc00"; el.textContent = "[System] " + data.msg; } else { let text = data.user + ": " + data.msg; if (data.user === username && pendingMessages[data.id] !== undefined) { const rtt = now - pendingMessages[data.id]; text += " (rtt: " + rtt + "ms)"; delete pendingMessages[data.id]; } else if (data.server_time) { const sp = now - data.server_time; text += " (sping: " + sp + "ms)"; } el.textContent = text; } win.appendChild(el); win.scrollTop = win.scrollHeight; }; socket.onclose = function (event) { if (event.code === 4002) { location.reload(); return; } const win = document.getElementById("chat-window"); if (win) { const el = document.createElement("div"); el.style.fontStyle = "italic"; el.style.color = "#e94560"; if (event.code === 4001) { el.textContent = "Session expired. Logging out..."; setTimeout(() => { logout(); }, 1500); } else { el.textContent = "Connection lost. Refresh the page."; } win.appendChild(el); } }; } function sendMessage() { if (!socket || socket.readyState !== WebSocket.OPEN) return; const input = document.getElementById("message-input"); const msg = input.value.trim(); if (!msg) return; const id = nextId++; pendingMessages[id] = Date.now(); socket.send(JSON.stringify({ msg, id })); input.value = ""; input.focus(); } init();