/** * coms/client/public/wsModule.js * WebSocket module: handles connection, dispatching incoming packets to * data and render modules, and provides a sendMessage API. */ import { addMessage, addNotice, getReplyTo, addUser, updateUser, getGroupedUsers, setMyId, users, setFocused, setReplyTo, } from "./data.js"; import { renderChat } from "./render.js"; /** * Initialize WebSocket connection and wire up event handlers. * * @param {string} username - The user's chosen name. * @param {HTMLElement} chatEl - The container element for chat entries. * @param {HTMLElement} inputContainer - The message input area element. * @param {HTMLElement} usersListEl - The element displaying the online users. * @returns {{ socket: WebSocket, sendMessage: (content: string) => void }} */ export function initWebSocket(username, chatEl, inputContainer, usersListEl) { const socket = new WebSocket( (location.protocol === "https:" ? "wss" : "ws") + "://" + location.host + "/ws", "coms", ); socket.onopen = () => { // Announce join. socket.send(JSON.stringify({ type: "join", data: { username } })); //addNotice(`Connected as ${username}`); renderChat(chatEl, inputContainer); }; socket.onmessage = (event) => { const packet = JSON.parse(event.data); switch (packet.type) { case "welcome": { // Initial user list. const myId = packet.data.you; setMyId(myId); const users = packet.data.users || []; users.forEach(({ id, username }) => addUser(id, username)); const groups = getGroupedUsers(); usersListEl.innerHTML = "Online: " + groups.join(", "); //addNotice(`Users online: ${groups.join(", ")}`); break; } case "join-event": { // A new user joined. const { id, username } = packet.data; addUser(id, username); addNotice( `${new Option(username).innerHTML} joined.`, ); const groups = getGroupedUsers(); usersListEl.innerHTML = "Online: " + groups.join(", "); break; } case "name-event": { // A user changed name. const { id, new: newName, old: oldName } = packet.data; updateUser(id, newName); addNotice( `${new Option(oldName).innerHTML} changed name to ${new Option(newName).innerHTML}`, ); const groups = getGroupedUsers(); usersListEl.innerHTML = "Online: " + groups.join(", "); break; } case "msg-event": { // A chat message arrived. const { id, username: u, content, ts, parent } = packet.data; addMessage({ id, username: u, content, ts, parent }); break; } case "msg-ack": { const { id } = packet.data; if (getReplyTo() === null) { setFocused(id); setReplyTo(id); } renderChat(chatEl, inputContainer); break; } default: console.warn("Unknown packet type:", packet.type); } renderChat(chatEl, inputContainer); }; socket.onerror = (err) => { console.error("WebSocket error:", err); addNotice( 'WebSocket error. See console for details.', ); renderChat(chatEl, inputContainer); }; socket.onclose = () => { addNotice("Disconnected."); renderChat(chatEl, inputContainer); }; /** * Send a chat message via WebSocket. * @param {string} content - The message text to send. */ function sendMessage(content) { const parent = getReplyTo(); socket.send(JSON.stringify({ type: "msg", data: { content, parent } })); } /** * Send a name change packet via WebSocket. * @param {string} username - The new username to set. */ function sendName(username) { socket.send(JSON.stringify({ type: "name", data: { username } })); } return { socket, sendMessage, sendName }; }