update: friends list + button
This commit is contained in:
parent
590604b385
commit
c5eea6e29e
8 changed files with 156 additions and 76 deletions
|
|
@ -37,6 +37,7 @@
|
||||||
<a href="/ttt" class="hover:bg-gray-700 rounded-md px-3 py-2">⭕ Tic-Tac-Toe</a>
|
<a href="/ttt" class="hover:bg-gray-700 rounded-md px-3 py-2">⭕ Tic-Tac-Toe</a>
|
||||||
<a href="/pong" class="hover:bg-gray-700 rounded-md px-3 py-2">▮•▮ Ping Pong</a>
|
<a href="/pong" class="hover:bg-gray-700 rounded-md px-3 py-2">▮•▮ Ping Pong</a>
|
||||||
<a href="/tour" class="hover:bg-gray-700 rounded-md px-3 py-2">🏆 Tournaments</a>
|
<a href="/tour" class="hover:bg-gray-700 rounded-md px-3 py-2">🏆 Tournaments</a>
|
||||||
|
<a href="/friends" class="hover:bg-gray-700 rounded-md px-3 py-2">😎 Friends</a>
|
||||||
<a href="/logout" class="hover:bg-gray-700 rounded-md px-3 py-2">🚪 Logout</a>
|
<a href="/logout" class="hover:bg-gray-700 rounded-md px-3 py-2">🚪 Logout</a>
|
||||||
</nav>
|
</nav>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { showError } from "@app/toast";
|
import { showError } from "@app/toast";
|
||||||
import client from "@app/api";
|
import client from "@app/api";
|
||||||
import cookie from "js-cookie";
|
import cookie from "js-cookie";
|
||||||
import { ensureWindowState, isNullish } from "@app/utils";
|
import { ensureWindowState, isNullish, updateFriendsList } from "@app/utils";
|
||||||
import { handleRoute, navigateTo } from "./routing";
|
import { handleRoute, navigateTo } from "./routing";
|
||||||
|
|
||||||
cookie.remove("pkce");
|
cookie.remove("pkce");
|
||||||
|
|
@ -105,10 +105,11 @@ if (!window.__state._headerProfile) {
|
||||||
|
|
||||||
window.__state._reloadOnAuthChange ??= false;
|
window.__state._reloadOnAuthChange ??= false;
|
||||||
if (!window.__state._reloadOnAuthChange) {
|
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 the last forced auth change is less than 1000 sec old -> we do nothing
|
||||||
if (Date.now() - (window.__state.lastAuthChange ?? Date.now()) < 1000)
|
if (Date.now() - (window.__state.lastAuthChange ?? Date.now()) < 1000)
|
||||||
return;
|
return;
|
||||||
|
await updateFriendsList();
|
||||||
handleRoute();
|
handleRoute();
|
||||||
});
|
});
|
||||||
window.__state._reloadOnAuthChange = true;
|
window.__state._reloadOnAuthChange = true;
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ let keysPressed: Record<string, boolean> = {};
|
||||||
declare module "ft_state" {
|
declare module "ft_state" {
|
||||||
interface State {
|
interface State {
|
||||||
chatSock?: Socket;
|
chatSock?: Socket;
|
||||||
|
friendList: { id: string; name: string }[];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -82,18 +83,20 @@ const sendButton = document.getElementById("b-send") as HTMLButtonElement;
|
||||||
const sendtextbox = document.getElementById(
|
const sendtextbox = document.getElementById(
|
||||||
"t-chat-window",
|
"t-chat-window",
|
||||||
) as HTMLButtonElement;
|
) as HTMLButtonElement;
|
||||||
const systemWindow = document.getElementById("chat-system-box") as HTMLDivElement;
|
const systemWindow = document.getElementById(
|
||||||
|
"chat-system-box",
|
||||||
|
) as HTMLDivElement;
|
||||||
|
|
||||||
function chatKeyToggle() {
|
function chatKeyToggle() {
|
||||||
let anti_flicker_control = false;
|
let anti_flicker_control = false;
|
||||||
const chat_hide_key = 'escape';
|
const chat_hide_key = "escape";
|
||||||
const chat_display_key = 'f2';
|
const chat_display_key = "f2";
|
||||||
const home_display_key = 'f8';
|
const home_display_key = "f8";
|
||||||
document.addEventListener("keydown", (event) => {
|
document.addEventListener("keydown", (event) => {
|
||||||
if (event.repeat && keysPressed[chat_hide_key] === true) {
|
if (event.repeat && keysPressed[chat_hide_key] === true) {
|
||||||
anti_flicker_control = true;
|
anti_flicker_control = true;
|
||||||
return ;
|
return;
|
||||||
};
|
}
|
||||||
keysPressed[event.key.toLowerCase()] = true;
|
keysPressed[event.key.toLowerCase()] = true;
|
||||||
});
|
});
|
||||||
document.addEventListener("keyup", (event) => {
|
document.addEventListener("keyup", (event) => {
|
||||||
|
|
@ -102,34 +105,33 @@ function chatKeyToggle() {
|
||||||
anti_flicker_control = false;
|
anti_flicker_control = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
setInterval( () => {
|
setInterval(() => {
|
||||||
if(keysPressed[chat_hide_key] === true) {
|
if (keysPressed[chat_hide_key] === true) {
|
||||||
overlay.classList.remove("opacity-60");
|
overlay.classList.remove("opacity-60");
|
||||||
chatBox.classList.add("hidden");
|
chatBox.classList.add("hidden");
|
||||||
chatMessageIn?.classList.add("hidden");
|
chatMessageIn?.classList.add("hidden");
|
||||||
chatMessageIn!.textContent = '';
|
chatMessageIn!.textContent = "";
|
||||||
profilList?.classList.add("hidden");
|
profilList?.classList.add("hidden");
|
||||||
windowStateHidden();
|
windowStateHidden();
|
||||||
}
|
}
|
||||||
if (keysPressed[chat_display_key] === true) {
|
if (keysPressed[chat_display_key] === true) {
|
||||||
anti_flicker_control = false;
|
anti_flicker_control = false;
|
||||||
chatBox.classList.remove("hidden");
|
chatBox.classList.remove("hidden");
|
||||||
overlay.classList.add("opacity-60");
|
overlay.classList.add("opacity-60");
|
||||||
chatMessageIn?.classList.add("hidden");
|
chatMessageIn?.classList.add("hidden");
|
||||||
chatMessageIn!.textContent = '';
|
chatMessageIn!.textContent = "";
|
||||||
let socket = window.__state.chatSock;
|
let socket = window.__state.chatSock;
|
||||||
if (!socket) return;
|
if (!socket) return;
|
||||||
connected(socket);
|
connected(socket);
|
||||||
sendtextbox.focus();
|
sendtextbox.focus();
|
||||||
windowStateVisable();
|
windowStateVisable();
|
||||||
|
|
||||||
}
|
}
|
||||||
if (keysPressed[home_display_key] === true) {
|
if (keysPressed[home_display_key] === true) {
|
||||||
navigateTo('/app/');
|
navigateTo("/app/");
|
||||||
quitChat();
|
quitChat();
|
||||||
}
|
}
|
||||||
}, 1000/10);
|
}, 1000 / 10);
|
||||||
};
|
}
|
||||||
|
|
||||||
function initChatSocket() {
|
function initChatSocket() {
|
||||||
let socket = getSocket();
|
let socket = getSocket();
|
||||||
|
|
@ -153,7 +155,8 @@ function initChatSocket() {
|
||||||
!sendButton ||
|
!sendButton ||
|
||||||
!sendtextbox ||
|
!sendtextbox ||
|
||||||
!systemWindow
|
!systemWindow
|
||||||
) return showError("fatal error");
|
)
|
||||||
|
return showError("fatal error");
|
||||||
|
|
||||||
// Listen for the 'connect' event
|
// Listen for the 'connect' event
|
||||||
socket.on("connect", async () => {
|
socket.on("connect", async () => {
|
||||||
|
|
@ -205,7 +208,7 @@ function initChatSocket() {
|
||||||
|
|
||||||
if (chatWindow && data.message.destination === "") {
|
if (chatWindow && data.message.destination === "") {
|
||||||
chatMessageIn?.classList.remove("hidden");
|
chatMessageIn?.classList.remove("hidden");
|
||||||
chatMessageIn!.textContent = '🔵';
|
chatMessageIn!.textContent = "🔵";
|
||||||
const messageElement = document.createElement("div");
|
const messageElement = document.createElement("div");
|
||||||
messageElement.textContent = `${data.message.user}: ${data.message.text}`;
|
messageElement.textContent = `${data.message.user}: ${data.message.text}`;
|
||||||
chatWindow.appendChild(messageElement);
|
chatWindow.appendChild(messageElement);
|
||||||
|
|
@ -214,7 +217,7 @@ function initChatSocket() {
|
||||||
|
|
||||||
if (chatWindow && data.message.destination === "privateMsg") {
|
if (chatWindow && data.message.destination === "privateMsg") {
|
||||||
chatMessageIn?.classList.remove("hidden");
|
chatMessageIn?.classList.remove("hidden");
|
||||||
chatMessageIn!.textContent = '🔴';
|
chatMessageIn!.textContent = "🔴";
|
||||||
const messageElement = document.createElement("div-private");
|
const messageElement = document.createElement("div-private");
|
||||||
messageElement.textContent = `🔒${data.message.user}: ${data.message.text}`;
|
messageElement.textContent = `🔒${data.message.user}: ${data.message.text}`;
|
||||||
chatWindow.appendChild(messageElement);
|
chatWindow.appendChild(messageElement);
|
||||||
|
|
@ -223,7 +226,7 @@ function initChatSocket() {
|
||||||
|
|
||||||
if (chatWindow && data.message.destination === "inviteMsg") {
|
if (chatWindow && data.message.destination === "inviteMsg") {
|
||||||
chatMessageIn?.classList.remove("hidden");
|
chatMessageIn?.classList.remove("hidden");
|
||||||
chatMessageIn!.textContent = '🟢';
|
chatMessageIn!.textContent = "🟢";
|
||||||
const messageElement = document.createElement("div-private");
|
const messageElement = document.createElement("div-private");
|
||||||
const chatWindow = document.getElementById(
|
const chatWindow = document.getElementById(
|
||||||
"t-chatbox",
|
"t-chatbox",
|
||||||
|
|
@ -243,7 +246,6 @@ function initChatSocket() {
|
||||||
}
|
}
|
||||||
systemWindow.appendChild(messageElement);
|
systemWindow.appendChild(messageElement);
|
||||||
systemWindow.lastElementChild?.scrollIntoView({ block: "end" });
|
systemWindow.lastElementChild?.scrollIntoView({ block: "end" });
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -278,9 +280,9 @@ function initChatSocket() {
|
||||||
if (blockUserBtn) {
|
if (blockUserBtn) {
|
||||||
let message = "";
|
let message = "";
|
||||||
if (data.userState === "block") {
|
if (data.userState === "block") {
|
||||||
(message = "un-block");
|
message = "un-block";
|
||||||
} else {
|
} else {
|
||||||
(message = "block");
|
message = "block";
|
||||||
}
|
}
|
||||||
blockUserBtn.textContent = message;
|
blockUserBtn.textContent = message;
|
||||||
}
|
}
|
||||||
|
|
@ -294,10 +296,8 @@ function initChatSocket() {
|
||||||
const htmlBaliseRegex = /<a\b[^>]*>[\s\S]*?<\/a>/;
|
const htmlBaliseRegex = /<a\b[^>]*>[\s\S]*?<\/a>/;
|
||||||
const htmlBaliseMatch = message.match(htmlBaliseRegex);
|
const htmlBaliseMatch = message.match(htmlBaliseRegex);
|
||||||
|
|
||||||
if (htmlBaliseMatch)
|
if (htmlBaliseMatch) addInviteMessage(message);
|
||||||
addInviteMessage(message);
|
else addMessage(message);
|
||||||
else
|
|
||||||
addMessage(message);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//receives broadcast of the next GAME
|
//receives broadcast of the next GAME
|
||||||
|
|
@ -505,7 +505,6 @@ clearText?.addEventListener("click", () => {
|
||||||
|
|
||||||
bquit?.addEventListener("click", () => {
|
bquit?.addEventListener("click", () => {
|
||||||
quitChat();
|
quitChat();
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
myGames?.addEventListener("click", () => {
|
myGames?.addEventListener("click", () => {
|
||||||
|
|
@ -527,7 +526,7 @@ sendtextbox.addEventListener("keydown", (event) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
chatButton!.addEventListener("click",() => {
|
chatButton!.addEventListener("click", () => {
|
||||||
if (chatBox.classList.contains("hidden")) {
|
if (chatBox.classList.contains("hidden")) {
|
||||||
chatBox.classList.toggle("hidden");
|
chatBox.classList.toggle("hidden");
|
||||||
overlay.classList.add("opacity-60");
|
overlay.classList.add("opacity-60");
|
||||||
|
|
@ -536,14 +535,14 @@ chatButton!.addEventListener("click",() => {
|
||||||
if (!socket) return;
|
if (!socket) return;
|
||||||
connected(socket);
|
connected(socket);
|
||||||
chatMessageIn?.classList.add("hidden");
|
chatMessageIn?.classList.add("hidden");
|
||||||
chatMessageIn!.textContent = '';
|
chatMessageIn!.textContent = "";
|
||||||
sendtextbox.focus();
|
sendtextbox.focus();
|
||||||
} else {
|
} else {
|
||||||
chatBox.classList.toggle("hidden");
|
chatBox.classList.toggle("hidden");
|
||||||
overlay.classList.remove("opacity-60");
|
overlay.classList.remove("opacity-60");
|
||||||
windowStateHidden();
|
windowStateHidden();
|
||||||
chatMessageIn?.classList.add("hidden");
|
chatMessageIn?.classList.add("hidden");
|
||||||
chatMessageIn!.textContent = '';
|
chatMessageIn!.textContent = "";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
|
import client from "@app/api";
|
||||||
import type { ClientProfil } from "../types_front";
|
import type { ClientProfil } from "../types_front";
|
||||||
import { Socket } from "socket.io-client";
|
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
|
* 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) {
|
export function actionBtnFriend(profile: ClientProfil, senderSocket: Socket) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const friend = document.querySelector("#btn-friend");
|
const friend = document.querySelector("#btn-friend");
|
||||||
friend?.addEventListener("click", () => {
|
friend?.addEventListener("click", async () => {
|
||||||
|
let friendList = getFriendList();
|
||||||
if (friend.textContent = "friend") {
|
if (!friendList.some(v => v.id === profile.userID!)) {
|
||||||
friend.textContent = "not-friend"
|
let req = await client.addFriend({ user: profile.userID! });
|
||||||
console.log('friend');
|
if (req.kind === 'success')
|
||||||
}
|
showSuccess('Successfully added a new Friend')
|
||||||
else {
|
else
|
||||||
friend.textContent = "not-friend"
|
showError('Failed to add a new Friend');
|
||||||
console.log('Not a friend');
|
}
|
||||||
|
else {
|
||||||
}
|
let req = await client.removeFriend({ user: profile.userID! });
|
||||||
|
if (req.kind === 'success')
|
||||||
});
|
showSuccess('Successfully removed a Friend')
|
||||||
}, 0)
|
else
|
||||||
|
showError('Failed to remove a Friend');
|
||||||
|
}
|
||||||
|
await updateFriendsList();
|
||||||
|
});
|
||||||
|
}, 0)
|
||||||
};
|
};
|
||||||
9
frontend/src/pages/friendList/friendList.html
Normal file
9
frontend/src/pages/friendList/friendList.html
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
<div class="fixed inset-0 flex items-center justify-center bg-[#43536b]">
|
||||||
|
<div
|
||||||
|
class="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-gray-200 w-212.5 p-6 rounded-xl shadow-2xl text-center z-50">
|
||||||
|
<h2 class="text-2xl font-bold text-center mb-4 text-gray-700">
|
||||||
|
FriendList
|
||||||
|
</h2>
|
||||||
|
<div id="friendList" class="max-w-3xl mx-auto space-y-2 max-h-[50dvh] overflow-scroll"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
41
frontend/src/pages/friendList/friendList.ts
Normal file
41
frontend/src/pages/friendList/friendList.ts
Normal file
|
|
@ -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<RouteHandlerReturn> {
|
||||||
|
setTitle("Tic Tac Toe Games");
|
||||||
|
let user = await updateUser();
|
||||||
|
if (isNullish(user)) {
|
||||||
|
return { html: '<span> You aren\'t logged in </span>', 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 = `
|
||||||
|
<div class="text-center font-semibold text-white">${g.name}</div>
|
||||||
|
<a href="/ttt/games/${g.id}" class="text-center">TTT Games</a>
|
||||||
|
<a href="/pong/games/${g.id}" class="text-center">Pong Games</a>
|
||||||
|
`;
|
||||||
|
return e;
|
||||||
|
}).filter(v => !isNullish(v));
|
||||||
|
|
||||||
|
return {
|
||||||
|
html: page, postInsert: async (app) => {
|
||||||
|
if (!app) return;
|
||||||
|
const friendsBox = app.querySelector<HTMLDivElement>("#friendList");
|
||||||
|
if (!friendsBox) return;
|
||||||
|
friendsElem.forEach(c => friendsBox.appendChild(c));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
addRoute('/friends', friends);
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { setTitle, handleRoute } from '@app/routing';
|
import { setTitle, handleRoute } from '@app/routing';
|
||||||
import './root/root.ts'
|
import './root/root.ts'
|
||||||
import '../chat/chat.ts'
|
|
||||||
import './pong/pong.ts'
|
import './pong/pong.ts'
|
||||||
import './login/login.ts'
|
import './login/login.ts'
|
||||||
import './signin/signin.ts'
|
import './signin/signin.ts'
|
||||||
|
|
@ -10,6 +9,7 @@ import './logout/logout.ts'
|
||||||
import './pongHistory/pongHistory.ts'
|
import './pongHistory/pongHistory.ts'
|
||||||
import './tttHistory/tttHistory.ts'
|
import './tttHistory/tttHistory.ts'
|
||||||
import './tourHistory/tourHistory.ts'
|
import './tourHistory/tourHistory.ts'
|
||||||
|
import './friendList/friendList.ts'
|
||||||
|
|
||||||
// ---- Initial load ----
|
// ---- Initial load ----
|
||||||
setTitle("");
|
setTitle("");
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
|
import client from "./api";
|
||||||
|
|
||||||
export function escapeHTML(str: string): string {
|
export function escapeHTML(str: string): string {
|
||||||
const p = document.createElement("p");
|
const p = document.createElement("p");
|
||||||
p.appendChild(document.createTextNode(str));
|
p.appendChild(document.createTextNode(str));
|
||||||
return p.innerHTML;
|
return p.innerHTML;
|
||||||
}
|
}
|
||||||
export function isNullish<T>(v: T | undefined | null): v is (null | undefined) {
|
export function isNullish<T>(v: T | undefined | null): v is null | undefined {
|
||||||
return v === null || v === undefined;
|
return v === null || v === undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -11,3 +13,22 @@ export function isNullish<T>(v: T | undefined | null): v is (null | undefined) {
|
||||||
export function ensureWindowState() {
|
export function ensureWindowState() {
|
||||||
window.__state = window.__state ?? {};
|
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;
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue