/**
* 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 };
}