151 lines
4.5 KiB
JavaScript
151 lines
4.5 KiB
JavaScript
/**
|
|
* 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,
|
|
clearData,
|
|
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 <span class="name">${username}</span>`);
|
|
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;
|
|
clearData();
|
|
setMyId(myId);
|
|
const users = packet.data.users || [];
|
|
users.forEach(({ id, username }) => addUser(id, username));
|
|
const groups = getGroupedUsers();
|
|
usersListEl.innerHTML = "Online: " + groups.join(", ");
|
|
// Replay chat history.
|
|
const history = packet.data.history || [];
|
|
history.forEach((raw) => {
|
|
try {
|
|
const histPkt = JSON.parse(raw);
|
|
if (histPkt.type === "msg-event") {
|
|
const { id, username: u, content, ts, parent } = histPkt.data;
|
|
addMessage({ id, username: u, content, ts, parent });
|
|
}
|
|
} catch (e) {
|
|
console.error("Invalid history entry", raw);
|
|
}
|
|
});
|
|
//addNotice(`<i>Users online: ${groups.join(", ")}</i>`);
|
|
break;
|
|
}
|
|
case "join-event": {
|
|
// A new user joined.
|
|
const { id, username } = packet.data;
|
|
addUser(id, username);
|
|
addNotice(
|
|
`<span class="name">${new Option(username).innerHTML}</span> 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(
|
|
`<span class="name">${new Option(oldName).innerHTML}</span> changed name to <span class="name">${new Option(newName).innerHTML}</span>`,
|
|
);
|
|
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(
|
|
'<span class="err">WebSocket error. See console for details.</span>',
|
|
);
|
|
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 };
|
|
}
|