// coms/client/public/data.js // Data module: holds the in‐memory threads, notices and reply state // Map of all messages and notices by ID // Client’s own session ID let myId = null; const threads = new Map(); // Map of connected users by unique ID const users = new Map(); // Ordered list of root‐level IDs (messages and notices) const rootIds = []; // Negative counter to generate unique IDs for notices let noticeCounter = -1; // ID of the message we’re currently replying to (or null) let replyTo = null; // ID of the message currently focused for navigation (or null) let focusedId = null; /** * Set the focused message ID (or null to clear). * @param {number|null} id */ function setFocused(id) { focusedId = id; } /** * Get the currently focused message ID. * @returns {number|null} */ function getFocused() { return focusedId; } /** * Clear the current focus. */ function clearFocused() { focusedId = null; } // -- User management API --- /** * Register a user with their unique ID. * @param {number} id * @param {string} name */ function addUser(id, name) { users.set(id, name); } /** * Remove a user by their ID. * @param {number} id */ function removeUser(id) { users.delete(id); } /** * Update a user’s name. * @param {number} id * @param {string} name */ function updateUser(id, name) { users.set(id, name); } /** * Get a list of {id, name} objects for all users. * @returns {{id:number, name:string}[]} */ function getUsers() { return Array.from(users.entries()).map(([id, name]) => ({ id, name })); } /** * Get grouped display strings: e.g. ["Anon (3x)", "Alice (1x)"] * @returns {string[]} */ function getGroupedUsers() { const counts = {}; for (const name of users.values()) { counts[name] = (counts[name] || 0) + 1; } return Object.entries(counts).map(([name, count]) => { const nameSpan = `${new Option(name).innerHTML}`; return count > 1 ? `${nameSpan} (${count}x)` : nameSpan; }); } /** * Add a new chat message to the thread structure. * @param {{id: number, username: string, content: string, ts: number, parent?: number}} msg */ function addMessage({ id, username, content, ts, parent = null }) { threads.set(id, { id, username, content, ts, parent, children: [] }); if (parent !== null && threads.has(parent)) { threads.get(parent).children.push(id); } else { rootIds.push(id); } } /** * Insert a system notice as a root‐level thread item. * @param {string} content — HTML or plain text for the notice */ function addNotice(content) { const ts = Math.floor(Date.now() / 1000); const id = noticeCounter--; threads.set(id, { id, username: "", content, ts, parent: null, children: [], }); rootIds.push(id); } /** * Reset all data (useful for unit tests or full re‐initialization). */ function clearData() { threads.clear(); rootIds.length = 0; noticeCounter = -1; replyTo = null; } /** @returns {Map} The threads map. */ function getThreads() { return threads; } /** @returns {number[]} The ordered list of root‐level IDs. */ function getRootIds() { return rootIds; } /** * Set this client’s own session ID. * @param {number} id */ function setMyId(id) { myId = id; } /** * Get this client’s own session ID. * @returns {number|null} */ function getMyId() { return myId; } /** @returns {number|null} The current reply‐to ID. */ function getReplyTo() { return replyTo; } /** @param {number|null} id — Set the current reply‐to ID. */ function setReplyTo(id) { replyTo = id; } export { threads, rootIds, users, addMessage, addNotice, addUser, removeUser, updateUser, getUsers, getGroupedUsers, clearData, getThreads, getRootIds, getReplyTo, setReplyTo, getFocused, setFocused, clearFocused, setMyId, getMyId, };