From b2862c303a24334b30943d2e48d745ff0375bb30 Mon Sep 17 00:00:00 2001 From: NigeParis Date: Thu, 4 Dec 2025 11:09:47 +0100 Subject: [PATCH 1/9] arrival messaeg in system info working --- frontend/src/pages/chat/chat.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/src/pages/chat/chat.ts b/frontend/src/pages/chat/chat.ts index 26dd177..02f2816 100644 --- a/frontend/src/pages/chat/chat.ts +++ b/frontend/src/pages/chat/chat.ts @@ -35,8 +35,8 @@ document.addEventListener('ft:pageChange', () => { }) function getSocket(): Socket { - // let addressHost = `wss://${machineHostName}:8888`; - let addressHost = `wss://localhost:8888`; + let addressHost = `wss://${machineHostName}:8888`; + // let addressHost = `wss://localhost:8888`; if (__socket === undefined) __socket = io(addressHost, { @@ -137,6 +137,7 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn // Ensure we have a user AND socket is connected if (!user || !socket.connected) return; const message = { + destination: 'system-info', type: "chat", user, token: document.cookie ?? "", From b280c1b7f60cb01f65f82c30527d32d1da301ed1 Mon Sep 17 00:00:00 2001 From: NigeParis Date: Thu, 4 Dec 2025 18:04:41 +0100 Subject: [PATCH 2/9] made bconnect.click() into function, seems to have solved the re loading of ping buddies after guest name change --- frontend/src/pages/chat/chat.ts | 239 ++++++++++++++++++++------------ src/chat/src/app.ts | 5 + 2 files changed, 153 insertions(+), 91 deletions(-) diff --git a/frontend/src/pages/chat/chat.ts b/frontend/src/pages/chat/chat.ts index 02f2816..4bfb307 100644 --- a/frontend/src/pages/chat/chat.ts +++ b/frontend/src/pages/chat/chat.ts @@ -4,7 +4,6 @@ import authHtml from './chat.html?raw'; import client from '@app/api' import { getUser, updateUser } from "@app/auth"; import io, { Socket } from 'socket.io-client'; -// import type { ClientMessage } from "@app/@types/dom"; const color = { red: 'color: red;', @@ -15,6 +14,7 @@ const color = { }; export type ClientMessage = { + command: string destination: string; user: string; text: string; @@ -32,7 +32,7 @@ document.addEventListener('ft:pageChange', () => { __socket.close(); __socket = undefined; console.log("Page changed"); -}) +}); function getSocket(): Socket { let addressHost = `wss://${machineHostName}:8888`; @@ -45,12 +45,24 @@ function getSocket(): Socket { transports: ["websocket"], }); return __socket; -} +}; + + +function addMessage(text: string) { + const chatWindow = document.getElementById("t-chatbox") as HTMLDivElement; + if (!chatWindow) return; + const messageElement = document.createElement("div-test"); + messageElement.textContent = text; + chatWindow.appendChild(messageElement); + chatWindow.scrollTop = chatWindow.scrollHeight; + console.log(`Added new message: ${text}`) + return ; +}; async function isLoggedIn() { return getUser() || null; -} +}; async function windowStateHidden() { const socketId = __socket || undefined; @@ -66,7 +78,7 @@ async function windowStateHidden() { why: 'tab window hidden - socket not dead', }); return; -} +}; async function windowStateVisable() { @@ -85,8 +97,30 @@ async function windowStateVisable() { }); setTitle('Chat Page'); return; -} +}; +function parseCmdMsg(msgText: string): string[] | undefined { + if (!msgText?.trim()) { + console.log('%c DEBUG - in FN parseCmdMsg : msgText = ""', color.red); + return; + } + msgText = msgText.trim(); + // Find the first space + const firstSpaceIndex = msgText.indexOf(' '); + const cmd = firstSpaceIndex === -1 ? msgText : msgText.slice(0, firstSpaceIndex); + const rest = firstSpaceIndex === -1 ? "" : msgText.slice(firstSpaceIndex + 1).trim(); + const command: string[] = ["", ""]; + if (!msgText.startsWith('@')) { + command[0] = "@msg"; + command[1] = msgText; + } else { + command[0] = cmd; + command[1] = rest; + } + console.log('%c DEBUG - split msgText[0]:', color.red, command[0]); + console.log('%c DEBUG - split msgText[1]:', color.red, command[1]); + return command; +}; async function listBuddies(buddies: HTMLDivElement, listBuddies: string ) { @@ -98,14 +132,15 @@ async function listBuddies(buddies: HTMLDivElement, listBuddies: string ) { console.log(`Added buddies: ${listBuddies}`) return ; -} +}; function waitSocketConnected(socket: Socket): Promise { return new Promise(resolve => { if (socket.connected) return resolve(); // already connected socket.on("connect", () => resolve()); }); -} +}; + const bconnected = document.getElementById('b-help') as HTMLButtonElement; if (bconnected) { bconnected.click(); @@ -118,8 +153,52 @@ function logout(socket: Socket) { if (__socket !== undefined) __socket.close(); // window.location.href = "/login"; -} +}; +function broadcastMsg (socket: Socket, msgCommand: string[]): void { + let msgText = msgCommand[1] ?? ""; + console.log('%cmsgText:', color.red, msgText); + addMessage(msgText); + const user = getUser(); + if (user && socket?.connected) { + const message = { + command: msgCommand, + destination: "", + type: "chat", + user: user.name, + token: document.cookie, + text: msgText, + timestamp: Date.now(), + SenderWindowID: socket.id, + }; + socket.emit('message', JSON.stringify(message)); + } +}; + + +async function connected(socket: Socket): Promise { + + const buddies = document.getElementById('div-buddies') as HTMLDivElement; + const loggedIn = await isLoggedIn(); + console.log('%cloggedIn:',color.blue, loggedIn?.name); + let oldUser = localStorage.getItem("oldName") ?? ""; + console.log('%coldUser:',color.yellow, oldUser); + if (loggedIn?.name === undefined) {console.log('');return ;} + oldUser = loggedIn.name ?? ""; + const res = await client.guestLogin(); + let user = await updateUser(); + console.log('%cUser?name:',color.yellow, user?.name); + localStorage.setItem("oldName", oldUser); + buddies.textContent = ""; + // if (chatWindow) { + // addMessage('@list - lists all connected users in the chat'); + socket.emit('list', { + oldUser: oldUser, + user: user?.name, + }); + // } + +}; function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn { @@ -130,13 +209,15 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn // Listen for the 'connect' event socket.on("connect", async () => { - + + const systemWindow = document.getElementById('system-box') as HTMLDivElement; await waitSocketConnected(socket); console.log("I AM Connected to the server:", socket.id); const user = getUser()?.name; // Ensure we have a user AND socket is connected if (!user || !socket.connected) return; const message = { + command: "", destination: 'system-info', type: "chat", user, @@ -146,6 +227,12 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn SenderWindowID: socket.id, }; socket.emit('message', JSON.stringify(message)); + // if (systemWindow) { + 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" @@ -157,8 +244,9 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn const systemWindow = document.getElementById('system-box') as HTMLDivElement; const chatWindow = document.getElementById("t-chatbox") as HTMLDivElement; const bconnected = document.getElementById('b-help') as HTMLButtonElement; + if (bconnected) { - bconnected.click(); + connected(socket); } if (chatWindow && data.message.destination === "") { @@ -178,16 +266,12 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn while (systemWindow.children.length > MAX_SYSTEM_MESSAGES) { systemWindow.removeChild(systemWindow.firstChild!); } - systemWindow.scrollTop = systemWindow.scrollHeight; } - console.log("Getuser():", getUser()); }); - - socket.on('logout', () => { const bquit = document.getElementById('b-quit') as HTMLDivElement | null; if (bquit instanceof HTMLDivElement) { @@ -202,25 +286,24 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn color?: { default: string, hover: string }, }; - - let toggle = false - window.addEventListener("focus", () => { - const bwhoami = document.getElementById('b-whoami') as HTMLButtonElement; - if (window.location.pathname === '/app/chat') { - console.log("%cWindow is focused on /chat:" + socket.id, color.green); - if (socket.id) - windowStateVisable(); - bwhoami.click(); - toggle = true; + let toggle = false + window.addEventListener("focus", () => { + const bwhoami = document.getElementById('b-whoami') as HTMLButtonElement; + if (window.location.pathname === '/app/chat') { + console.log("%cWindow is focused on /chat:" + socket.id, color.green); + if (socket.id) { + windowStateVisable(); + connected(socket); } - }); - - window.addEventListener("blur", () => { - console.log("%cWindow is not focused on /chat", color.red); - if (socket.id) - windowStateHidden(); - toggle = false; - }); + toggle = true; + } + }); + window.addEventListener("blur", () => { + console.log("%cWindow is not focused on /chat", color.red); + if (socket.id) + windowStateHidden(); + toggle = false; + }); setTitle('Chat Page'); // Listen for the 'connect' event @@ -236,6 +319,7 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn const username = document.getElementById('username') as HTMLDivElement; const buddies = document.getElementById('div-buddies') as HTMLDivElement; const bquit = document.getElementById('b-quit') as HTMLDivElement; + const systemWindow = document.getElementById('system-box') as HTMLDivElement; chatWindow.textContent = ''; chatWindow.innerHTML = ''; @@ -252,56 +336,51 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn console.log('unknown response: ', value); } - const addMessage = (text: string) => { - if (!chatWindow) return; - const messageElement = document.createElement("div-test"); - messageElement.textContent = text; - chatWindow.appendChild(messageElement); - chatWindow.scrollTop = chatWindow.scrollHeight; - console.log(`Added new message: ${text}`) - return ; - }; socket.once('welcome', (data) => { chatWindow.textContent = ''; chatWindow.innerHTML = ''; buddies.textContent = ''; buddies.innerHTML = ''; - bconnected.click(); + connected(socket); addMessage (`${data.msg} ` + getUser()?.name); - }); + }); // Send button sendButton?.addEventListener("click", () => { if (sendtextbox && sendtextbox.value.trim()) { - const msgText = sendtextbox.value.trim(); - bconnected.click(); - addMessage(msgText); - const user = getUser(); - if (user && socket?.connected) { - const message = { - destination: "", - type: "chat", - user: user.name, - token: document.cookie, - text: msgText, - timestamp: Date.now(), - SenderWindowID: socket.id, - }; - socket.emit('message', JSON.stringify(message)); + let msgText: string = sendtextbox.value.trim(); + const msgCommand = parseCmdMsg(msgText) ?? ""; + connected(socket); + if (msgCommand !== "") { + switch (msgCommand[0]) { + case '@msg': + broadcastMsg(socket, msgCommand); + break; + case '@who': + bwhoami.click(); + break; + case '@cls': + clearText.click(); + break; + case '@quit': + bquit.click(); + break; + default: + addMessage('Command not known'); + break; + } + // Clear the input in all cases + sendtextbox.value = ""; } - sendtextbox.value = ""; } }); - - - // Clear Text button clearText?.addEventListener("click", () => { if (chatWindow) { - bconnected.click(); + connected(socket); chatWindow.innerHTML = ''; } }); @@ -310,7 +389,9 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn if (socket) { logout(socket); setTitle('Chat Page'); - bconnected.click(); + systemWindow.innerHTML = ""; + chatWindow.textContent = ""; + connected(socket); } else { getSocket(); } @@ -318,32 +399,8 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn setInterval(async () => { - //bconnected.click(); - }, 10000); // every 10 second - - // Help Text button - bconnected?.addEventListener("click", async () => { - - const loggedIn = await isLoggedIn(); - console.log('%cloggedIn:',color.blue, loggedIn?.name); - let oldUser = localStorage.getItem("oldName") ?? ""; - console.log('%coldUser:',color.yellow, oldUser); - if (loggedIn?.name === undefined) {console.log('');return ;} - oldUser = loggedIn.name || "undefined"; - const res = await client.guestLogin(); - let user = await updateUser(); - console.log('%cUser?name:',color.yellow, user?.name); - localStorage.setItem("oldName", oldUser); - buddies.textContent = ""; - // if (chatWindow) { - // addMessage('@list - lists all connected users in the chat'); - socket.emit('list', { - oldUser: oldUser, - user: user?.name, - }); - // } - - }); + //connected(socket); + },10000); // every 10 second socket.on('listBud', (myBuddies: string) => { console.log('List buddies connected ', myBuddies); diff --git a/src/chat/src/app.ts b/src/chat/src/app.ts index 4ac2c96..733ba7f 100644 --- a/src/chat/src/app.ts +++ b/src/chat/src/app.ts @@ -34,6 +34,7 @@ interface ClientInfo { } export type ClientMessage = { + command: string destination: string; user: string; text: string; @@ -269,6 +270,7 @@ async function onReady(fastify: FastifyInstance) { if (!clientName) return; console.log(color.green, `Client logging out: ${clientName} (${socket.id})`); const obj = { + command: '', destination: 'system-info', type: 'chat' as const, user: clientName, @@ -295,6 +297,7 @@ async function onReady(fastify: FastifyInstance) { if (clientName !== null) { const obj = { + command: '', destination: 'system-info', type: 'chat', user: clientName, @@ -319,6 +322,7 @@ async function onReady(fastify: FastifyInstance) { if (clientName !== null) { const obj = { + command: '', destination: 'system-info', type: 'chat', user: clientName, @@ -364,6 +368,7 @@ async function onReady(fastify: FastifyInstance) { ); if (clientName !== null) { const obj = { + command: '', destination: 'system-info', type: 'chat', user: clientName, From 0c2bdfaf77732fc8eb4c52600836539576cfcbe2 Mon Sep 17 00:00:00 2001 From: NigeParis Date: Thu, 4 Dec 2025 18:58:23 +0100 Subject: [PATCH 3/9] end of the day --- frontend/src/pages/chat/chat.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/src/pages/chat/chat.ts b/frontend/src/pages/chat/chat.ts index 4bfb307..62659b4 100644 --- a/frontend/src/pages/chat/chat.ts +++ b/frontend/src/pages/chat/chat.ts @@ -361,7 +361,7 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn bwhoami.click(); break; case '@cls': - clearText.click(); + chatWindow.innerHTML = ''; break; case '@quit': bquit.click(); @@ -380,7 +380,6 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn clearText?.addEventListener("click", () => { if (chatWindow) { - connected(socket); chatWindow.innerHTML = ''; } }); From a1e7504444399ccbe21935ed8cb719066703b336 Mon Sep 17 00:00:00 2001 From: NigeParis Date: Fri, 5 Dec 2025 16:37:26 +0100 Subject: [PATCH 4/9] First private messages working --- frontend/src/chat/chat.css | 13 +- frontend/src/pages/chat/chat.ts | 303 +++++++++++++++++++------------- src/chat/src/app.ts | 56 ++++++ src/package.json | 2 +- src/pnpm-lock.yaml | 101 ++++++----- 5 files changed, 303 insertions(+), 172 deletions(-) diff --git a/frontend/src/chat/chat.css b/frontend/src/chat/chat.css index eb43dce..9c3bccd 100644 --- a/frontend/src/chat/chat.css +++ b/frontend/src/chat/chat.css @@ -159,8 +159,11 @@ div-buddies-list { @apply text-black - whitespace-pre-wrap; - + whitespace-pre-wrap + cursor-pointer + hover:text-blue-500 + transition-colors + duration-150; } p { @@ -180,4 +183,10 @@ div-notlog { text-red-800 text-3xl text-center; +} + +div-private { + @apply + text-blue-800; + } \ No newline at end of file diff --git a/frontend/src/pages/chat/chat.ts b/frontend/src/pages/chat/chat.ts index 62659b4..8d380f1 100644 --- a/frontend/src/pages/chat/chat.ts +++ b/frontend/src/pages/chat/chat.ts @@ -47,7 +47,6 @@ function getSocket(): Socket { return __socket; }; - function addMessage(text: string) { const chatWindow = document.getElementById("t-chatbox") as HTMLDivElement; if (!chatWindow) return; @@ -59,7 +58,6 @@ function addMessage(text: string) { return ; }; - async function isLoggedIn() { return getUser() || null; }; @@ -80,14 +78,12 @@ async function windowStateHidden() { return; }; - async function windowStateVisable() { const socketId = __socket || undefined; let oldName = localStorage.getItem("oldName") || undefined; console.log("%c WINDOW VISIBLE - oldName :'" + oldName + "'", color.green); if (socketId === undefined || oldName === undefined) {console.log("%SOCKET ID", color.red); return;} - // const res = await client.guestLogin(); let user = await updateUser(); if(user === null) return; console.log("%cUserName :'" + user?.name + "'", color.green); @@ -100,39 +96,57 @@ async function windowStateVisable() { }; function parseCmdMsg(msgText: string): string[] | undefined { - if (!msgText?.trim()) { - console.log('%c DEBUG - in FN parseCmdMsg : msgText = ""', color.red); - return; - } - msgText = msgText.trim(); - // Find the first space - const firstSpaceIndex = msgText.indexOf(' '); - const cmd = firstSpaceIndex === -1 ? msgText : msgText.slice(0, firstSpaceIndex); - const rest = firstSpaceIndex === -1 ? "" : msgText.slice(firstSpaceIndex + 1).trim(); - const command: string[] = ["", ""]; - if (!msgText.startsWith('@')) { - command[0] = "@msg"; - command[1] = msgText; - } else { - command[0] = cmd; - command[1] = rest; - } - console.log('%c DEBUG - split msgText[0]:', color.red, command[0]); - console.log('%c DEBUG - split msgText[1]:', color.red, command[1]); - return command; -}; -async function listBuddies(buddies: HTMLDivElement, listBuddies: string ) { + if (!msgText?.trim()) return; + msgText = msgText.trim(); + const command: string[] = ['', '']; + if (!msgText.startsWith('@')) { + command[0] = '@msg'; + command[1] = msgText; + return command; + } + const noArgCommands = ['@quit', '@cls', '@profile']; + if (noArgCommands.includes(msgText)) { + command[0] = msgText; + command[1] = ''; + return command; + } + const colonIndex = msgText.indexOf(":"); + if (colonIndex === -1) { + command[0] = msgText; + command[1] = ''; + return command; + } + const cmd = msgText.slice(0, colonIndex).trim(); + const rest = msgText.slice(colonIndex + 1).trim(); + command[0] = cmd; + command[1] = rest; + return command; +} + +async function listBuddies(buddies: HTMLDivElement, listBuddies: string) { if (!buddies) return; + const sendtextbox = document.getElementById('t-chat-window') as HTMLButtonElement; + const messageElement = document.createElement("div-buddies-list"); messageElement.textContent = listBuddies + '\n'; + + + // ✔ Add click-to-copy + messageElement.style.cursor = "pointer"; // optional visual hint + messageElement.addEventListener("click", () => { + navigator.clipboard.writeText(listBuddies); + sendtextbox.value = `@${listBuddies}: `; + console.log("Copied to clipboard:", listBuddies); + sendtextbox.focus(); // move cursor into the box + }); + buddies.appendChild(messageElement); buddies.scrollTop = buddies.scrollHeight; - console.log(`Added buddies: ${listBuddies}`) - return ; + console.log(`Added buddies: ${listBuddies}`); +} -}; function waitSocketConnected(socket: Socket): Promise { return new Promise(resolve => { @@ -141,6 +155,27 @@ function waitSocketConnected(socket: Socket): Promise { }); }; +function quitChat (socket: Socket) { + + try { + const systemWindow = document.getElementById('system-box') as HTMLDivElement; + const chatWindow = document.getElementById("t-chatbox") as HTMLDivElement; + if (socket) { + logout(socket); + setTitle('Chat Page'); + systemWindow.innerHTML = ""; + chatWindow.textContent = ""; + connected(socket); + } else { + getSocket(); + } + } catch (e) { + console.error("Quit Chat error:", e); + showError('Failed to Quit Chat: Unknown error'); + } + +}; + const bconnected = document.getElementById('b-help') as HTMLButtonElement; if (bconnected) { bconnected.click(); @@ -163,7 +198,7 @@ function broadcastMsg (socket: Socket, msgCommand: string[]): void { if (user && socket?.connected) { const message = { command: msgCommand, - destination: "", + destination: '', type: "chat", user: user.name, token: document.cookie, @@ -178,28 +213,61 @@ function broadcastMsg (socket: Socket, msgCommand: string[]): void { async function connected(socket: Socket): Promise { - const buddies = document.getElementById('div-buddies') as HTMLDivElement; - const loggedIn = await isLoggedIn(); - console.log('%cloggedIn:',color.blue, loggedIn?.name); - let oldUser = localStorage.getItem("oldName") ?? ""; - console.log('%coldUser:',color.yellow, oldUser); - if (loggedIn?.name === undefined) {console.log('');return ;} - oldUser = loggedIn.name ?? ""; - const res = await client.guestLogin(); - let user = await updateUser(); - console.log('%cUser?name:',color.yellow, user?.name); - localStorage.setItem("oldName", oldUser); - buddies.textContent = ""; - // if (chatWindow) { - // addMessage('@list - lists all connected users in the chat'); + try { + const buddies = document.getElementById('div-buddies') as HTMLDivElement; + const loggedIn = await isLoggedIn(); + console.log('%cloggedIn:',color.blue, loggedIn?.name); + let oldUser = localStorage.getItem("oldName") ?? ""; + console.log('%coldUser:',color.yellow, oldUser); + if (loggedIn?.name === undefined) {console.log('');return ;} + oldUser = loggedIn.name ?? ""; + const res = await client.guestLogin(); + let user = await updateUser(); + console.log('%cUser?name:',color.yellow, user?.name); + localStorage.setItem("oldName", oldUser); + buddies.textContent = ""; socket.emit('list', { oldUser: oldUser, user: user?.name, }); - // } - + } catch (e) { + console.error("Login error:", e); + showError('Failed to login: Unknown error'); + } }; +async function whoami(socket: Socket) { + try { + const chatWindow = document.getElementById("t-chatbox") as HTMLDivElement; + const loggedIn = await isLoggedIn(); + + const res = await client.guestLogin(); + switch (res.kind) { + case 'success': { + let user = await updateUser(); + if (chatWindow) { + socket.emit('updateClientName', { + oldUser: '', + user: user?.name + }); + } + if (user === null) + return showError('Failed to get user: no user ?'); + setTitle(`Welcome ${user.guest ? '[GUEST] ' : ''}${user.name}`); + break; + } + case 'failed': { + showError(`Failed to login: ${res.msg}`); + } + } + } catch (e) { + console.error("Login error:", e); + showError('Failed to login: Unknown error'); + } +}; + + + function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn { @@ -227,19 +295,17 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn SenderWindowID: socket.id, }; socket.emit('message', JSON.stringify(message)); - // if (systemWindow) { - const messageElement = document.createElement("div"); - messageElement.textContent = `${user}: is connected au server`; - systemWindow.appendChild(messageElement); - systemWindow.scrollTop = systemWindow.scrollHeight; - // } + 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}) => { - console.log("Message Obj Recieved:", data); - console.log("%cRecieved data.message.text: ", color.blue, data.message.text); - console.log("%cRecieved data.message.user: ", color.blue, data.message.user); + console.log("%cDEBUG LOGS - Message Obj Recieved:", color.green, data); + console.log("%cDEBUG LOGS - Recieved data.message.text: ", color.green, data.message.text); + console.log("%cDEBUG LOGS - Recieved data.message.user: ", color.green, data.message.user); // Display the message in the chat window const systemWindow = document.getElementById('system-box') as HTMLDivElement; const chatWindow = document.getElementById("t-chatbox") as HTMLDivElement; @@ -255,6 +321,14 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn 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; + } + + const MAX_SYSTEM_MESSAGES = 10; if (systemWindow && data.message.destination === "system-info") { @@ -272,13 +346,14 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn }); - socket.on('logout', () => { - const bquit = document.getElementById('b-quit') as HTMLDivElement | null; - if (bquit instanceof HTMLDivElement) { - bquit.click(); - } + socket.on('logout', () => { + quitChat(socket); }); + socket.on('privMessageCopy', (message) => { + addMessage(message); + }) + type Providers = { name: string, display_name: string, @@ -288,7 +363,7 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn let toggle = false window.addEventListener("focus", () => { - const bwhoami = document.getElementById('b-whoami') as HTMLButtonElement; + //nst bwhoami = document.getElementById('b-whoami') as HTMLButtonElement; if (window.location.pathname === '/app/chat') { console.log("%cWindow is focused on /chat:" + socket.id, color.green); if (socket.id) { @@ -298,6 +373,7 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn toggle = true; } }); + window.addEventListener("blur", () => { console.log("%cWindow is not focused on /chat", color.red); if (socket.id) @@ -305,6 +381,28 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn toggle = false; }); + + // setInterval(async () => { + // //connected(socket); + // },10000); // every 10 seco + + socket.on('listBud', (myBuddies: string) => { + const buddies = document.getElementById('div-buddies') as HTMLDivElement; + console.log('List buddies connected ', myBuddies); + listBuddies(buddies,myBuddies); + }); + + socket.once('welcome', (data) => { + const buddies = document.getElementById('div-buddies') as HTMLDivElement; + const chatWindow = document.getElementById('t-chatbox') as HTMLDivElement; + chatWindow.innerHTML = ''; + buddies.textContent = ''; + buddies.innerHTML = ''; + connected(socket); + addMessage (`${data.msg} ` + getUser()?.name); + }); + + setTitle('Chat Page'); // Listen for the 'connect' event return { @@ -336,16 +434,6 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn console.log('unknown response: ', value); } - - socket.once('welcome', (data) => { - chatWindow.textContent = ''; - chatWindow.innerHTML = ''; - buddies.textContent = ''; - buddies.innerHTML = ''; - connected(socket); - addMessage (`${data.msg} ` + getUser()?.name); - }); - // Send button sendButton?.addEventListener("click", () => { if (sendtextbox && sendtextbox.value.trim()) { @@ -358,16 +446,32 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn broadcastMsg(socket, msgCommand); break; case '@who': - bwhoami.click(); - break; + whoami(socket); + break; case '@cls': chatWindow.innerHTML = ''; break; case '@quit': - bquit.click(); + quitChat(socket); break; default: - addMessage('Command not known'); + + const user = getUser()?.name; + // Ensure we have a user AND socket is connected + if (!user || !socket.connected) return; + const message = { + command: msgCommand[0], + destination: '', + type: "chat", + user, + token: document.cookie ?? "", + text: msgCommand[1], + timestamp: Date.now(), + SenderWindowID: socket.id, + }; + //socket.emit('MsgObjectServer', message); + socket.emit('privMessage', JSON.stringify(message)); + // addMessage(JSON.stringify(message)); break; } // Clear the input in all cases @@ -378,35 +482,15 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn // Clear Text button clearText?.addEventListener("click", () => { - if (chatWindow) { chatWindow.innerHTML = ''; } }); bquit?.addEventListener('click', () => { - if (socket) { - logout(socket); - setTitle('Chat Page'); - systemWindow.innerHTML = ""; - chatWindow.textContent = ""; - connected(socket); - } else { - getSocket(); - } + quitChat(socket); }); - - setInterval(async () => { - //connected(socket); - },10000); // every 10 second - - socket.on('listBud', (myBuddies: string) => { - console.log('List buddies connected ', myBuddies); - listBuddies(buddies,myBuddies); - }); - - // Enter key to send message sendtextbox!.addEventListener('keydown', (event) => { if (event.key === 'Enter') { @@ -416,32 +500,7 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn // Whoami button to display user name bwhoami?.addEventListener('click', async () => { - try { - const loggedIn = await isLoggedIn(); - - const res = await client.guestLogin(); - switch (res.kind) { - case 'success': { - let user = await updateUser(); - if (chatWindow) { - socket.emit('updateClientName', { - oldUser: '', - user: user?.name - }); - } - if (user === null) - return showError('Failed to get user: no user ?'); - setTitle(`Welcome ${user.guest ? '[GUEST] ' : ''}${user.name}`); - break; - } - case 'failed': { - showError(`Failed to login: ${res.msg}`); - } - } - } catch (e) { - console.error("Login error:", e); - showError('Failed to login: Unknown error'); - } + whoami(socket); }); } } diff --git a/src/chat/src/app.ts b/src/chat/src/app.ts index 733ba7f..a798de2 100644 --- a/src/chat/src/app.ts +++ b/src/chat/src/app.ts @@ -83,6 +83,8 @@ declare module 'fastify' { io: Server<{ hello: (message: string) => string; MsgObjectServer: (data: { message: ClientMessage }) => void; + privMessage: (data: string) => void; + privMessageCopy: (msg: string) => void; message: (msg: string) => void; listBud: (msg: string) => void; testend: (sock_id_client: string) => void; @@ -183,6 +185,33 @@ async function onReady(fastify: FastifyInstance) { }); } + + + function sendPrivMessage(data: ClientMessage, sender?: string) { + fastify.io.fetchSockets().then((sockets) => { + const senderSocket = sockets.find(s => s.id === sender); + for (const s of sockets) { + if (s.id === sender) continue; + const clientInfo = clientChat.get(s.id); + if (!clientInfo?.user) { + console.log(color.yellow, `Skipping socket ${s.id} (no user found)`); + continue; + } + let user: string = clientChat.get(s.id)?.user ?? ""; + const atUser = `@${user}`; + if (atUser !== data.command || atUser === "") { + console.log(color.yellow, `User: '${atUser}' (No user the same is found): '${data.command}' `); + continue; + } + s.emit('MsgObjectServer', { message: data }); + if (senderSocket) + senderSocket.emit('privMessageCopy',`${data.command}: ${data.text}🔒`); + // Debug logs + console.log(color.green, 'Priv to:', clientInfo.user); + } + }); + } + fastify.io.on('connection', (socket: Socket) => { socket.on('message', (message: string) => { @@ -337,6 +366,33 @@ async function onReady(fastify: FastifyInstance) { } }); + + socket.on('privMessage', (data) => { + const clientName: string = clientChat.get(socket.id)?.user || ""; + const prvMessage: ClientMessage = JSON.parse(data) || ""; + console.log( + color.blue, + `DEBUG LOG: ClientName: '${clientName}' id Socket: '${socket.id}' target Name:`, + prvMessage.command + ); + + if (clientName !== null) { + const obj = { + command: prvMessage.command, + destination: 'privateMsg', + type: 'chat', + user: clientName, + token: '', + text: prvMessage.text, + timestamp: Date.now(), + SenderWindowID: socket.id, + }; + console.log(color.blue, 'PRIV MESSAGE OUT :', obj.SenderWindowID); + sendPrivMessage(obj, obj.SenderWindowID); + // clientChat.delete(obj.user); + } + }); + socket.on('client_entered', (data) => { // data may be undefined (when frontend calls emit with no payload) diff --git a/src/package.json b/src/package.json index 9fa1e9c..45b7a6d 100644 --- a/src/package.json +++ b/src/package.json @@ -36,7 +36,7 @@ "vite": "^7.2.6" }, "dependencies": { - "@redocly/cli": "^2.12.1", + "@redocly/cli": "^2.12.3", "bindings": "^1.5.0" } } diff --git a/src/pnpm-lock.yaml b/src/pnpm-lock.yaml index ad74292..a50dc94 100644 --- a/src/pnpm-lock.yaml +++ b/src/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: dependencies: '@redocly/cli': - specifier: ^2.12.1 - version: 2.12.1(@opentelemetry/api@1.9.0)(ajv@8.17.1)(core-js@3.47.0) + specifier: ^2.12.3 + version: 2.12.3(@opentelemetry/api@1.9.0)(ajv@8.17.1)(core-js@3.47.0) bindings: specifier: ^1.5.0 version: 1.5.0 @@ -943,8 +943,8 @@ packages: '@redocly/ajv@8.17.1': resolution: {integrity: sha512-EDtsGZS964mf9zAUXAl9Ew16eYbeyAFWhsPr0fX6oaJxgd8rApYlPBf0joyhnUHz88WxrigyFtTaqqzXNzPgqw==} - '@redocly/cli@2.12.1': - resolution: {integrity: sha512-XGD28QjjZEzN+J9WOROzw4fHNi+Fyw/gCyDZDgI4nX4j9gEBT1PcxN75wWpMoDGHKAUj8ghrhMHtfQoUuR90zg==} + '@redocly/cli@2.12.3': + resolution: {integrity: sha512-1SDW551scNdb4HmNpzyUf4gjsK89KkRUeXF91VVMRkQ5+lFEq1Nj259jN1M25uOd/cg1QjKE3kIbnN1dxPa3ng==} engines: {node: '>=22.12.0 || >=20.19.0 <21.0.0', npm: '>=10'} hasBin: true @@ -958,12 +958,12 @@ packages: resolution: {integrity: sha512-0EbE8LRbkogtcCXU7liAyC00n9uNG9hJ+eMyHFdUsy9lB/WGqnEBgwjA9q2cyzAVcdTkQqTBBU1XePNnN3OijA==} engines: {node: '>=18.17.0', npm: '>=9.5.0'} - '@redocly/openapi-core@2.12.1': - resolution: {integrity: sha512-xMlKf4dnZsxP3JYBNZFsMNBJqVxWlwLuyGLhGc36hXw50YOla1UjrVZ5psIyzLXgUPI3QJDA1XmGcJ8rcex/ow==} + '@redocly/openapi-core@2.12.3': + resolution: {integrity: sha512-3gdSRftIeUbzXvwDi/tBjO0uj9PzR0XzbWjNwuu3HlVXJ1ElB+K31AnzQ2iA6mjIHq9uvmLRXAs9MsP/0Hbzug==} engines: {node: '>=22.12.0 || >=20.19.0 <21.0.0', npm: '>=10'} - '@redocly/respect-core@2.12.1': - resolution: {integrity: sha512-ADm+JMHWGYeOwzdGEQ8CYKjmMBLU0ycZTwJbCkQsUulXSNkNA7GzA8lrMM2+I8cPMRk25G5PmtfAR7U+a0o1ew==} + '@redocly/respect-core@2.12.3': + resolution: {integrity: sha512-ZYqrLBlRVVHwgPawOjo94sKmeuuien77xtkXluTa6+y/wkQ8c5oYY7OqWbasMv0IoxSPehwVMa0AL0OCQP3uCQ==} engines: {node: '>=22.12.0 || >=20.19.0 <21.0.0', npm: '>=10'} '@rollup/rollup-android-arm-eabi@4.53.3': @@ -2613,10 +2613,10 @@ packages: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true - react-dom@19.2.0: - resolution: {integrity: sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==} + react-dom@19.2.1: + resolution: {integrity: sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg==} peerDependencies: - react: ^19.2.0 + react: ^19.2.1 react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -2626,8 +2626,8 @@ packages: peerDependencies: react: ^18.0.0 || ^19.0.0 - react@19.2.0: - resolution: {integrity: sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==} + react@19.2.1: + resolution: {integrity: sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw==} engines: {node: '>=0.10.0'} readable-stream@3.6.2: @@ -3054,6 +3054,10 @@ packages: resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==} engines: {node: '>=18'} + ulid@3.0.2: + resolution: {integrity: sha512-yu26mwteFYzBAot7KVMqFGCVpsF6g8wXfJzQUHvu1no3+rRRSFcSV2nKeYvNPLD2J4b08jYBDhHUjeH0ygIl9w==} + hasBin: true + undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} @@ -3864,14 +3868,14 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 - '@redocly/cli@2.12.1(@opentelemetry/api@1.9.0)(ajv@8.17.1)(core-js@3.47.0)': + '@redocly/cli@2.12.3(@opentelemetry/api@1.9.0)(ajv@8.17.1)(core-js@3.47.0)': dependencies: '@opentelemetry/exporter-trace-otlp-http': 0.202.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-node': 2.0.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.34.0 - '@redocly/openapi-core': 2.12.1(ajv@8.17.1) - '@redocly/respect-core': 2.12.1(ajv@8.17.1) + '@redocly/openapi-core': 2.12.3(ajv@8.17.1) + '@redocly/respect-core': 2.12.3(ajv@8.17.1) abort-controller: 3.0.0 chokidar: 3.6.0 colorette: 1.4.0 @@ -3883,13 +3887,14 @@ snapshots: https-proxy-agent: 7.0.6(supports-color@10.2.2) mobx: 6.15.0 pluralize: 8.0.0 - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - redoc: 2.5.1(core-js@3.47.0)(mobx@6.15.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(styled-components@6.1.19(react-dom@19.2.0(react@19.2.0))(react@19.2.0)) + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + redoc: 2.5.1(core-js@3.47.0)(mobx@6.15.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(styled-components@6.1.19(react-dom@19.2.1(react@19.2.1))(react@19.2.1)) semver: 7.7.3 set-cookie-parser: 2.7.2 simple-websocket: 9.1.0 - styled-components: 6.1.19(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + styled-components: 6.1.19(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + ulid: 3.0.2 undici: 6.22.0 yargs: 17.0.1 transitivePeerDependencies: @@ -3922,7 +3927,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@redocly/openapi-core@2.12.1(ajv@8.17.1)': + '@redocly/openapi-core@2.12.3(ajv@8.17.1)': dependencies: '@redocly/ajv': 8.17.1 '@redocly/config': 0.40.0 @@ -3936,12 +3941,12 @@ snapshots: transitivePeerDependencies: - ajv - '@redocly/respect-core@2.12.1(ajv@8.17.1)': + '@redocly/respect-core@2.12.3(ajv@8.17.1)': dependencies: '@faker-js/faker': 7.6.0 '@noble/hashes': 1.8.0 '@redocly/ajv': 8.17.1 - '@redocly/openapi-core': 2.12.1(ajv@8.17.1) + '@redocly/openapi-core': 2.12.3(ajv@8.17.1) better-ajv-errors: 1.2.0(ajv@8.17.1) colorette: 2.0.20 json-pointer: 0.6.2 @@ -5282,21 +5287,21 @@ snapshots: dependencies: obliterator: 2.0.5 - mobx-react-lite@4.1.1(mobx@6.15.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + mobx-react-lite@4.1.1(mobx@6.15.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1): dependencies: mobx: 6.15.0 - react: 19.2.0 - use-sync-external-store: 1.6.0(react@19.2.0) + react: 19.2.1 + use-sync-external-store: 1.6.0(react@19.2.1) optionalDependencies: - react-dom: 19.2.0(react@19.2.0) + react-dom: 19.2.1(react@19.2.1) - mobx-react@9.2.0(mobx@6.15.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + mobx-react@9.2.0(mobx@6.15.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1): dependencies: mobx: 6.15.0 - mobx-react-lite: 4.1.1(mobx@6.15.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - react: 19.2.0 + mobx-react-lite: 4.1.1(mobx@6.15.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) + react: 19.2.1 optionalDependencies: - react-dom: 19.2.0(react@19.2.0) + react-dom: 19.2.1(react@19.2.1) mobx@6.15.0: {} @@ -5661,20 +5666,20 @@ snapshots: minimist: 1.2.8 strip-json-comments: 2.0.1 - react-dom@19.2.0(react@19.2.0): + react-dom@19.2.1(react@19.2.1): dependencies: - react: 19.2.0 + react: 19.2.1 scheduler: 0.27.0 react-is@16.13.1: {} - react-tabs@6.1.0(react@19.2.0): + react-tabs@6.1.0(react@19.2.1): dependencies: clsx: 2.1.1 prop-types: 15.8.1 - react: 19.2.0 + react: 19.2.1 - react@19.2.0: {} + react@19.2.1: {} readable-stream@3.6.2: dependencies: @@ -5690,7 +5695,7 @@ snapshots: real-require@0.2.0: {} - redoc@2.5.1(core-js@3.47.0)(mobx@6.15.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(styled-components@6.1.19(react-dom@19.2.0(react@19.2.0))(react@19.2.0)): + redoc@2.5.1(core-js@3.47.0)(mobx@6.15.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1)(styled-components@6.1.19(react-dom@19.2.1(react@19.2.1))(react@19.2.1)): dependencies: '@redocly/openapi-core': 1.34.5(supports-color@10.2.2) classnames: 2.5.1 @@ -5703,19 +5708,19 @@ snapshots: mark.js: 8.11.1 marked: 4.3.0 mobx: 6.15.0 - mobx-react: 9.2.0(mobx@6.15.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + mobx-react: 9.2.0(mobx@6.15.0)(react-dom@19.2.1(react@19.2.1))(react@19.2.1) openapi-sampler: 1.6.2 path-browserify: 1.0.1 perfect-scrollbar: 1.5.6 polished: 4.3.1 prismjs: 1.30.0 prop-types: 15.8.1 - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) - react-tabs: 6.1.0(react@19.2.0) + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) + react-tabs: 6.1.0(react@19.2.1) slugify: 1.4.7 stickyfill: 1.1.1 - styled-components: 6.1.19(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + styled-components: 6.1.19(react-dom@19.2.1(react@19.2.1))(react@19.2.1) swagger2openapi: 7.0.8 url-template: 2.0.8 transitivePeerDependencies: @@ -6034,7 +6039,7 @@ snapshots: dependencies: '@tokenizer/token': 0.3.0 - styled-components@6.1.19(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + styled-components@6.1.19(react-dom@19.2.1(react@19.2.1))(react@19.2.1): dependencies: '@emotion/is-prop-valid': 1.2.2 '@emotion/unitless': 0.8.1 @@ -6042,8 +6047,8 @@ snapshots: css-to-react-native: 3.2.0 csstype: 3.1.3 postcss: 8.4.49 - react: 19.2.0 - react-dom: 19.2.0(react@19.2.0) + react: 19.2.1 + react-dom: 19.2.1(react@19.2.1) shallowequal: 1.1.0 stylis: 4.3.2 tslib: 2.6.2 @@ -6178,6 +6183,8 @@ snapshots: uint8array-extras@1.5.0: {} + ulid@3.0.2: {} + undici-types@6.21.0: {} undici-types@7.16.0: {} @@ -6194,9 +6201,9 @@ snapshots: url-template@2.0.8: {} - use-sync-external-store@1.6.0(react@19.2.0): + use-sync-external-store@1.6.0(react@19.2.1): dependencies: - react: 19.2.0 + react: 19.2.1 util-deprecate@1.0.2: {} From e12879391350bb9ff33327a7f28aecebf889424f Mon Sep 17 00:00:00 2001 From: NigeParis Date: Fri, 5 Dec 2025 19:01:42 +0100 Subject: [PATCH 5/9] going home end of day --- frontend/src/pages/chat/chat.ts | 51 +++++++++++++++++---------------- src/chat/src/app.ts | 27 +++++++++-------- 2 files changed, 42 insertions(+), 36 deletions(-) diff --git a/frontend/src/pages/chat/chat.ts b/frontend/src/pages/chat/chat.ts index 8d380f1..5a514cb 100644 --- a/frontend/src/pages/chat/chat.ts +++ b/frontend/src/pages/chat/chat.ts @@ -58,7 +58,7 @@ function addMessage(text: string) { return ; }; -async function isLoggedIn() { +function isLoggedIn() { return getUser() || null; }; @@ -74,11 +74,13 @@ async function windowStateHidden() { socketId.emit('client_left', { user: userName?.name, why: 'tab window hidden - socket not dead', - }); + }); return; }; -async function windowStateVisable() { +async function windowStateVisable() { + + const buddies = document.getElementById('div-buddies') as HTMLDivElement; const socketId = __socket || undefined; let oldName = localStorage.getItem("oldName") || undefined; console.log("%c WINDOW VISIBLE - oldName :'" + oldName + "'", color.green); @@ -91,6 +93,9 @@ async function windowStateVisable() { userName: oldName, user: user?.name, }); + buddies.innerHTML = ''; + buddies.textContent = ''; + //connected(socketId); setTitle('Chat Page'); return; }; @@ -128,21 +133,21 @@ async function listBuddies(buddies: HTMLDivElement, listBuddies: string) { if (!buddies) return; const sendtextbox = document.getElementById('t-chat-window') as HTMLButtonElement; + const buddiesElement = document.createElement("div-buddies-list"); + buddiesElement.textContent = listBuddies + '\n'; + const user = getUser()?.name ?? ""; - const messageElement = document.createElement("div-buddies-list"); - messageElement.textContent = listBuddies + '\n'; - - - // ✔ Add click-to-copy - messageElement.style.cursor = "pointer"; // optional visual hint - messageElement.addEventListener("click", () => { + buddiesElement.style.cursor = "pointer"; + buddiesElement.addEventListener("click", () => { navigator.clipboard.writeText(listBuddies); - sendtextbox.value = `@${listBuddies}: `; - console.log("Copied to clipboard:", listBuddies); - sendtextbox.focus(); // move cursor into the box + if (listBuddies !== user && user !== "") { + sendtextbox.value = `@${listBuddies}: `; + console.log("Copied to clipboard:", listBuddies); + sendtextbox.focus(); + } }); - buddies.appendChild(messageElement); + buddies.appendChild(buddiesElement); buddies.scrollTop = buddies.scrollHeight; console.log(`Added buddies: ${listBuddies}`); } @@ -150,7 +155,7 @@ async function listBuddies(buddies: HTMLDivElement, listBuddies: string) { function waitSocketConnected(socket: Socket): Promise { return new Promise(resolve => { - if (socket.connected) return resolve(); // already connected + if (socket.connected) return resolve(); socket.on("connect", () => resolve()); }); }; @@ -176,10 +181,10 @@ function quitChat (socket: Socket) { }; -const bconnected = document.getElementById('b-help') as HTMLButtonElement; -if (bconnected) { - bconnected.click(); -} +// const bconnected = document.getElementById('b-help') as HTMLButtonElement; +// if (bconnected) { +// bconnected.click(); +// } function logout(socket: Socket) { socket.emit("logout"); // notify server @@ -221,7 +226,7 @@ async function connected(socket: Socket): Promise { console.log('%coldUser:',color.yellow, oldUser); if (loggedIn?.name === undefined) {console.log('');return ;} oldUser = loggedIn.name ?? ""; - const res = await client.guestLogin(); + // const res = await client.guestLogin(); let user = await updateUser(); console.log('%cUser?name:',color.yellow, user?.name); localStorage.setItem("oldName", oldUser); @@ -365,10 +370,10 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn window.addEventListener("focus", () => { //nst bwhoami = document.getElementById('b-whoami') as HTMLButtonElement; if (window.location.pathname === '/app/chat') { + connected(socket); console.log("%cWindow is focused on /chat:" + socket.id, color.green); if (socket.id) { windowStateVisable(); - connected(socket); } toggle = true; } @@ -385,8 +390,7 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn // setInterval(async () => { // //connected(socket); // },10000); // every 10 seco - - socket.on('listBud', (myBuddies: string) => { + socket.on('listBud', async (myBuddies: string) => { const buddies = document.getElementById('div-buddies') as HTMLDivElement; console.log('List buddies connected ', myBuddies); listBuddies(buddies,myBuddies); @@ -455,7 +459,6 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn quitChat(socket); break; default: - const user = getUser()?.name; // Ensure we have a user AND socket is connected if (!user || !socket.connected) return; diff --git a/src/chat/src/app.ts b/src/chat/src/app.ts index a798de2..f85f2ea 100644 --- a/src/chat/src/app.ts +++ b/src/chat/src/app.ts @@ -132,16 +132,16 @@ async function onReady(fastify: FastifyInstance) { seen.add(username.user); count++; // console.log(color.green,"count: ", count); - console.log(color.yellow, 'Client:', color.reset, username.user); + // console.log(color.yellow, 'Client:', color.reset, username.user); const targetSocketId = target; io.to(targetSocketId!).emit('listBud', username.user); - console.log( - color.yellow, - 'Chat Socket ID:', - color.reset, - socketId, - ); + // console.log( + // color.yellow, + // 'Chat Socket ID:', + // color.reset, + // socketId, + // ); continue; } @@ -203,11 +203,13 @@ async function onReady(fastify: FastifyInstance) { console.log(color.yellow, `User: '${atUser}' (No user the same is found): '${data.command}' `); continue; } - s.emit('MsgObjectServer', { message: data }); - if (senderSocket) - senderSocket.emit('privMessageCopy',`${data.command}: ${data.text}🔒`); - // Debug logs - console.log(color.green, 'Priv to:', clientInfo.user); + if (data.text !== "") { + s.emit('MsgObjectServer', { message: data }); + if (senderSocket) + senderSocket.emit('privMessageCopy',`${data.command}: ${data.text}🔒`); + // Debug logs + } + console.log(color.green, 'Priv to:', data.text); } }); } @@ -361,6 +363,7 @@ async function onReady(fastify: FastifyInstance) { SenderWindowID: socket.id, }; console.log(color.blue, 'BROADCASTS OUT :', obj.SenderWindowID); + broadcast(obj, obj.SenderWindowID); // clientChat.delete(obj.user); } From 3550303a93d56114aebe510bf0e47b19b77ee07c Mon Sep 17 00:00:00 2001 From: NigeParis Date: Sat, 6 Dec 2025 15:33:11 +0100 Subject: [PATCH 6/9] Added profil popup - WIP page opens on double click of the name of buddy --- frontend/src/chat/chat.css | 29 +++++++++++++++++++ frontend/src/pages/chat/chat.html | 6 ++++ frontend/src/pages/chat/chat.ts | 48 ++++++++++++++++++++++++++++--- src/chat/src/app.ts | 48 +++++++++++-------------------- 4 files changed, 95 insertions(+), 36 deletions(-) diff --git a/frontend/src/chat/chat.css b/frontend/src/chat/chat.css index 9c3bccd..e12d5f3 100644 --- a/frontend/src/chat/chat.css +++ b/frontend/src/chat/chat.css @@ -4,6 +4,8 @@ src: url("/fonts/NimbusMonoL.woff2") format("woff2"); } +@tailwind utilities; + .btn-style { @apply w-[100px] @@ -189,4 +191,31 @@ div-private { @apply text-blue-800; +} + +.popUpBox { + @apply + bg-white + p-6 rounded-xl + shadow-xl + w-[800px] + h-[350px] + p-[10px] + border-1 + border-black + +} + +.profilPopup { + @apply + fixed + inset-0 + bg-black/50 + flex + justify-center + items-center; +} + +.hidden{ + display: none; } \ No newline at end of file diff --git a/frontend/src/pages/chat/chat.html b/frontend/src/pages/chat/chat.html index 2882ae5..6314eff 100644 --- a/frontend/src/pages/chat/chat.html +++ b/frontend/src/pages/chat/chat.html @@ -31,6 +31,12 @@

Charlie

--> + diff --git a/frontend/src/pages/chat/chat.ts b/frontend/src/pages/chat/chat.ts index 5a514cb..406a731 100644 --- a/frontend/src/pages/chat/chat.ts +++ b/frontend/src/pages/chat/chat.ts @@ -110,13 +110,23 @@ function parseCmdMsg(msgText: string): string[] | undefined { command[1] = msgText; return command; } - const noArgCommands = ['@quit', '@cls', '@profile']; + const noArgCommands = ['@quit', '@who', '@cls']; if (noArgCommands.includes(msgText)) { command[0] = msgText; command[1] = ''; return command; } - const colonIndex = msgText.indexOf(":"); + + const ArgCommands = ['@profil', '@block']; + const userName = msgText.indexOf(" "); + const cmd2 = msgText.slice(0, userName).trim() ?? ""; + const user = msgText.slice(userName + 1).trim(); + if (ArgCommands.includes(cmd2)) { + command[0] = cmd2; + command[1] = user; + return command; + } + const colonIndex = msgText.indexOf(":"); if (colonIndex === -1) { command[0] = msgText; command[1] = ''; @@ -147,6 +157,12 @@ async function listBuddies(buddies: HTMLDivElement, listBuddies: string) { } }); + buddiesElement.addEventListener("dblclick", () => { + console.log("Open profile:", listBuddies); + openProfilePopup(`Profil: ${listBuddies}`); + + }); + buddies.appendChild(buddiesElement); buddies.scrollTop = buddies.scrollHeight; console.log(`Added buddies: ${listBuddies}`); @@ -244,7 +260,7 @@ async function connected(socket: Socket): Promise { async function whoami(socket: Socket) { try { const chatWindow = document.getElementById("t-chatbox") as HTMLDivElement; - const loggedIn = await isLoggedIn(); + const loggedIn = isLoggedIn(); const res = await client.guestLogin(); switch (res.kind) { @@ -271,6 +287,15 @@ async function whoami(socket: Socket) { } }; +async function openProfilePopup(profil: string) { + + const modalname = document.getElementById("modal-name") ?? null; + if (modalname) + modalname.innerHTML = profil; + const profilList = document.getElementById("profile-modal") ?? null; + if (profilList) + profilList.classList.remove("hidden"); +} function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn { @@ -438,6 +463,16 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn console.log('unknown response: ', value); } + + + 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"); + }); + // Send button sendButton?.addEventListener("click", () => { if (sendtextbox && sendtextbox.value.trim()) { @@ -452,6 +487,10 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn case '@who': whoami(socket); break; + case '@profil': + if (`${msgCommand[1]}`) + openProfilePopup(`Profil: ${msgCommand[1]}`); + break; case '@cls': chatWindow.innerHTML = ''; break; @@ -501,7 +540,8 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn } }); - // Whoami button to display user name + // Whoami button to display user name addMessage(msgCommand[0]); + bwhoami?.addEventListener('click', async () => { whoami(socket); }); diff --git a/src/chat/src/app.ts b/src/chat/src/app.ts index f85f2ea..d2a7cae 100644 --- a/src/chat/src/app.ts +++ b/src/chat/src/app.ts @@ -144,43 +144,32 @@ async function onReady(fastify: FastifyInstance) { // ); continue; } - // If no io provided, assume entries in the map are valid and count them. count++; - console.log( - color.red, - 'Client (unverified):', - color.reset, - username, - ); - console.log( - color.red, - 'Chat Socket ID (unverified):', - color.reset, - socketId, - ); + console.log(color.red, 'DEBUG LOG: - Client (unverified):', color.reset, username ); + console.log(color.red, 'DEBUG LOG: - Chat Socket ID (unverified):', color.reset, socketId); } return count; } function broadcast(data: ClientMessage, sender?: string) { fastify.io.fetchSockets().then((sockets) => { - for (const s of sockets) { + for (const socket of sockets) { // Skip sender's own socket - if (s.id === sender) continue; + if (socket.id === sender) continue; // Get client name from map - const clientInfo = clientChat.get(s.id); + const clientInfo = clientChat.get(socket.id); if (!clientInfo?.user) { - console.log(color.yellow, `Skipping socket ${s.id} (no user found)`); + console.log(color.yellow, `Skipping socket ${socket.id} (no user found)`); continue; } // Emit structured JSON object - s.emit('MsgObjectServer', { message: data }); + socket.emit('MsgObjectServer', { message: data }); // Debug logs - console.log(color.green, 'Broadcast to:', clientInfo.user); - console.log(' Target socket ID:', s.id); - console.log(' Target rooms:', [...s.rooms]); - console.log(' Sender socket ID:', sender ?? 'none'); + console.log(color.green, `'Broadcast to:', ${data.command} message: ${data.text}`); + // console.log('DEBUG - Target socket ID:', s.id); + // console.log('DEBUG - Target rooms:', [...s.rooms]); + // console.log('DEBUG - Sender socket ID:', sender ?? 'none'); } }); } @@ -200,16 +189,17 @@ async function onReady(fastify: FastifyInstance) { let user: string = clientChat.get(s.id)?.user ?? ""; const atUser = `@${user}`; if (atUser !== data.command || atUser === "") { - console.log(color.yellow, `User: '${atUser}' (No user the same is found): '${data.command}' `); + console.log(color.yellow, `DEBUG LOG: User: '${atUser}' command NOT FOUND: '${data.command[0]}' `); continue; } if (data.text !== "") { s.emit('MsgObjectServer', { message: data }); + console.log(color.yellow, `DEBUG LOG: User: '${atUser}' command FOUND: '${data.command}' `); if (senderSocket) senderSocket.emit('privMessageCopy',`${data.command}: ${data.text}🔒`); // Debug logs } - console.log(color.green, 'Priv to:', data.text); + console.log(color.green, `'Priv to:', ${data.command} message: ${data.text}`); } }); } @@ -244,12 +234,7 @@ async function onReady(fastify: FastifyInstance) { // Send object directly — DO NOT wrap it in a string broadcast(obj, obj.SenderWindowID); - console.log( - color.red, - 'connected in the Chat :', - connectedUser(fastify.io), - color.reset, - ); + console.log(color.red, 'DEBUG LOG: connected in the Chat :', connectedUser(fastify.io), color.reset); }); socket.on('testend', (sock_id_cl: string) => { @@ -288,8 +273,7 @@ async function onReady(fastify: FastifyInstance) { // }; if (client) { client.user = userFromFrontend.user; - console.log(color.green, 'client.user is: ', client.user); - + console.log(color.green, `'DEBUG LOG: client.user is, '${client.user}'`); } } }); From 01f85c2779052970bf28271648cf97ebbf1212a7 Mon Sep 17 00:00:00 2001 From: NigeParis Date: Sat, 6 Dec 2025 16:33:28 +0100 Subject: [PATCH 7/9] getProfil Function return html for profil page --- frontend/src/chat/chat.css | 4 ++-- frontend/src/pages/chat/chat.html | 4 ++-- frontend/src/pages/chat/chat.ts | 25 ++++++++++++++++++++++--- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/frontend/src/chat/chat.css b/frontend/src/chat/chat.css index e12d5f3..d98a4fb 100644 --- a/frontend/src/chat/chat.css +++ b/frontend/src/chat/chat.css @@ -1,8 +1,8 @@ @import "tailwindcss"; -@font-face { +/* @font-face { font-family: "Nimbus Mono L"; src: url("/fonts/NimbusMonoL.woff2") format("woff2"); -} +} */ @tailwind utilities; diff --git a/frontend/src/pages/chat/chat.html b/frontend/src/pages/chat/chat.html index 6314eff..bf06259 100644 --- a/frontend/src/pages/chat/chat.html +++ b/frontend/src/pages/chat/chat.html @@ -28,12 +28,12 @@
+

Charlie

-->Marks
diff --git a/frontend/src/pages/chat/chat.ts b/frontend/src/pages/chat/chat.ts index 406a731..ac7ec31 100644 --- a/frontend/src/pages/chat/chat.ts +++ b/frontend/src/pages/chat/chat.ts @@ -58,10 +58,20 @@ function addMessage(text: string) { return ; }; +function clearText() { + const chatWindow = document.getElementById("t-chatbox") as HTMLDivElement; + if (!chatWindow) return; + chatWindow.innerHTML = ""; +} + function isLoggedIn() { return getUser() || null; }; +function getProfil(user: string): string { + return `Profil: ${user}
` +} + async function windowStateHidden() { const socketId = __socket || undefined; // let oldName = localStorage.getItem("oldName") ?? undefined; @@ -159,8 +169,14 @@ async function listBuddies(buddies: HTMLDivElement, listBuddies: string) { buddiesElement.addEventListener("dblclick", () => { console.log("Open profile:", listBuddies); - openProfilePopup(`Profil: ${listBuddies}`); - + const profile: string = getProfil(listBuddies); + openProfilePopup(`${profile}`); + setTimeout(() => { + const clearTextBtn = document.querySelector("#popup-b-clear"); + clearTextBtn?.addEventListener("click", () => { + clearText(); + }); + }, 0) }); buddies.appendChild(buddiesElement); @@ -289,12 +305,15 @@ async function whoami(socket: Socket) { async function openProfilePopup(profil: string) { + const modalname = document.getElementById("modal-name") ?? null; if (modalname) - modalname.innerHTML = profil; + modalname.innerHTML = `${profil}`; const profilList = document.getElementById("profile-modal") ?? null; if (profilList) profilList.classList.remove("hidden"); + // The popup now exists → attach the event + } From 2959dca576c3ef5cc1c239ea6d4058f841a1ca6f Mon Sep 17 00:00:00 2001 From: NigeParis Date: Mon, 8 Dec 2025 15:43:43 +0100 Subject: [PATCH 8/9] profil displays basic info if guest or not --- frontend/src/chat/chat.css | 7 +++ frontend/src/pages/chat/chat.ts | 73 +++++++++++++++++----- src/chat/src/app.ts | 107 ++++++++++++++++++++++++++++---- 3 files changed, 159 insertions(+), 28 deletions(-) diff --git a/frontend/src/chat/chat.css b/frontend/src/chat/chat.css index d98a4fb..5e9b65e 100644 --- a/frontend/src/chat/chat.css +++ b/frontend/src/chat/chat.css @@ -216,6 +216,13 @@ div-private { items-center; } +.popup-b-clear { + @apply + absolute + bottom-42 + right-12 +} + .hidden{ display: none; } \ No newline at end of file diff --git a/frontend/src/pages/chat/chat.ts b/frontend/src/pages/chat/chat.ts index ac7ec31..4b8bd2f 100644 --- a/frontend/src/pages/chat/chat.ts +++ b/frontend/src/pages/chat/chat.ts @@ -68,8 +68,30 @@ function isLoggedIn() { return getUser() || null; }; -function getProfil(user: string): string { - return `Profil: ${user}
` +function actionBtnPopUp() { + setTimeout(() => { + const clearTextBtn = document.querySelector("#popup-b-clear"); + clearTextBtn?.addEventListener("click", () => { + clearText(); + }); + }, 0) +} + +// getProfil get the profil of user +function getProfil(socket: Socket, user: string) { + if (!socket.connected) return; + const profil = { + command: '@profil', + destination: 'profilMessage', + type: "chat", + user: user, + token: document.cookie ?? "", + text: user, + timestamp: Date.now(), + SenderWindowID: socket.id, + }; + // addMessage(JSON.stringify(profil)); + socket.emit('profilMessage', JSON.stringify(profil)); } async function windowStateHidden() { @@ -149,7 +171,7 @@ function parseCmdMsg(msgText: string): string[] | undefined { return command; } -async function listBuddies(buddies: HTMLDivElement, listBuddies: string) { +async function listBuddies(socket: Socket, buddies: HTMLDivElement, listBuddies: string) { if (!buddies) return; const sendtextbox = document.getElementById('t-chat-window') as HTMLButtonElement; @@ -169,14 +191,15 @@ async function listBuddies(buddies: HTMLDivElement, listBuddies: string) { buddiesElement.addEventListener("dblclick", () => { console.log("Open profile:", listBuddies); - const profile: string = getProfil(listBuddies); - openProfilePopup(`${profile}`); - setTimeout(() => { - const clearTextBtn = document.querySelector("#popup-b-clear"); - clearTextBtn?.addEventListener("click", () => { - clearText(); - }); - }, 0) + getProfil(socket, listBuddies); + // openProfilePopup(`${profile}`); + // setTimeout(() => { + // const clearTextBtn = document.querySelector("#popup-b-clear"); + // clearTextBtn?.addEventListener("click", () => { + // clearText(); + // }); + // }, 0) + // actionBtnPopUp(); }); buddies.appendChild(buddiesElement); @@ -313,7 +336,6 @@ async function openProfilePopup(profil: string) { if (profilList) profilList.classList.remove("hidden"); // The popup now exists → attach the event - } @@ -394,7 +416,12 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn console.log("Getuser():", getUser()); }); + socket.on('profilMessage', (profil) => { + openProfilePopup(profil); + actionBtnPopUp(); + }); + socket.on('logout', () => { quitChat(socket); }); @@ -437,7 +464,7 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn socket.on('listBud', async (myBuddies: string) => { const buddies = document.getElementById('div-buddies') as HTMLDivElement; console.log('List buddies connected ', myBuddies); - listBuddies(buddies,myBuddies); + listBuddies(socket, buddies, myBuddies); }); socket.once('welcome', (data) => { @@ -507,8 +534,22 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn whoami(socket); break; case '@profil': - if (`${msgCommand[1]}`) - openProfilePopup(`Profil: ${msgCommand[1]}`); + getProfil(socket, msgCommand[1]); + // Ensure we have a user AND socket is connected + // if (!socket.connected) return; + // const profil = { + // command: msgCommand[0], + // destination: 'profil', + // type: "chat", + // user: msgCommand[1], + // token: document.cookie ?? "", + // text: msgCommand[1], + // timestamp: Date.now(), + // SenderWindowID: socket.id, + // }; + // //socket.emit('MsgObjectServer', message); + // addMessage(JSON.stringify(profil)); + // socket.emit('profilMessage', JSON.stringify(profil)); break; case '@cls': chatWindow.innerHTML = ''; @@ -524,7 +565,7 @@ function handleChat(_url: string, _args: RouteHandlerParams): RouteHandlerReturn command: msgCommand[0], destination: '', type: "chat", - user, + user: user, token: document.cookie ?? "", text: msgCommand[1], timestamp: Date.now(), diff --git a/src/chat/src/app.ts b/src/chat/src/app.ts index d2a7cae..a02fac9 100644 --- a/src/chat/src/app.ts +++ b/src/chat/src/app.ts @@ -6,6 +6,7 @@ import * as auth from '@shared/auth'; import * as swagger from '@shared/swagger'; import * as utils from '@shared/utils'; import { Server, Socket } from 'socket.io'; +import type { User } from '@shared/database/mixin/user'; // colors for console.log export const color = { @@ -84,6 +85,7 @@ declare module 'fastify' { hello: (message: string) => string; MsgObjectServer: (data: { message: ClientMessage }) => void; privMessage: (data: string) => void; + profilMessage: (data: string) => void; privMessageCopy: (msg: string) => void; message: (msg: string) => void; listBud: (msg: string) => void; @@ -146,7 +148,7 @@ async function onReady(fastify: FastifyInstance) { } // If no io provided, assume entries in the map are valid and count them. count++; - console.log(color.red, 'DEBUG LOG: - Client (unverified):', color.reset, username ); + console.log(color.red, 'DEBUG LOG: - Client (unverified):', color.reset, username); console.log(color.red, 'DEBUG LOG: - Chat Socket ID (unverified):', color.reset, socketId); } return count; @@ -174,7 +176,58 @@ async function onReady(fastify: FastifyInstance) { }); } + function formatTimestamp(ms: number) { + const d = new Date(ms); + return d.toLocaleString('fr-FR', { timeZone: 'Europe/Paris' }); + } + function getUserByName(users: User[], name: string) { + return users.find(u => u.name === name) || null; + } + + + // this function returns html the profil pop up in CHAT of a user 'nickname unique' TODO .... + async function getProfil(user: string): Promise { + let profilHtmlPopup = '404: Error: Profil not found'; + const sockets = await fastify.io.fetchSockets(); + const users: User[] = fastify.db.getAllUsers() ?? []; + + // const senderSocket = sockets.find(socket => socket.id === user); + for (const socket of sockets) { + const clientInfo = clientChat?.get(socket.id); + const allUsers: User | null = getUserByName(users, user); + if (clientInfo?.user === allUsers?.name) { + const lastSeen = formatTimestamp(clientInfo?.lastSeen ?? 0); + console.log(color.yellow, `'Clientinfo.user: '${lastSeen}' user: '${user}'`); + profilHtmlPopup = `
+ Profil of ${clientInfo?.user}
+ Login Name: '${allUsers?.login ?? 'Guest'}' +
+ +
Joined: xx/xx/xx
+
Last connection: ${lastSeen}
+
About: No description
+ `; + break; + } + }; + return profilHtmlPopup; + }; + + function sendProfil(data: ClientMessage, clientProfil?: string) { + + fastify.io.fetchSockets().then((sockets) => { + const senderSocket = sockets.find(socket => socket.id === clientProfil); + for (const socket of sockets) { + const clientInfo = clientChat.get(socket.id); + if (clientInfo?.user === data.user) { + if (senderSocket) { + socket.emit('profilMessage', `${data.text}`); + } + } + } + }); + } function sendPrivMessage(data: ClientMessage, sender?: string) { fastify.io.fetchSockets().then((sockets) => { @@ -186,20 +239,20 @@ async function onReady(fastify: FastifyInstance) { console.log(color.yellow, `Skipping socket ${s.id} (no user found)`); continue; } - let user: string = clientChat.get(s.id)?.user ?? ""; + const user: string = clientChat.get(s.id)?.user ?? ''; const atUser = `@${user}`; - if (atUser !== data.command || atUser === "") { + if (atUser !== data.command || atUser === '') { console.log(color.yellow, `DEBUG LOG: User: '${atUser}' command NOT FOUND: '${data.command[0]}' `); continue; } - if (data.text !== "") { + if (data.text !== '') { s.emit('MsgObjectServer', { message: data }); console.log(color.yellow, `DEBUG LOG: User: '${atUser}' command FOUND: '${data.command}' `); - if (senderSocket) - senderSocket.emit('privMessageCopy',`${data.command}: ${data.text}🔒`); - // Debug logs + if (senderSocket) { + senderSocket.emit('privMessageCopy', `${data.command}: ${data.text}🔒`); + } } - console.log(color.green, `'Priv to:', ${data.command} message: ${data.text}`); + console.log(color.green, `DEBUG LOG: 'Priv to:', ${data.command} message: ${data.text}`); } }); } @@ -355,12 +408,12 @@ async function onReady(fastify: FastifyInstance) { socket.on('privMessage', (data) => { - const clientName: string = clientChat.get(socket.id)?.user || ""; - const prvMessage: ClientMessage = JSON.parse(data) || ""; + const clientName: string = clientChat.get(socket.id)?.user || ''; + const prvMessage: ClientMessage = JSON.parse(data) || ''; console.log( color.blue, `DEBUG LOG: ClientName: '${clientName}' id Socket: '${socket.id}' target Name:`, - prvMessage.command + prvMessage.command, ); if (clientName !== null) { @@ -374,12 +427,42 @@ async function onReady(fastify: FastifyInstance) { timestamp: Date.now(), SenderWindowID: socket.id, }; - console.log(color.blue, 'PRIV MESSAGE OUT :', obj.SenderWindowID); + console.log(color.blue, 'DEBUG LOG: PRIV MESSAGE OUT :', obj.SenderWindowID); sendPrivMessage(obj, obj.SenderWindowID); // clientChat.delete(obj.user); } }); + socket.on('profilMessage', async (data) => { + const clientName: string = clientChat.get(socket.id)?.user || ''; + const profilMessage: ClientMessage = JSON.parse(data) || ''; + const users: User[] = fastify.db.getAllUsers() ?? []; + console.log(color.yellow, 'DEBUG LOG: ALL USERS EVER CONNECTED:', users); + console.log( + color.blue, + `DEBUG LOG: ClientName: '${clientName}' id Socket: '${socket.id}' target profil:`, + profilMessage.user, + ); + const profileHtml: string = await getProfil(profilMessage.user); + if (clientName !== null) { + const testuser: User | null = getUserByName(users, profilMessage.user); + console.log(color.yellow, 'user:', testuser?.login ?? 'Guest'); + const obj = { + command: profilMessage.command, + destination: 'profilMsg', + type: 'chat', + user: clientName, + token: '', + text: profileHtml, + timestamp: Date.now(), + SenderWindowID: socket.id, + }; + console.log(color.blue, 'DEBUG - profil message MESSAGE OUT :', obj.SenderWindowID); + sendProfil(obj, obj.SenderWindowID); + // clientChat.delete(obj.user); + } + }); + socket.on('client_entered', (data) => { // data may be undefined (when frontend calls emit with no payload) From db409abe7c00734656d84b347bb22b9bf43696d7 Mon Sep 17 00:00:00 2001 From: NigeParis Date: Mon, 8 Dec 2025 16:26:39 +0100 Subject: [PATCH 9/9] profil now displays info on players not connected --- src/chat/src/app.ts | 40 ++++++++++++++++------------------------ 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/src/chat/src/app.ts b/src/chat/src/app.ts index a02fac9..033e17c 100644 --- a/src/chat/src/app.ts +++ b/src/chat/src/app.ts @@ -176,10 +176,10 @@ async function onReady(fastify: FastifyInstance) { }); } - function formatTimestamp(ms: number) { - const d = new Date(ms); - return d.toLocaleString('fr-FR', { timeZone: 'Europe/Paris' }); - } + // function formatTimestamp(ms: number) { + // const d = new Date(ms); + // return d.toLocaleString('fr-FR', { timeZone: 'Europe/Paris' }); + // } function getUserByName(users: User[], name: string) { return users.find(u => u.name === name) || null; @@ -189,28 +189,20 @@ async function onReady(fastify: FastifyInstance) { // this function returns html the profil pop up in CHAT of a user 'nickname unique' TODO .... async function getProfil(user: string): Promise { let profilHtmlPopup = '404: Error: Profil not found'; - const sockets = await fastify.io.fetchSockets(); const users: User[] = fastify.db.getAllUsers() ?? []; + const allUsers: User | null = getUserByName(users, user); + console.log(color.yellow, `'userFound is:'${allUsers?.name}`); + if (user === allUsers?.name) { + console.log(color.yellow, `'login Name: '${allUsers.login}' user: '${user}'`); + profilHtmlPopup = `
+ Profil of ${allUsers.name}
+ Login Name: '${allUsers?.login ?? 'Guest'}' +
+ +
About: No description
+ `; + } - // const senderSocket = sockets.find(socket => socket.id === user); - for (const socket of sockets) { - const clientInfo = clientChat?.get(socket.id); - const allUsers: User | null = getUserByName(users, user); - if (clientInfo?.user === allUsers?.name) { - const lastSeen = formatTimestamp(clientInfo?.lastSeen ?? 0); - console.log(color.yellow, `'Clientinfo.user: '${lastSeen}' user: '${user}'`); - profilHtmlPopup = `
- Profil of ${clientInfo?.user}
- Login Name: '${allUsers?.login ?? 'Guest'}' -
- -
Joined: xx/xx/xx
-
Last connection: ${lastSeen}
-
About: No description
- `; - break; - } - }; return profilHtmlPopup; };