Added code.
This commit is contained in:
148
client/public/app.js
Normal file
148
client/public/app.js
Normal file
@@ -0,0 +1,148 @@
|
||||
import {
|
||||
setReplyTo,
|
||||
getThreads,
|
||||
addNotice,
|
||||
updateUser,
|
||||
getGroupedUsers,
|
||||
getMyId,
|
||||
getUsers,
|
||||
getFocused,
|
||||
setFocused,
|
||||
clearFocused,
|
||||
} from "./data.js";
|
||||
import { renderChat } from "./render.js";
|
||||
import { initWebSocket } from "./wsModule.js";
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const chat = document.getElementById("chat");
|
||||
const inputContainer = document.getElementById("inputContainer");
|
||||
const rootButton = document.getElementById("rootButton");
|
||||
const sendButton = document.getElementById("sendButton");
|
||||
const input = document.getElementById("messageInput");
|
||||
const usersList = document.getElementById("userList");
|
||||
const nameInput = document.getElementById("nameInput");
|
||||
const nameButton = document.getElementById("nameButton");
|
||||
|
||||
chat.style.display = "";
|
||||
inputContainer.style.display = "flex";
|
||||
|
||||
const defaultName = "Anon";
|
||||
const MAX_NAME_LEN = 31;
|
||||
|
||||
const { socket, sendMessage, sendName } = initWebSocket(
|
||||
defaultName,
|
||||
chat,
|
||||
inputContainer,
|
||||
usersList,
|
||||
);
|
||||
|
||||
// Renames.
|
||||
nameButton.addEventListener("click", () => {
|
||||
const newName = nameInput.value.trim() || defaultName;
|
||||
if (newName.length > MAX_NAME_LEN) {
|
||||
addNotice(
|
||||
`<span class="err">Name may be at most ${MAX_NAME_LEN} characters.</span>`,
|
||||
);
|
||||
renderChat(chat, inputContainer);
|
||||
nameInput.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
sendName(newName);
|
||||
updateUser(getMyId(), newName);
|
||||
const groups = getGroupedUsers();
|
||||
usersList.innerHTML = "Online: " + groups.join(", ");
|
||||
|
||||
nameInput.value = "";
|
||||
nameInput.placeholder = `Name: ${newName}`;
|
||||
input.focus();
|
||||
});
|
||||
|
||||
// Enter to set name.
|
||||
nameInput.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Enter") nameButton.click();
|
||||
});
|
||||
|
||||
// Button to send.
|
||||
sendButton.addEventListener("click", () => {
|
||||
const text = input.value.trim();
|
||||
if (!text) return;
|
||||
sendMessage(text);
|
||||
input.value = "";
|
||||
input.focus();
|
||||
});
|
||||
|
||||
// Enter to send and escape to go to root.
|
||||
input.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Enter") {
|
||||
sendButton.click();
|
||||
} else if (e.key === "Escape") {
|
||||
setReplyTo(null);
|
||||
input.placeholder = "";
|
||||
renderChat(chat, inputContainer);
|
||||
input.focus();
|
||||
}
|
||||
});
|
||||
|
||||
// Click message to reply.
|
||||
chat.addEventListener("click", (e) => {
|
||||
const msgDiv = e.target.closest(".msg");
|
||||
if (!msgDiv) return;
|
||||
const id = Number(msgDiv.dataset.id);
|
||||
if (!isFinite(id)) return;
|
||||
const threads = getThreads();
|
||||
const author = threads.get(id)?.username;
|
||||
setReplyTo(id);
|
||||
setFocused(id);
|
||||
input.placeholder = author ? `Replying to @${author}` : "";
|
||||
renderChat(chat, inputContainer);
|
||||
input.focus();
|
||||
});
|
||||
|
||||
// Go back to root.
|
||||
rootButton.addEventListener("click", () => {
|
||||
setReplyTo(null);
|
||||
clearFocused();
|
||||
input.placeholder = "";
|
||||
renderChat(chat, inputContainer);
|
||||
input.focus();
|
||||
});
|
||||
|
||||
// Global Escape handler: clear reply context, focus input, and clear focused message.
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Escape") {
|
||||
setReplyTo(null);
|
||||
clearFocused();
|
||||
input.placeholder = "";
|
||||
renderChat(chat, inputContainer);
|
||||
input.focus();
|
||||
}
|
||||
});
|
||||
|
||||
// Arrow key navigation for focused messages (also sets reply target).
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (e.key === "ArrowDown" || e.key === "ArrowUp") {
|
||||
const msgs = Array.from(chat.querySelectorAll("div.msg"));
|
||||
if (!msgs.length) return;
|
||||
let idx = msgs.findIndex(
|
||||
(div) => Number(div.dataset.id) === getFocused(),
|
||||
);
|
||||
if (idx === -1) {
|
||||
idx = e.key === "ArrowDown" ? -1 : msgs.length;
|
||||
}
|
||||
idx =
|
||||
e.key === "ArrowDown"
|
||||
? Math.min(msgs.length - 1, idx + 1)
|
||||
: Math.max(0, idx - 1);
|
||||
const newId = Number(msgs[idx].dataset.id);
|
||||
setFocused(newId);
|
||||
setReplyTo(newId); // Move input box under focused message.
|
||||
const threads = getThreads();
|
||||
const author = threads.get(newId)?.username;
|
||||
input.placeholder = author ? `Replying to @${author}` : "";
|
||||
renderChat(chat, inputContainer);
|
||||
const newDiv = chat.querySelector(`div[data-id="${newId}"]`);
|
||||
if (newDiv) newDiv.scrollIntoView({ block: "nearest" });
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user