559 lines
16 KiB
TypeScript
559 lines
16 KiB
TypeScript
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";
|
|
import { actionBtnFriend } from "./chatHelperFunctions/actionBtnFriend";
|
|
|
|
const MAX_SYSTEM_MESSAGES = 10;
|
|
let inviteMsgFlag: boolean = false;
|
|
export let noGuestFlag: boolean = true;
|
|
let keysPressed: Record<string, boolean> = {};
|
|
|
|
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_hide_key = 'escape';
|
|
const chat_display_key = 'f2';
|
|
const home_display_key = 'f8';
|
|
document.addEventListener("keydown", (event) => {
|
|
if (event.repeat && keysPressed[chat_hide_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_hide_key) {
|
|
anti_flicker_control = false;
|
|
}
|
|
});
|
|
setInterval( () => {
|
|
if(keysPressed[chat_hide_key] === true) {
|
|
overlay.classList.remove("opacity-60");
|
|
chatBox.classList.add("hidden");
|
|
chatMessageIn?.classList.add("hidden");
|
|
chatMessageIn!.textContent = '';
|
|
profilList?.classList.add("hidden");
|
|
windowStateHidden();
|
|
}
|
|
if (keysPressed[chat_display_key] === true) {
|
|
anti_flicker_control = false;
|
|
chatBox.classList.remove("hidden");
|
|
overlay.classList.add("opacity-60");
|
|
chatMessageIn?.classList.add("hidden");
|
|
chatMessageIn!.textContent = '';
|
|
let socket = window.__state.chatSock;
|
|
if (!socket) return;
|
|
connected(socket);
|
|
sendtextbox.focus();
|
|
windowStateVisable();
|
|
|
|
}
|
|
if (keysPressed[home_display_key] === true) {
|
|
navigateTo('/app/');
|
|
quitChat();
|
|
}
|
|
}, 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.lastElementChild?.scrollIntoView({ block: "end" });
|
|
});
|
|
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.lastElementChild?.scrollIntoView({ block: "end" });
|
|
|
|
}
|
|
});
|
|
|
|
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);
|
|
actionBtnFriend(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");
|
|
} else {
|
|
(message = "block");
|
|
}
|
|
blockUserBtn.textContent = message;
|
|
}
|
|
});
|
|
|
|
socket.on("logout", () => {
|
|
quitChat();
|
|
});
|
|
|
|
socket.on("privMessageCopy", (message: string) => {
|
|
const htmlBaliseRegex = /<a\b[^>]*>[\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);
|
|
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();
|
|
}
|
|
});
|