From f14d618ed5bb0b9abbdf464df74ee086b4ab0607 Mon Sep 17 00:00:00 2001 From: Maieul BOYER Date: Sun, 11 Jan 2026 13:52:00 +0100 Subject: [PATCH] feat(chat): Handle user change --- frontend/src/@types/dom.d.ts | 1 + frontend/src/auth.ts | 8 +- frontend/src/chat/chat.ts | 701 +++++++++--------- .../src/chat/chatHelperFunctions/quitChat.ts | 4 +- 4 files changed, 363 insertions(+), 351 deletions(-) diff --git a/frontend/src/@types/dom.d.ts b/frontend/src/@types/dom.d.ts index b41a23f..49a8701 100644 --- a/frontend/src/@types/dom.d.ts +++ b/frontend/src/@types/dom.d.ts @@ -2,6 +2,7 @@ import { type State } from "ft_state"; interface CustomEventMap { "ft:pageChange": CustomEvent; + "ft:userChange": CustomEvent<{id: user, name: string} | null>; } declare global { diff --git a/frontend/src/auth.ts b/frontend/src/auth.ts index b65b8ac..acbc97a 100644 --- a/frontend/src/auth.ts +++ b/frontend/src/auth.ts @@ -1,7 +1,7 @@ import { showError } from "@app/toast"; import client from "@app/api"; import cookie from "js-cookie"; -import { ensureWindowState } from "@app/utils"; +import { ensureWindowState, isNullish } from "@app/utils"; import { navigateTo } from "./routing"; @@ -40,8 +40,14 @@ export function isLogged(): boolean { } export function setUser(newUser: User | null) { + let sendEvent = (window.__state.user?.id !== newUser?.id); window.__state.user = newUser; updateHeaderProfile(); + + if (sendEvent) { + const event = new CustomEvent("ft:userChange", { detail: isNullish(newUser) ? null : { id: newUser.id, name: newUser.name } }); + document.dispatchEvent(event); + } } export function updateHeaderProfile() { diff --git a/frontend/src/chat/chat.ts b/frontend/src/chat/chat.ts index f3d6bda..2389a4d 100644 --- a/frontend/src/chat/chat.ts +++ b/frontend/src/chat/chat.ts @@ -2,16 +2,16 @@ import "./chat.css"; import io, { Socket } from "socket.io-client"; import type { blockedUnBlocked } from "./types_front"; import type { - ClientMessage, - ClientProfil, - ClientProfilPartial, + ClientMessage, + ClientProfil, + ClientProfilPartial, } from "./types_front"; import type { User } from "@app/auth"; import { - addRoute, - setTitle, - type RouteHandlerParams, - type RouteHandlerReturn, + addRoute, + setTitle, + type RouteHandlerParams, + type RouteHandlerReturn, } from "@app/routing"; import authHtml from "./chat.html?raw"; import { getUser } from "@app/auth"; @@ -31,395 +31,400 @@ import { quitChat } from "./chatHelperFunctions/quitChat"; import { openMessagePopup } from "./chatHelperFunctions/openMessagePopup"; import { windowStateVisable } from "./chatHelperFunctions/windowStateVisable"; import { cmdList } from "./chatHelperFunctions/cmdList"; -import { showInfo } from '../toast'; +import { showInfo } from "../toast"; const MAX_SYSTEM_MESSAGES = 10; let inviteMsgFlag: boolean = false; export let noGuestFlag: boolean = true; declare module "ft_state" { - interface State { - chatSock?: Socket; - } + interface State { + chatSock?: Socket; + } } 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; + 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.classList.add("hidden"); chatBox.innerHTML = authHtml; -let socket = getSocket(); -let blockMessage: boolean; -// Listen for the 'connect' event -socket.on("connect", async () => { - const systemWindow = document.getElementById("system-box") as HTMLDivElement; - const sendtextbox = document.getElementById( - "t-chat-window" - ) as HTMLButtonElement; - const noGuest = document.getElementById("noGuest") ?? null; +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 chatWindow = document.querySelector("#t-chatbox")!; +const clearText = document.getElementById("b-clear") as HTMLButtonElement; +const gameMessage = document.getElementById("game-modal") ?? 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; - 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 = "❤️"; - } +function initChatSocket() { + let socket = getSocket(); + let blockMessage: boolean; + // 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; -}); + 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; + }); -// Listen for messages from the server "MsgObjectServer" -socket.on("MsgObjectServer", (data: { message: ClientMessage }) => { - const systemWindow = document.getElementById("system-box") as HTMLDivElement; - const chatWindow = document.getElementById("t-chatbox") as HTMLDivElement; + // Listen for messages from the server "MsgObjectServer" + socket.on("MsgObjectServer", (data: { message: ClientMessage }) => { + if (socket) { + connected(socket); + } - if (socket) { - connected(socket); - } + if (chatWindow && data.message.destination === "") { + 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 === "") { - 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") { + 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 === "privateMsg") { - 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") { + 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 (chatWindow && data.message.destination === "inviteMsg") { - 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}`; + systemWindow.appendChild(messageElement); - if (systemWindow && data.message.destination === "system-info") { - const messageElement = document.createElement("div"); - messageElement.textContent = `${data.message.user}: ${data.message.text}`; - systemWindow.appendChild(messageElement); + // keep only last 10 + while (systemWindow.children.length > MAX_SYSTEM_MESSAGES) { + systemWindow.removeChild(systemWindow.firstChild!); + } + systemWindow.scrollTop = systemWindow.scrollHeight; + } + }); - // keep only last 10 - while (systemWindow.children.length > MAX_SYSTEM_MESSAGES) { - systemWindow.removeChild(systemWindow.firstChild!); - } - 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); + }); -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); -}); + 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("blockUser", (blocked: ClientProfil) => { - let icon = "⛔"; - if (inviteMsgFlag) { - const chatWindow = document.getElementById("t-chatbox") as HTMLDivElement; - 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"), (blockMessage = true)); + } else { + ((message = "block"), (blockMessage = false)); + } + blockUserBtn.textContent = message; + } + }); -socket.on("blockBtn", (data: blockedUnBlocked) => { - const blockUserBtn = document.querySelector("#popup-b-block"); - if (blockUserBtn) { - let message = ""; - if (data.userState === "block") { - (message = "un-block"), (blockMessage = true); - } else { - (message = "block"), (blockMessage = false); - } - blockUserBtn.textContent = message; - } -}); + socket.on("logout", () => { + quitChat(); + }); -socket.on("logout", () => { - quitChat(socket); -}); + socket.on("privMessageCopy", (message: string) => { + addMessage(message); + }); -socket.on("privMessageCopy", (message: string) => { - addMessage(message); -}); + //receives broadcast of the next GAME + socket.on("nextGame", (message: string) => { + openMessagePopup(message); + }); -//receives broadcast of the next GAME -socket.on("nextGame", (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 "@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(() => { - connected(socket); - }, 16); - if (window.location.pathname === "/app/chat") { - if (socket.id) { - await windowStateVisable(); - } - toggle = true; - } + 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 (socket.id) windowStateHidden(); - toggle = false; + if (window.__state.chatSock?.id) windowStateHidden(); + toggle = false; }); - -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); -}); -setTitle("Chat Page"); -const sendButton = document.getElementById("b-send") as HTMLButtonElement; -const chatWindow = document.getElementById("t-chatbox") as HTMLDivElement; -const sendtextbox = document.getElementById( - "t-chat-window" -) as HTMLButtonElement; -const clearText = document.getElementById("b-clear") as HTMLButtonElement; -const buddies = document.getElementById("div-buddies") as HTMLDivElement; -const bquit = document.getElementById("b-quit") as HTMLDivElement; - -buddies.textContent = ""; -buddies.innerHTML = ""; -const buttonPro = document.getElementById("close-modal") ?? null; - -if (buttonPro) - buttonPro.addEventListener("click", () => { - const profilList = document.getElementById("profile-modal") ?? null; - if (profilList) profilList.classList.add("hidden"); - }); - -const buttonMessage = document.getElementById("close-modal-message") ?? null; - -if (buttonMessage) - buttonMessage.addEventListener("click", () => { - const gameMessage = document.getElementById("game-modal") ?? null; - if (gameMessage) gameMessage.classList.add("hidden"); - const modalmessage = document.getElementById("modal-message") ?? null; - if (modalmessage) { - modalmessage.innerHTML = ""; - } - }); - -// Send button -sendButton?.addEventListener("click", () => { - const notify = document.getElementById("notify") ?? null; - const noGuest = document.getElementById("noGuest") ?? null; - 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 "@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(socket); - 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 = ""; - } - } -}); - // Clear Text button clearText?.addEventListener("click", () => { - if (chatWindow) { - chatWindow.innerHTML = ""; - } + if (chatWindow) { + chatWindow.innerHTML = ""; + } }); bquit?.addEventListener("click", () => { - quitChat(socket); + quitChat(); }); // Enter key to send message sendtextbox.addEventListener("keydown", (event) => { - if (!sendtextbox) return; - if (event.key === "Enter") { - event.preventDefault(); - sendButton?.click(); - } + if (!sendtextbox) return; + if (event.key === "Enter") { + event.preventDefault(); + sendButton?.click(); + } }); -const chatButton = document.querySelector('#chatButton'); chatButton!.addEventListener("click", () => { - - const overlay = document.querySelector('#overlay')!; - if (chatBox.classList.contains('hidden')) { - chatBox.classList.toggle('hidden'); - overlay.classList.add('opacity-60'); - } else { - chatBox.classList.toggle('hidden'); - overlay.classList.remove('opacity-60'); - } + if (chatBox.classList.contains("hidden")) { + chatBox.classList.toggle("hidden"); + overlay.classList.add("opacity-60"); + } else { + chatBox.classList.toggle("hidden"); + overlay.classList.remove("opacity-60"); + } }); -document.addEventListener('click', () => { - if (socket) { - connected(socket); - } -}); \ No newline at end of file +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(); + // logged out + // hide chat button + } else { + // user has changed + initChatSocket(); + } +}); diff --git a/frontend/src/chat/chatHelperFunctions/quitChat.ts b/frontend/src/chat/chatHelperFunctions/quitChat.ts index d5ed5f7..6ddc978 100644 --- a/frontend/src/chat/chatHelperFunctions/quitChat.ts +++ b/frontend/src/chat/chatHelperFunctions/quitChat.ts @@ -10,7 +10,7 @@ import { setTitle } from "@app/routing"; * @param socket */ -export function quitChat (socket: Socket) { +export function quitChat () { const chatBox = document.getElementById("chatBox")!; const overlay = document.querySelector('#overlay')!; @@ -26,4 +26,4 @@ export function quitChat (socket: Socket) { showError('Failed to Quit Chat: Unknown error'); } -}; \ No newline at end of file +};