import "./chat.css"; import io, { Socket } from "socket.io-client"; import type { blockedUnBlocked } from "./types_front"; import type { ClientMessage, ClientProfil, ClientProfilPartial, } from "./types_front"; import type { User } from "@app/auth"; import { navigateTo } from "@app/routing"; import authHtml from "./chat.html?raw"; import { getUser } from "@app/auth"; import { listBuddies } from "./chatHelperFunctions/listBuddies"; import { getProfil } from "./chatHelperFunctions/getProfil"; import { addInviteMessage, addMessage } from "./chatHelperFunctions/addMessage"; import { broadcastMsg } from "./chatHelperFunctions/broadcastMsg"; import { openProfilePopup } from "./chatHelperFunctions/openProfilePopup"; import { actionBtnPopUpBlock } from "./chatHelperFunctions/actionBtnPopUpBlock"; import { actionBtnPongGames } from "./chatHelperFunctions/actionBtnPongGames"; import { windowStateHidden } from "./chatHelperFunctions/windowStateHidden"; import { blockUser } from "./chatHelperFunctions/blockUser"; import { parseCmdMsg } from "./chatHelperFunctions/parseCmdMsg"; import { actionBtnPopUpInvite } from "./chatHelperFunctions/actionBtnPopUpInvite"; import { waitSocketConnected } from "./chatHelperFunctions/waitSocketConnected"; import { connected } from "./chatHelperFunctions/connected"; import { quitChat } from "./chatHelperFunctions/quitChat"; import { openMessagePopup } from "./chatHelperFunctions/openMessagePopup"; import { windowStateVisable } from "./chatHelperFunctions/windowStateVisable"; import { cmdList } from "./chatHelperFunctions/cmdList"; import { actionBtnTTTGames } from "./chatHelperFunctions/actionBtnTTTGames"; import { showError } from "@app/toast"; const MAX_SYSTEM_MESSAGES = 10; let inviteMsgFlag: boolean = false; export let noGuestFlag: boolean = true; let keysPressed: Record = {}; declare module "ft_state" { interface State { chatSock?: Socket; } } class DivPrivate extends HTMLElement { constructor() { super(); } } customElements.define("div-private", DivPrivate); export function getSocket(): Socket { if (window.__state.chatSock === undefined) window.__state.chatSock = io(window.location.host, { path: "/api/chat/socket.io/", }) as any as Socket; return window.__state.chatSock; } const chatBox = document.getElementById("chatBox")!; chatBox.classList.add("hidden"); chatBox.innerHTML = authHtml; const bquit = document.getElementById("b-quit") as HTMLDivElement; const buddies = document.getElementById("div-buddies") as HTMLDivElement; const buttonMessage = document.getElementById("close-modal-message") ?? null; const buttonPro = document.getElementById("close-modal") ?? null; const chatButton = document.querySelector("#chatButton"); const chatMessageIn = document.querySelector("#chatMessageIn"); const chatWindow = document.querySelector("#t-chatbox")!; const clearText = document.getElementById("b-clear") as HTMLButtonElement; const gameMessage = document.getElementById("game-modal") ?? null; const myGames = document.getElementById("b-hGame") ?? null; const myTTTGames = document.getElementById("b-hTGame") ?? null; const modalmessage = document.getElementById("modal-message") ?? null; const noGuest = document.getElementById("noGuest") ?? null; const notify = document.getElementById("notify") ?? null; const overlay = document.querySelector("#overlay")!; const profilList = document.getElementById("profile-modal") ?? null; const sendButton = document.getElementById("b-send") as HTMLButtonElement; const sendtextbox = document.getElementById( "t-chat-window", ) as HTMLButtonElement; const systemWindow = document.getElementById("chat-system-box") as HTMLDivElement; function chatKeyToggle() { let anti_flicker_control = false; const chat_toggle_key = 'f2'; const chat_toggle_key2 = 'f1'; document.addEventListener("keydown", (event) => { if (event.repeat && keysPressed[chat_toggle_key] === true) { anti_flicker_control = true; return ; }; keysPressed[event.key.toLowerCase()] = true; }); document.addEventListener("keyup", (event) => { keysPressed[event.key.toLowerCase()] = false; if (event.key.toLowerCase() === chat_toggle_key) { anti_flicker_control = false; } }); setInterval( () => { if(keysPressed[chat_toggle_key] === true) { overlay.classList.remove("opacity-60"); chatBox.classList.add("hidden"); chatMessageIn?.classList.add("hidden"); chatMessageIn!.textContent = ''; } if (keysPressed[chat_toggle_key2] === true) { anti_flicker_control = false; chatBox.classList.remove("hidden"); overlay.classList.add("opacity-60"); chatMessageIn?.classList.add("hidden"); chatMessageIn!.textContent = ''; sendtextbox.focus(); } }, 1000/10); }; function initChatSocket() { let socket = getSocket(); if ( !chatBox || !chatMessageIn || !bquit || !buttonMessage || !buttonPro || !chatButton || !chatWindow || !clearText || !gameMessage || !myGames || !myTTTGames || !modalmessage || !noGuest || !notify || !overlay || !profilList || !sendButton || !sendtextbox || !systemWindow ) return showError("fatal error"); // Listen for the 'connect' event socket.on("connect", async () => { await waitSocketConnected(socket); const user = getUser()?.name; const userID = getUser()?.id; // Ensure we have a user AND socket is connected if (!user || !socket.connected || !noGuest) return; const message = { command: "", destination: "system-info", type: "chat", user, token: document.cookie ?? "", text: " has Just ARRIVED in the chat", timestamp: Date.now(), SenderWindowID: socket.id, SenderID: userID, }; socket.emit("message", JSON.stringify(message)); const guest = getUser()?.guest; if (guest) { noGuest.innerText = ""; } else { noGuest.innerText = "❤️"; } const userProfile: ClientProfil = { command: "@noguest", destination: "", type: "chat", timestamp: Date.now(), guestmsg: true, }; socket.emit("guestmsg", JSON.stringify(userProfile)); const messageElement = document.createElement("div"); messageElement.textContent = `${user}: is connected au server`; systemWindow.appendChild(messageElement); systemWindow.scrollTop = systemWindow.scrollHeight; }); chatMessageIn?.classList.add("hidden"); chatKeyToggle(); // Listen for messages from the server "MsgObjectServer" socket.on("MsgObjectServer", (data: { message: ClientMessage }) => { if (socket) { connected(socket); } if (chatWindow && data.message.destination === "") { chatMessageIn?.classList.remove("hidden"); chatMessageIn!.textContent = '🔵'; const messageElement = document.createElement("div"); messageElement.textContent = `${data.message.user}: ${data.message.text}`; chatWindow.appendChild(messageElement); chatWindow.scrollTop = chatWindow.scrollHeight; } if (chatWindow && data.message.destination === "privateMsg") { chatMessageIn?.classList.remove("hidden"); chatMessageIn!.textContent = '🔴'; const messageElement = document.createElement("div-private"); messageElement.textContent = `🔒${data.message.user}: ${data.message.text}`; chatWindow.appendChild(messageElement); chatWindow.scrollTop = chatWindow.scrollHeight; } if (chatWindow && data.message.destination === "inviteMsg") { chatMessageIn?.classList.remove("hidden"); chatMessageIn!.textContent = '🟢'; const messageElement = document.createElement("div-private"); const chatWindow = document.getElementById( "t-chatbox", ) as HTMLDivElement; messageElement.innerHTML = `🏓${data.message.SenderUserName}: ${data.message.innerHtml}`; chatWindow.appendChild(messageElement); chatWindow.scrollTop = chatWindow.scrollHeight; } if (systemWindow && data.message.destination === "system-info") { const messageElement = document.createElement("div"); messageElement.textContent = `${data.message.user}: ${data.message.text}`; // keep only last 10 while (systemWindow.children.length > MAX_SYSTEM_MESSAGES) { systemWindow.removeChild(systemWindow.firstChild!); } systemWindow.appendChild(messageElement); systemWindow.scrollTop = systemWindow.scrollHeight; } }); socket.on("profilMessage", (profil: ClientProfil) => { profil.SenderID = getUser()?.id ?? ""; profil.SenderName = getUser()?.name ?? ""; openProfilePopup(profil); socket.emit("isBlockdBtn", profil); socket.emit("check_Block_button", profil); actionBtnPopUpInvite(profil, socket); actionBtnPopUpBlock(profil, socket); actionBtnPongGames(profil, socket); actionBtnTTTGames(profil, socket); }); socket.on("blockUser", (blocked: ClientProfil) => { let icon = "⛔"; if (inviteMsgFlag) { const messageElement = document.createElement("div"); if (`${blocked.text}` === "I have un-blocked you") { icon = "💚"; } messageElement.innerText = `${icon}${blocked.SenderName}: ${blocked.text}`; chatWindow.appendChild(messageElement); chatWindow.scrollTop = chatWindow.scrollHeight; } }); socket.on("blockBtn", (data: blockedUnBlocked) => { const blockUserBtn = document.querySelector("#popup-b-block"); if (blockUserBtn) { let message = ""; if (data.userState === "block") { (message = "un-block"); // ((message = "un-block"), (blockMessage = true)); } else { // ((message = "block"), (blockMessage = false)); (message = "block"); } blockUserBtn.textContent = message; } }); socket.on("logout", () => { quitChat(); }); socket.on("privMessageCopy", (message: string) => { const htmlBaliseRegex = /]*>[\s\S]*?<\/a>/; const htmlBaliseMatch = message.match(htmlBaliseRegex); if (htmlBaliseMatch) addInviteMessage(message); else addMessage(message); }); //receives broadcast of the next GAME socket.on("nextGame", (message: string) => { openMessagePopup(message); }); //receives broadcast of the next GAME socket.on("tourStatus", (message: string) => { openMessagePopup(message); }); socket.on("listBud", async (myBuddies: string[]) => { const buddies = document.getElementById( "div-buddies", ) as HTMLDivElement; listBuddies(socket, buddies, myBuddies); }); socket.once("welcome", (data) => { const buddies = document.getElementById( "div-buddies", ) as HTMLDivElement; buddies.textContent = ""; buddies.innerHTML = ""; connected(socket); addMessage(`${data.msg} ` + getUser()?.name); }); buddies.textContent = ""; buddies.innerHTML = ""; } if (buttonPro) buttonPro.addEventListener("click", () => { if (profilList) profilList.classList.add("hidden"); }); if (buttonMessage) buttonMessage.addEventListener("click", () => { if (gameMessage) gameMessage.classList.add("hidden"); if (modalmessage) { modalmessage.innerHTML = ""; } }); // Send button sendButton?.addEventListener("click", () => { let socket = window.__state.chatSock; if (!socket) return; const userId = getUser()?.id; const userAskingToBlock = getUser()?.name; if (sendtextbox && sendtextbox.value.trim()) { let msgText: string = sendtextbox.value.trim(); const msgCommand = parseCmdMsg(msgText) ?? ""; connected(socket); if (msgCommand !== "") { switch (msgCommand[0]) { case "@msg": broadcastMsg(socket, msgCommand); break; case "@block": if (msgCommand[1] === "") { break; } if (!userAskingToBlock) return; if (!userId) return; const userToBlock: ClientProfil = { command: msgCommand[0], destination: "", type: "chat", user: msgCommand[1], userID: userId, timestamp: Date.now(), SenderWindowID: socket.id, SenderName: userAskingToBlock, }; blockUser(userToBlock, socket); break; case "@notify": if (notify === null) { break; } if (inviteMsgFlag === false) { notify.innerText = "🔔"; inviteMsgFlag = true; } else { notify.innerText = "🔕"; inviteMsgFlag = false; } break; case "@pong": if (msgCommand[1] === "") { navigateTo("/app/pong/games"); quitChat(); } break; case "@ttt": if (msgCommand[1] === "") { navigateTo("/app/ttt/games"); quitChat(); } break; case "@guest": if (!userId) { return; } if (!userAskingToBlock) { return; } if (noGuest === null) { break; } const guest = getUser()?.guest; if (noGuestFlag === false && noGuest.innerText === "💔") { noGuest.innerText = "❤️​"; noGuestFlag = true; } else { noGuest.innerText = "💔"; noGuestFlag = false; } if (guest) { noGuestFlag = true; noGuest.innerText = ""; sendtextbox.value = ""; } const userProfile: ClientProfilPartial = { command: "@noguest", destination: "", type: "chat", user: userAskingToBlock, userID: userId, timestamp: Date.now(), SenderWindowID: "", guestmsg: noGuestFlag, }; socket.emit("guestmsg", JSON.stringify(userProfile)); break; case "@profile": if (msgCommand[1] === "") { break; } getProfil(socket, msgCommand[1]); break; case "@cls": chatWindow.innerHTML = ""; break; case "@help": cmdList(); break; case "@quit": quitChat(); break; default: const user: User | null = getUser(); if (!user) return; if (!user || !socket.connected) return; const message: ClientProfilPartial = { command: msgCommand[0], destination: "", type: "chat", user: user.name, userID: user.id, token: document.cookie ?? "", text: msgCommand[1], timestamp: Date.now(), SenderWindowID: socket.id ?? "", SenderID: user.id, }; socket.emit("privMessage", JSON.stringify(message)); break; } // Clear the input in all cases sendtextbox.value = ""; } } }); let toggle = false; window.addEventListener("focus", async () => { setTimeout(() => { if (window.__state.chatSock) connected(window.__state.chatSock); }, 16); // if (window.location.pathname === "/app/chat") { // if (window.__state.chatSock?.id) { await windowStateVisable(); // } toggle = true; // } }); window.addEventListener("blur", () => { if (window.__state.chatSock?.id) windowStateHidden(); toggle = false; }); // Clear Text button clearText?.addEventListener("click", () => { if (chatWindow) { chatWindow.innerHTML = ""; } }); bquit?.addEventListener("click", () => { quitChat(); }); myGames?.addEventListener("click", () => { navigateTo("/app/pong/games"); quitChat(); }); myTTTGames?.addEventListener("click", () => { navigateTo("/app/ttt/games"); quitChat(); }); // Enter key to send message sendtextbox.addEventListener("keydown", (event) => { if (!sendtextbox) return; if (event.key === "Enter") { event.preventDefault(); sendButton?.click(); } }); chatButton!.addEventListener("click",() => { if (chatBox.classList.contains("hidden")) { chatBox.classList.toggle("hidden"); overlay.classList.add("opacity-60"); windowStateVisable(); let socket = window.__state.chatSock; if (!socket) return; connected(socket); chatMessageIn?.classList.add("hidden"); chatMessageIn!.textContent = ''; sendtextbox.focus(); } else { chatBox.classList.toggle("hidden"); overlay.classList.remove("opacity-60"); windowStateHidden(); chatMessageIn?.classList.add("hidden"); chatMessageIn!.textContent = ''; } }); document.addEventListener("ft:userChange", (user) => { let newUser: { id: string; name: string } | null = user.detail; window.__state.chatSock?.disconnect(); window.__state.chatSock = undefined; if (newUser === null) { quitChat(); } else { initChatSocket(); } });