asdkjlfhlaskjdhf
This commit is contained in:
@@ -29,10 +29,8 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
primeNotifications();
|
primeNotifications();
|
||||||
|
|
||||||
const defaultName = "Anon";
|
const defaultName = "Anon";
|
||||||
const MAX_NAME_LEN = 15; // server limit is NAME_MAX_LENGTH-1 (15 chars)
|
const MAX_NAME_LEN = 15;
|
||||||
|
|
||||||
// Fetch WebSocket domain from the client server so deployments can point to a
|
|
||||||
// remote websocket host without rebuilding the bundle.
|
|
||||||
fetch("/ws-domain")
|
fetch("/ws-domain")
|
||||||
.then((res) => res.json())
|
.then((res) => res.json())
|
||||||
.then(({ domain }) => domain || "")
|
.then(({ domain }) => domain || "")
|
||||||
@@ -118,7 +116,8 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
input.focus();
|
input.focus();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Global Escape handler: clear reply context, focus input, and clear focused message.
|
// Global Escape handler: clear reply context, focus input, and clear
|
||||||
|
// focused message.
|
||||||
document.addEventListener("keydown", (e) => {
|
document.addEventListener("keydown", (e) => {
|
||||||
if (e.key === "Escape") {
|
if (e.key === "Escape") {
|
||||||
setReplyTo(null);
|
setReplyTo(null);
|
||||||
|
|||||||
@@ -1,50 +1,41 @@
|
|||||||
// coms/client/public/data.js
|
|
||||||
// Data module: holds the in‐memory threads, notices and reply state
|
|
||||||
|
|
||||||
import { notifyNotice } from "./notifications.js";
|
import { notifyNotice } from "./notifications.js";
|
||||||
|
|
||||||
// Map of all messages and notices by ID (string IDs)
|
// Client’s own session ID.
|
||||||
// Client’s own session ID
|
|
||||||
let myId = null;
|
let myId = null;
|
||||||
|
|
||||||
|
// Map of all messages and notices by ID (string IDs).
|
||||||
const threads = new Map();
|
const threads = new Map();
|
||||||
|
|
||||||
// Map of connected users by unique ID
|
// Map of connected users by unique ID.
|
||||||
const users = new Map();
|
const users = new Map();
|
||||||
|
|
||||||
// Ordered list of root‐level IDs (messages and notices, stored as strings)
|
// Ordered list of root‐level IDs (messages and notices, stored as strings).
|
||||||
const rootIds = [];
|
const rootIds = [];
|
||||||
|
|
||||||
// Negative counter to generate unique IDs for notices
|
// Negative counter to generate unique IDs for notices.
|
||||||
let noticeCounter = -1;
|
let noticeCounter = -1;
|
||||||
|
|
||||||
// ID of the message we’re currently replying to (string or null)
|
// ID of the message we’re currently replying to (string or null).
|
||||||
let replyTo = null;
|
let replyTo = null;
|
||||||
|
|
||||||
// ID of the message currently focused for navigation (string or null)
|
// ID of the message currently focused for navigation (string or null).
|
||||||
let focusedId = null;
|
let focusedId = null;
|
||||||
|
|
||||||
/**
|
// Set the focused message ID (or null to clear).
|
||||||
* Set the focused message ID (or null to clear).
|
|
||||||
* @param {string|null} id
|
|
||||||
*/
|
|
||||||
function setFocused(id) {
|
function setFocused(id) {
|
||||||
focusedId = id;
|
focusedId = id;
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* Get the currently focused message ID.
|
// Get the currently focused message ID.
|
||||||
* @returns {string|null}
|
|
||||||
*/
|
|
||||||
function getFocused() {
|
function getFocused() {
|
||||||
return focusedId;
|
return focusedId;
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* Clear the current focus.
|
|
||||||
*/
|
|
||||||
function clearFocused() {
|
function clearFocused() {
|
||||||
focusedId = null;
|
focusedId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- User management API ---
|
// USER MANAGEMENT STUFF.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a user with their unique ID.
|
* Register a user with their unique ID.
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
// Simple notification helpers for the chat client.
|
|
||||||
|
|
||||||
const APP_NAME = "COMS";
|
|
||||||
|
|
||||||
function supportsNotifications() {
|
function supportsNotifications() {
|
||||||
return typeof Notification !== "undefined";
|
return typeof Notification !== "undefined";
|
||||||
}
|
}
|
||||||
@@ -12,9 +8,6 @@ function stripHtml(input) {
|
|||||||
return div.textContent || div.innerText || "";
|
return div.textContent || div.innerText || "";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Ask for permission if not already granted/denied.
|
|
||||||
*/
|
|
||||||
function primeNotifications() {
|
function primeNotifications() {
|
||||||
if (!supportsNotifications()) return;
|
if (!supportsNotifications()) return;
|
||||||
if (Notification.permission === "default") {
|
if (Notification.permission === "default") {
|
||||||
@@ -34,14 +27,10 @@ function showNotification(title, body) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Notify the user of a system notice.
|
|
||||||
* @param {string} content - HTML or text content of the notice.
|
|
||||||
*/
|
|
||||||
function notifyNotice(content) {
|
function notifyNotice(content) {
|
||||||
const text = stripHtml(content);
|
const text = stripHtml(content);
|
||||||
if (!text) return;
|
if (!text) return;
|
||||||
showNotification(`${APP_NAME} notice`, text);
|
showNotification(`COMS`, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -53,7 +42,7 @@ function notifyDirectReply(authorName, content) {
|
|||||||
const text = stripHtml(content);
|
const text = stripHtml(content);
|
||||||
if (!text) return;
|
if (!text) return;
|
||||||
const name = authorName || "Anon";
|
const name = authorName || "Anon";
|
||||||
showNotification(`${name} replied to your thread`, text);
|
showNotification(`${name} replied`, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
export { primeNotifications, notifyNotice, notifyDirectReply };
|
export { primeNotifications, notifyNotice, notifyDirectReply };
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
// Render module: handles chat UI rendering of messages and notices
|
// this one sucked.
|
||||||
import { getThreads, getRootIds, getReplyTo, getFocused } from "./data.js";
|
import { getThreads, getRootIds, getReplyTo, getFocused } from "./data.js";
|
||||||
|
|
||||||
/**
|
// Render the chat history and input container into the provided chat element.
|
||||||
* Render the chat history and input container into the provided chat element.
|
|
||||||
* @param {HTMLElement} chatEl - The container element for chat entries.
|
|
||||||
* @param {HTMLElement} inputContainer - The message input area to append at bottom.
|
|
||||||
*/
|
|
||||||
export function renderChat(chatEl, inputContainer) {
|
export function renderChat(chatEl, inputContainer) {
|
||||||
// Clear existing content.
|
// Clear existing content.
|
||||||
chatEl.innerHTML = "";
|
chatEl.innerHTML = "";
|
||||||
@@ -49,7 +45,7 @@ export function renderChat(chatEl, inputContainer) {
|
|||||||
const shortTimestamp = `${h}:${m} ${ampm}`;
|
const shortTimestamp = `${h}:${m} ${ampm}`;
|
||||||
const tsSpan = `<span class="ts" title="${fullTimestamp}">${shortTimestamp}</span>`;
|
const tsSpan = `<span class="ts" title="${fullTimestamp}">${shortTimestamp}</span>`;
|
||||||
|
|
||||||
// Distinguish notice vs normal message.
|
// Distinguish notices and normal messages.
|
||||||
if (msg.username) {
|
if (msg.username) {
|
||||||
div.classList.add("msg");
|
div.classList.add("msg");
|
||||||
if (getFocused() === id) div.classList.add("focused");
|
if (getFocused() === id) div.classList.add("focused");
|
||||||
@@ -59,7 +55,7 @@ export function renderChat(chatEl, inputContainer) {
|
|||||||
div.innerHTML = `${tsSpan} ${msg.content}`;
|
div.innerHTML = `${tsSpan} ${msg.content}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append to chat and render children.
|
// Append to chat and render the children.
|
||||||
chatEl.appendChild(div);
|
chatEl.appendChild(div);
|
||||||
msg.children.forEach((childId) => renderNode(childId, depth + 1));
|
msg.children.forEach((childId) => renderNode(childId, depth + 1));
|
||||||
}
|
}
|
||||||
@@ -91,14 +87,12 @@ export function renderChat(chatEl, inputContainer) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scroll to bottom.
|
// Scroll to bottom. Or at least try. Let the browser know that is our
|
||||||
|
// intention. May or may not actually work.
|
||||||
chatEl.scrollTop = chatEl.scrollHeight;
|
chatEl.scrollTop = chatEl.scrollHeight;
|
||||||
const input = inputContainer.querySelector("input");
|
const input = inputContainer.querySelector("input");
|
||||||
if (input) {
|
if (input) {
|
||||||
input.focus();
|
input.focus();
|
||||||
// Ensure inputContainer margin resets if at root.
|
if (replyTo === null) inputContainer.style.marginLeft = "0";
|
||||||
if (replyTo === null) {
|
|
||||||
inputContainer.style.marginLeft = "0";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,3 @@
|
|||||||
/**
|
|
||||||
* coms/client/public/wsModule.js
|
|
||||||
* WebSocket module: handles connection, dispatching incoming packets to
|
|
||||||
* data and render modules, and provides a sendMessage API.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
addMessage,
|
addMessage,
|
||||||
addNotice,
|
addNotice,
|
||||||
|
|||||||
Reference in New Issue
Block a user