import { notifyNotice } from "./notifications.js";
// Client’s own session ID.
let myId = null;
// Map of all messages and notices by ID (string IDs).
const threads = new Map();
// Map of connected users by unique ID.
const users = new Map();
// Ordered list of root‐level IDs (messages and notices, stored as strings).
const rootIds = [];
// Negative counter to generate unique IDs for notices.
let noticeCounter = -1;
// ID of the message we’re currently replying to (string or null).
let replyTo = null;
// ID of the message currently focused for navigation (string or null).
let focusedId = null;
// Set the focused message ID (or null to clear).
function setFocused(id) {
focusedId = id;
}
// Get the currently focused message ID.
function getFocused() {
return focusedId;
}
function clearFocused() {
focusedId = null;
}
// USER MANAGEMENT STUFF.
/**
* Register a user with their unique ID.
* @param {string} id
* @param {string} name
*/
function addUser(id, name) {
users.set(id, name);
}
/**
* Remove a user by their ID.
* @param {string} id
*/
function removeUser(id) {
users.delete(id);
}
/**
* Update a user’s name.
* @param {string} id
* @param {string} name
*/
function updateUser(id, name) {
users.set(id, name);
}
/**
* Get a list of {id, name} objects for all users.
* @returns {{id:string, 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: string|number, username: string, content: string, ts: number, parent?: string|number|null, authorId?: string|number|null}} msg
*/
function addMessage({
id,
username,
content,
ts,
parent = null,
authorId = null,
}) {
const sid = String(id);
const sparent = parent === null ? null : String(parent);
threads.set(sid, {
id: sid,
username,
authorId: authorId === null ? null : String(authorId),
content,
ts,
parent: sparent,
children: [],
});
if (sparent !== null && threads.has(sparent)) {
threads.get(sparent).children.push(sid);
} else {
rootIds.push(sid);
}
}
/**
* Insert a system notice as a root‐level thread item.
* @param {string} content — HTML or plain text for the notice
* @param {boolean} [shouldNotify=true] — whether to trigger a browser notification
*/
function addNotice(content, shouldNotify = true) {
const ts = Math.floor(Date.now() / 1000);
const id = String(noticeCounter--);
threads.set(id, {
id,
username: "",
content,
ts,
parent: null,
children: [],
});
rootIds.push(id);
if (shouldNotify) notifyNotice(content);
}
/**
* Reset all data (useful for unit tests or full re‐initialization).
*/
function clearData() {
threads.clear();
rootIds.length = 0;
noticeCounter = -1;
replyTo = null;
users.clear();
}
/** @returns {Map} The threads map. */
function getThreads() {
return threads;
}
/** @returns {string[]} The ordered list of root‐level IDs. */
function getRootIds() {
return rootIds;
}
/**
* Set this client’s own session ID.
* @param {string|number} id
*/
function setMyId(id) {
myId = String(id);
}
/**
* Get this client’s own session ID.
* @returns {string|null}
*/
function getMyId() {
return myId;
}
/** @returns {string|null} The current reply‐to ID. */
function getReplyTo() {
return replyTo;
}
/** @param {string|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,
};