diff --git a/frontend/index.html b/frontend/index.html index 59f7892..2182ae4 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -37,6 +37,7 @@ ⭕ Tic-Tac-Toe ▮•▮ Ping Pong 🏆 Tournaments + 😎 Friends 🚪 Logout diff --git a/frontend/src/auth.ts b/frontend/src/auth.ts index 5c97eab..63491fc 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, isNullish } from "@app/utils"; +import { ensureWindowState, isNullish, updateFriendsList } from "@app/utils"; import { handleRoute, navigateTo } from "./routing"; cookie.remove("pkce"); @@ -105,10 +105,11 @@ if (!window.__state._headerProfile) { window.__state._reloadOnAuthChange ??= false; if (!window.__state._reloadOnAuthChange) { - document.addEventListener("ft:userChange", () => { + document.addEventListener("ft:userChange", async () => { // if the last forced auth change is less than 1000 sec old -> we do nothing if (Date.now() - (window.__state.lastAuthChange ?? Date.now()) < 1000) return; + await updateFriendsList(); handleRoute(); }); window.__state._reloadOnAuthChange = true; diff --git a/frontend/src/chat/chat.ts b/frontend/src/chat/chat.ts index f3e2aa2..f52ce0b 100644 --- a/frontend/src/chat/chat.ts +++ b/frontend/src/chat/chat.ts @@ -39,6 +39,7 @@ let keysPressed: Record = {}; declare module "ft_state" { interface State { chatSock?: Socket; + friendList: { id: string; name: string }[]; } } @@ -82,18 +83,20 @@ 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; +const systemWindow = document.getElementById( + "chat-system-box", +) as HTMLDivElement; function chatKeyToggle() { let anti_flicker_control = false; - const chat_hide_key = 'escape'; - const chat_display_key = 'f2'; - const home_display_key = 'f8'; + const chat_hide_key = "escape"; + const chat_display_key = "f2"; + const home_display_key = "f8"; document.addEventListener("keydown", (event) => { if (event.repeat && keysPressed[chat_hide_key] === true) { anti_flicker_control = true; - return ; - }; + return; + } keysPressed[event.key.toLowerCase()] = true; }); document.addEventListener("keyup", (event) => { @@ -102,34 +105,33 @@ function chatKeyToggle() { anti_flicker_control = false; } }); - setInterval( () => { - if(keysPressed[chat_hide_key] === true) { - overlay.classList.remove("opacity-60"); - chatBox.classList.add("hidden"); - chatMessageIn?.classList.add("hidden"); - chatMessageIn!.textContent = ''; - profilList?.classList.add("hidden"); - windowStateHidden(); + setInterval(() => { + if (keysPressed[chat_hide_key] === true) { + overlay.classList.remove("opacity-60"); + chatBox.classList.add("hidden"); + chatMessageIn?.classList.add("hidden"); + chatMessageIn!.textContent = ""; + profilList?.classList.add("hidden"); + windowStateHidden(); } if (keysPressed[chat_display_key] === true) { - anti_flicker_control = false; - chatBox.classList.remove("hidden"); - overlay.classList.add("opacity-60"); - chatMessageIn?.classList.add("hidden"); - chatMessageIn!.textContent = ''; - let socket = window.__state.chatSock; - if (!socket) return; - connected(socket); - sendtextbox.focus(); - windowStateVisable(); - + anti_flicker_control = false; + chatBox.classList.remove("hidden"); + overlay.classList.add("opacity-60"); + chatMessageIn?.classList.add("hidden"); + chatMessageIn!.textContent = ""; + let socket = window.__state.chatSock; + if (!socket) return; + connected(socket); + sendtextbox.focus(); + windowStateVisable(); } - if (keysPressed[home_display_key] === true) { - navigateTo('/app/'); + if (keysPressed[home_display_key] === true) { + navigateTo("/app/"); quitChat(); } - }, 1000/10); -}; + }, 1000 / 10); +} function initChatSocket() { let socket = getSocket(); @@ -152,9 +154,10 @@ function initChatSocket() { !profilList || !sendButton || !sendtextbox || - !systemWindow - ) return showError("fatal error"); - + !systemWindow + ) + return showError("fatal error"); + // Listen for the 'connect' event socket.on("connect", async () => { await waitSocketConnected(socket); @@ -202,10 +205,10 @@ function initChatSocket() { if (socket) { connected(socket); } - + if (chatWindow && data.message.destination === "") { chatMessageIn?.classList.remove("hidden"); - chatMessageIn!.textContent = '🔵'; + chatMessageIn!.textContent = "🔵"; const messageElement = document.createElement("div"); messageElement.textContent = `${data.message.user}: ${data.message.text}`; chatWindow.appendChild(messageElement); @@ -214,7 +217,7 @@ function initChatSocket() { if (chatWindow && data.message.destination === "privateMsg") { chatMessageIn?.classList.remove("hidden"); - chatMessageIn!.textContent = '🔴'; + chatMessageIn!.textContent = "🔴"; const messageElement = document.createElement("div-private"); messageElement.textContent = `🔒${data.message.user}: ${data.message.text}`; chatWindow.appendChild(messageElement); @@ -223,7 +226,7 @@ function initChatSocket() { if (chatWindow && data.message.destination === "inviteMsg") { chatMessageIn?.classList.remove("hidden"); - chatMessageIn!.textContent = '🟢'; + chatMessageIn!.textContent = "🟢"; const messageElement = document.createElement("div-private"); const chatWindow = document.getElementById( "t-chatbox", @@ -236,14 +239,13 @@ function initChatSocket() { if (systemWindow && data.message.destination === "system-info") { const messageElement = document.createElement("div"); messageElement.textContent = `${data.message.user}: ${data.message.text}`; - + // keep only last 10 while (systemWindow.children.length > MAX_SYSTEM_MESSAGES) { systemWindow.removeChild(systemWindow.firstChild!); } systemWindow.appendChild(messageElement); systemWindow.lastElementChild?.scrollIntoView({ block: "end" }); - } }); @@ -278,9 +280,9 @@ function initChatSocket() { if (blockUserBtn) { let message = ""; if (data.userState === "block") { - (message = "un-block"); + message = "un-block"; } else { - (message = "block"); + message = "block"; } blockUserBtn.textContent = message; } @@ -294,17 +296,15 @@ function initChatSocket() { const htmlBaliseRegex = /]*>[\s\S]*?<\/a>/; const htmlBaliseMatch = message.match(htmlBaliseRegex); - if (htmlBaliseMatch) - addInviteMessage(message); - else - addMessage(message); + if (htmlBaliseMatch) addInviteMessage(message); + else addMessage(message); }); //receives broadcast of the next GAME socket.on("nextGame", (message: string) => { openMessagePopup(message); }); - + //receives broadcast of the next GAME socket.on("tourStatus", (message: string) => { openMessagePopup(message); @@ -391,20 +391,20 @@ sendButton?.addEventListener("click", () => { } break; - case "@pong": + case "@pong": if (msgCommand[1] === "") { navigateTo("/app/pong/games"); quitChat(); - } + } break; - case "@ttt": + case "@ttt": if (msgCommand[1] === "") { navigateTo("/app/ttt/games"); quitChat(); - } + } break; - + case "@guest": if (!userId) { return; @@ -505,7 +505,6 @@ clearText?.addEventListener("click", () => { bquit?.addEventListener("click", () => { quitChat(); - }); myGames?.addEventListener("click", () => { @@ -527,7 +526,7 @@ sendtextbox.addEventListener("keydown", (event) => { } }); -chatButton!.addEventListener("click",() => { +chatButton!.addEventListener("click", () => { if (chatBox.classList.contains("hidden")) { chatBox.classList.toggle("hidden"); overlay.classList.add("opacity-60"); @@ -536,14 +535,14 @@ chatButton!.addEventListener("click",() => { if (!socket) return; connected(socket); chatMessageIn?.classList.add("hidden"); - chatMessageIn!.textContent = ''; - sendtextbox.focus(); + chatMessageIn!.textContent = ""; + sendtextbox.focus(); } else { chatBox.classList.toggle("hidden"); overlay.classList.remove("opacity-60"); windowStateHidden(); chatMessageIn?.classList.add("hidden"); - chatMessageIn!.textContent = ''; + chatMessageIn!.textContent = ""; } }); diff --git a/frontend/src/chat/chatHelperFunctions/actionBtnFriend.ts b/frontend/src/chat/chatHelperFunctions/actionBtnFriend.ts index c804154..8ca45ec 100644 --- a/frontend/src/chat/chatHelperFunctions/actionBtnFriend.ts +++ b/frontend/src/chat/chatHelperFunctions/actionBtnFriend.ts @@ -1,5 +1,8 @@ +import client from "@app/api"; import type { ClientProfil } from "../types_front"; import { Socket } from "socket.io-client"; +import { showError, showSuccess } from "@app/toast"; +import { getFriendList, updateFriendsList } from "@app/utils"; /** * function listens for a click on the TTT game History button @@ -8,20 +11,25 @@ import { Socket } from "socket.io-client"; **/ export function actionBtnFriend(profile: ClientProfil, senderSocket: Socket) { - setTimeout(() => { - const friend = document.querySelector("#btn-friend"); - friend?.addEventListener("click", () => { - - if (friend.textContent = "friend") { - friend.textContent = "not-friend" - console.log('friend'); - } - else { - friend.textContent = "not-friend" - console.log('Not a friend'); - - } - - }); - }, 0) -}; \ No newline at end of file + setTimeout(() => { + const friend = document.querySelector("#btn-friend"); + friend?.addEventListener("click", async () => { + let friendList = getFriendList(); + if (!friendList.some(v => v.id === profile.userID!)) { + let req = await client.addFriend({ user: profile.userID! }); + if (req.kind === 'success') + showSuccess('Successfully added a new Friend') + else + showError('Failed to add a new Friend'); + } + else { + let req = await client.removeFriend({ user: profile.userID! }); + if (req.kind === 'success') + showSuccess('Successfully removed a Friend') + else + showError('Failed to remove a Friend'); + } + await updateFriendsList(); + }); + }, 0) +}; diff --git a/frontend/src/pages/friendList/friendList.html b/frontend/src/pages/friendList/friendList.html new file mode 100644 index 0000000..68f64db --- /dev/null +++ b/frontend/src/pages/friendList/friendList.html @@ -0,0 +1,9 @@ +
+
+

+ FriendList +

+
+
+
diff --git a/frontend/src/pages/friendList/friendList.ts b/frontend/src/pages/friendList/friendList.ts new file mode 100644 index 0000000..83fb2df --- /dev/null +++ b/frontend/src/pages/friendList/friendList.ts @@ -0,0 +1,41 @@ +import { addRoute, navigateTo, setTitle, type RouteHandlerParams, type RouteHandlerReturn } from "@app/routing"; +import page from './friendList.html?raw'; +import { getFriendList, isNullish, updateFriendsList } from "@app/utils"; +import client from "@app/api"; +import { updateUser } from "@app/auth"; +import { showError } from "@app/toast"; + + +async function friends(_url: string, args: RouteHandlerParams): Promise { + setTitle("Tic Tac Toe Games"); + let user = await updateUser(); + if (isNullish(user)) { + return { html: ' You aren\'t logged in ', postInsert: () => { showError("You must be logged in !"); navigateTo("/") } }; + } + await updateFriendsList(); + let friendList = getFriendList(); + friendList.sort(); + + let friendsElem = friendList.map(g => { + let e = document.createElement('div'); + e.className = 'grid grid-cols-[1fr_auto_1fr] items-center bg-zinc-800 rounded-lg px-4 py-3'; + + e.innerHTML = ` +
${g.name}
+ TTT Games + Pong Games + `; + return e; + }).filter(v => !isNullish(v)); + + return { + html: page, postInsert: async (app) => { + if (!app) return; + const friendsBox = app.querySelector("#friendList"); + if (!friendsBox) return; + friendsElem.forEach(c => friendsBox.appendChild(c)); + } + }; +} + +addRoute('/friends', friends); diff --git a/frontend/src/pages/index.ts b/frontend/src/pages/index.ts index cd6c41d..aef2743 100644 --- a/frontend/src/pages/index.ts +++ b/frontend/src/pages/index.ts @@ -1,6 +1,5 @@ import { setTitle, handleRoute } from '@app/routing'; import './root/root.ts' -import '../chat/chat.ts' import './pong/pong.ts' import './login/login.ts' import './signin/signin.ts' @@ -10,6 +9,7 @@ import './logout/logout.ts' import './pongHistory/pongHistory.ts' import './tttHistory/tttHistory.ts' import './tourHistory/tourHistory.ts' +import './friendList/friendList.ts' // ---- Initial load ---- setTitle(""); diff --git a/frontend/src/utils.ts b/frontend/src/utils.ts index 80a3888..6229799 100644 --- a/frontend/src/utils.ts +++ b/frontend/src/utils.ts @@ -1,9 +1,11 @@ +import client from "./api"; + export function escapeHTML(str: string): string { const p = document.createElement("p"); p.appendChild(document.createTextNode(str)); return p.innerHTML; } -export function isNullish(v: T | undefined | null): v is (null | undefined) { +export function isNullish(v: T | undefined | null): v is null | undefined { return v === null || v === undefined; } @@ -11,3 +13,22 @@ export function isNullish(v: T | undefined | null): v is (null | undefined) { export function ensureWindowState() { window.__state = window.__state ?? {}; } + +export async function updateFriendsList() { + window.__state = window.__state ?? {}; + window.__state.friendList ??= []; + + try { + let req = await client.listFriend(); + if (req.kind === "success") { + window.__state.friendList = req.payload.friends; + } + } catch (e: unknown) { } +} + +export function getFriendList() { + ensureWindowState(); + window.__state.friendList ??= []; + + return window.__state.friendList; +}