diff --git a/src/api/user/default.js b/src/api/user/default.js index 1c63b59..2c7902b 100644 --- a/src/api/user/default.js +++ b/src/api/user/default.js @@ -59,9 +59,9 @@ const deleteFriends = database.prepare('DELETE FROM friends WHERE username = ?;' export default async function(fastify, options) { fastify.register(cors, { - origin: process.ENV.CORS_ORIGIN || 'http://localhost:5173', + origin: process.env.CORS_ORIGIN || 'http://localhost:5173', credentials: true, - methods: [ "GET", "POST", "DELETE", "OPTIONS" ] + methods: [ "GET", "POST", "PATCH", "DELETE", "OPTIONS" ] }); fastify.register(fastifyJWT, { diff --git a/src/front/index.html b/src/front/index.html index ee61724..c348c97 100644 --- a/src/front/index.html +++ b/src/front/index.html @@ -11,10 +11,8 @@ - - - - + +
diff --git a/src/front/static/css/style.css b/src/front/static/css/style.css index 56bb708..65bae17 100644 --- a/src/front/static/css/style.css +++ b/src/front/static/css/style.css @@ -1,3 +1,4 @@ +@import url('https://fonts.googleapis.com/css2?family=Lexend:wght@100..900&display=swap'); @import "tailwindcss"; @layer utilities { .no-scrollbar::-webkit-scrollbar { diff --git a/src/front/static/ts/views/LoginPage.ts b/src/front/static/ts/views/LoginPage.ts index 97e6ed0..2daecda 100644 --- a/src/front/static/ts/views/LoginPage.ts +++ b/src/front/static/ts/views/LoginPage.ts @@ -41,7 +41,7 @@ export default class extends Aview { login with John Google - + login with Rusty diff --git a/src/front/static/ts/views/Profile.ts b/src/front/static/ts/views/Profile.ts index fda349f..4374d70 100644 --- a/src/front/static/ts/views/Profile.ts +++ b/src/front/static/ts/views/Profile.ts @@ -44,6 +44,7 @@ export default class extends Aview { if (!await isLogged()) navigationManager("/"); + let pc: number = 0; dragElement(document.getElementById("window")); let uuid: String; uuid = document.cookie.match(new RegExp('(^| )' + "uuid" + '=([^;]+)'))[2]; @@ -63,6 +64,7 @@ export default class extends Aview { credentials: "include", }); let matchCount = await matchCount_req.json(); + pc += matchCount.n_matches; let matches_req = await fetch(`http://localhost:3002/users/${uuid}/matchHistory?game=pong&iStart=0&iEnd=${matchCount.n_matches}`, { method: "GET", @@ -88,7 +90,9 @@ export default class extends Aview { const header = popup.appendChild(document.createElement("div")); header.classList.add("bg-linear-to-r", "from-orange-200", "to-orange-300", "flex", "flex-row", "min-w-35", "justify-between", "px-2"); header.id = `${id}-header`; - header.appendChild(document.createElement("span")).innerText = "score.ts"; + const title = header.appendChild(document.createElement("span")); + title.classList.add("font-[Kubasta]"); + title.innerText = "score-pong.ts"; const btn = header.appendChild(document.createElement("button")); btn.innerText = " × "; btn.onclick = () => { document.getElementById(`${id}`).remove(); }; @@ -116,6 +120,7 @@ export default class extends Aview { credentials: "include", }); matchCount = await matchCount_req.json(); + pc += matchCount.n_matches; matches_req = await fetch(`http://localhost:3002/users/${uuid}/matchHistory?game=tetris&iStart=0&iEnd=${matchCount.n_matches}`, { method: "GET", @@ -132,7 +137,11 @@ export default class extends Aview { for (let match of matches.matchHistory) { const newEntry = document.createElement("li"); newEntry.classList.add("m-1", "default-button", "bg-neutral-200", "dark:bg-neutral-800", "text-neutral-900", "dark:text-white"); - newEntry.innerHTML = match.score.p1Score > match.score.p2Score ? `${match.score.p1} - winner` : `${match.score.p2} - winner`; + newEntry.innerHTML = match.score.p2 != undefined ? + (match.score.p1Score > match.score.p2Score ? `${match.score.p1} - winner` : `${match.score.p2} - winner`) + : + (`solo game - ${match.score.p1Score}`) + ; main.insertBefore(newEntry, main.firstChild); const popup: HTMLDivElement = document.createElement("div"); @@ -142,7 +151,9 @@ export default class extends Aview { const header = popup.appendChild(document.createElement("div")); header.classList.add("bg-linear-to-r", "from-orange-200", "to-orange-300", "flex", "flex-row", "min-w-35", "justify-between", "px-2"); header.id = `${id}-header`; - header.appendChild(document.createElement("span")).innerText = "score.ts"; + const title = header.appendChild(document.createElement("span")); + title.classList.add("font-[Kubasta]"); + title.innerText = "score-tetris.ts"; const btn = header.appendChild(document.createElement("button")); btn.innerText = " × "; btn.onclick = () => { document.getElementById(`${id}`).remove(); }; @@ -153,7 +164,11 @@ export default class extends Aview { popup_content.appendChild(document.createElement("span")).innerText = `${date.toDateString()} ${date.getHours()}:${date.getMinutes().toString().padStart(2, '0')}`; const score = popup_content.appendChild(document.createElement("span")); score.classList.add(); - score.innerText = `${match.score.p1} : ${match.score.p1Score} - ${match.score.p2Score} : ${match.score.p2}`; + score.innerText = match.score.p2 != undefined ? + (`${match.score.p1} : ${match.score.p1Score} - ${match.score.p2Score} : ${match.score.p2}`) + : + (`${match.score.p1} : ${match.score.p1Score}`) + ; const tx = popup_content.appendChild(document.createElement("a")); tx.href = `https://testnet.snowscan.xyz/tx/${match.tx}`; tx.innerText = "transaction proof"; @@ -169,8 +184,12 @@ export default class extends Aview { if (!profile) return; const picture = profile.appendChild(document.createElement("img")); - picture.src = "https://api.kanel.ovh/pp"; - picture.classList.add("text-neutral-900", "dark:text-white", "center", "h-18", "w-18", "mx-3"); + const a = await fetch(`http://localhost:3002/users/${uuid}/avatar`, { + method: "GET", + credentials: "include", + }); + picture.src = a.status === 200 ? `http://localhost:3002/users/${uuid}/avatar` : "https://api.kanel.ovh/pp"; + picture.classList.add("text-neutral-900", "dark:text-white", "center", "h-18", "w-18", "mx-3", "reverse-border"); const nametag = profile.appendChild(document.createElement("div")); nametag.innerHTML = ` @@ -181,9 +200,9 @@ export default class extends Aview { const winrate = profile.appendChild(document.createElement("div")); winrate.innerHTML = ` -
wins: ${userdata.wins}
-
losses: ${userdata.losses}
-
winrate: ${ (userdata.wins != 0 && userdata.losses != 0) ? Math.round(userdata.wins / (userdata.wins + userdata.losses) * 100) + " %" : "-" }
+
total playcount: ${pc}
+
pong winrate: ${ (userdata.pong.wins == 0 && userdata.pong.losses == 0) ? "-" : Math.round(userdata.pong.wins / (userdata.pong.wins + userdata.pong.losses) * 100) + " %" }
+
tetris winrate: ${ (userdata.tetris.wins == 0 && userdata.tetris.losses == 0) ? "-" : Math.round(userdata.tetris.wins / (userdata.tetris.wins + userdata.tetris.losses) * 100) + " %" }
`; winrate.classList.add("text-neutral-900", "dark:text-white", "grow", "content-center"); } diff --git a/src/front/static/ts/views/ProfileMenu.ts b/src/front/static/ts/views/ProfileMenu.ts index 68317c0..84422ca 100644 --- a/src/front/static/ts/views/ProfileMenu.ts +++ b/src/front/static/ts/views/ProfileMenu.ts @@ -40,13 +40,13 @@ export default class extends Aview { async function getMainHTML() { if (!(await isLogged())) { - document.getElementById("menu-bottom-div").classList.add("hidden"); + document.getElementById("menu-bottom-div")?.classList.add("hidden"); return ` login register `; } - document.getElementById("menu-bottom-div").classList.remove("hidden"); + document.getElementById("menu-bottom-div")?.classList.remove("hidden"); uuid = document.cookie.match(new RegExp('(^| )' + "uuid" + '=([^;]+)'))[2]; const userdata_req = await fetch(`http://localhost:3002/users/${uuid}`, { @@ -65,14 +65,13 @@ export default class extends Aview {
profile settings - friends `; } document.getElementById("profile-items").innerHTML = await getMainHTML(); - document.getElementById("menu-logout").addEventListener("click", async () => { + document.getElementById("menu-logout")?.addEventListener("click", async () => { let req = await fetch("http://localhost:3001/logout", { method: "GET", credentials: "include", diff --git a/src/front/static/ts/views/RegisterPage.ts b/src/front/static/ts/views/RegisterPage.ts index 30618f1..7485e93 100644 --- a/src/front/static/ts/views/RegisterPage.ts +++ b/src/front/static/ts/views/RegisterPage.ts @@ -5,8 +5,7 @@ import { isLogged, navigationManager } from "../main.ts" export default class extends Aview { - constructor() - { + constructor() { super(); this.setTitle("register"); setOnekoState("default"); @@ -53,7 +52,7 @@ export default class extends Aview { } async run() { - dragElement(document.getElementById("window")); + dragElement(document.getElementById("window")); const login = async () => { const username = (document.getElementById("username") as HTMLInputElement).value; const password = (document.getElementById("password") as HTMLInputElement).value; @@ -67,34 +66,34 @@ export default class extends Aview { }); const data = await data_req.json(); - if (data_req.status === 200) - { + if (data_req.status === 200) { let uuid_req = await fetch("http://localhost:3001/me", { method: "GET", credentials: "include", }); let uuid = await uuid_req.json(); - document.cookie = `uuid=${uuid.user};max-ages=${60*60*24*7}`; + document.cookie = `uuid=${uuid.user};max-ages=${60 * 60 * 24 * 7}`; console.log(document.cookie); isLogged(); navigationManager("/"); } - else if (data_req.status === 400) - { - document.getElementById("login-error-message").innerHTML = "error: " + data.error; - document.getElementById("login-error-message").classList.remove("hidden"); + else if (data_req.status === 400) { + if (document.getElementById("login-error-message")) { + document.getElementById("login-error-message").innerHTML = "error: " + data.error; + document.getElementById("login-error-message")?.classList.remove("hidden"); + } } - else - { + else { throw new Error("invalid response"); } } - catch (error) - { + catch (error) { console.error(error); - document.getElementById("login-error-message").innerHTML = "error: server error, try again later..."; - document.getElementById("login-error-message").classList.remove("hidden"); + if (document.getElementById("login-error-message")) { + document.getElementById("login-error-message").innerHTML = "error: server error, try again later..."; + document.getElementById("login-error-message")?.classList.remove("hidden"); + } } }; diff --git a/src/front/static/ts/views/Settings.ts b/src/front/static/ts/views/Settings.ts index 021b215..87e3066 100644 --- a/src/front/static/ts/views/Settings.ts +++ b/src/front/static/ts/views/Settings.ts @@ -24,9 +24,18 @@ export default class extends Aview { ×
-
- - +
+
+ + +
+
+ + +

@@ -35,13 +44,13 @@ export default class extends Aview { `; } - async run() { - if (!await isLogged()) - navigationManager("/"); + async run() { + if (!await isLogged()) + navigationManager("/"); - dragElement(document.getElementById("window")); + dragElement(document.getElementById("window")); - const isTOTPEnabled = async () => { + const isTOTPEnabled = async () => { const totpVerify_req = await fetch('http://localhost:3001/2fa', { method: "GET", credentials: "include" @@ -55,51 +64,84 @@ export default class extends Aview { } return false; }; + + let uuid: String; + uuid = document.cookie.match(new RegExp('(^| )' + "uuid" + '=([^;]+)'))[2]; + const userdata_req = await fetch(`http://localhost:3002/users/${uuid}`, { + method: "GET", + credentials: "include", + }); + if (userdata_req.status == 404) { + console.error("invalid user"); + return; + } + let userdata = await userdata_req.json(); - let uuid: String; - uuid = document.cookie.match(new RegExp('(^| )' + "uuid" + '=([^;]+)'))[2]; - const userdata_req = await fetch(`http://localhost:3002/users/${uuid}`, { - method: "GET", - credentials: "include", - }); - if (userdata_req.status == 404) { - console.error("invalid user"); - return; - } - let userdata = await userdata_req.json(); + (document.getElementById("displayName-input") as HTMLInputElement).placeholder = userdata.displayName; + (document.getElementById("displayName-input") as HTMLInputElement).value = userdata.displayName; - (document.getElementById("displayName-input") as HTMLInputElement).placeholder = userdata.displayName; - (document.getElementById("displayName-input") as HTMLInputElement).value = userdata.displayName; + document.getElementById("displayName-button")?.addEventListener("click", async () => { + const changeDisplayName_req = await fetch(`http://localhost:3002/users/${uuid}/displayName`, { + method: "PATCH", + headers: { "Content-Type": "application/json", }, + credentials: "include", + body: JSON.stringify({ displayName: (document.getElementById("displayName-input") as HTMLInputElement).value }) + }); + if (changeDisplayName_req.status == 200) { + // idk display success + } + else { + // display error ig, uuuh it's in await changeDisplayName.json().error + } + }); - document.getElementById("displayName-button")?.addEventListener("click", async () => { - const changeDisplayName_req = await fetch(`http://localhost:3002/users/${uuid}/displayName`, { - method: "PATCH", - headers: { "Content-Type": "application/json", }, - credentials: "include", - body: JSON.stringify({ displayName: (document.getElementById("displayName-input") as HTMLInputElement).value }) - }); - if (changeDisplayName_req.status == 200) { - // idk display success - } - else { - // display error ig, uuuh it's in await changeDisplayName.json().error - } - }); + document.getElementById("deleteAccount-button")?.addEventListener("click", async () => { + const delete_req = await fetch(`http://localhost:3001/`, { + method: "DELETE", + credentials: "include", + }); - document.getElementById("deleteAccount-button")?.addEventListener("click", async () => { - const delete_req = await fetch(`http://localhost:3001/`, { - method: "DELETE", - credentials: "include", - }); + if (delete_req.status == 200) + navigationManager("/"); + else + console.error("xd"); // xd????????????? + }); - if (delete_req.status == 200) - navigationManager("/"); - else - console.error("xd"); // xd????????????? - }); + const upload = document.getElementById("upload-file") as HTMLInputElement; + upload.addEventListener("change", () => { + const fileList: FileList | null = upload.files; + if (!fileList) + return console.error("empty"); + if (!fileList[0].type.startsWith("image/")) { + console.error("invalid file"); + return; + } + document.getElementById("upload-preview")?.classList.remove("hidden"); + const img = document.getElementById("upload-preview-img") as HTMLImageElement; + img.classList.remove("hidden"); - const totpButton = document.getElementById("2fa-button") as HTMLButtonElement; + const reader = new FileReader(); + reader.onload = (e) => { + if (!e.target) + return; + img.src = e.target.result as string; + }; + + reader.readAsDataURL(fileList[0]); + }); + + (document.getElementById("upload-submit") as HTMLButtonElement).onclick = async () => { + const up_req = await fetch(`http://localhost:3002/users/${uuid}/avatar`, { + method: "POST", + headers: { "Content-Type": upload.files[0].type } , + credentials: "include", + body: upload.files[0], //upload uuuh whatever i have to upload + }); + console.log(up_req.status); + }; + + const totpButton = document.getElementById("2fa-button") as HTMLButtonElement; if ((await isTOTPEnabled()) === true) { totpButton.innerHTML = "disable 2fa"; @@ -133,5 +175,5 @@ export default class extends Aview { } }); } - } + } } diff --git a/src/front/static/ts/views/Tetris.ts b/src/front/static/ts/views/Tetris.ts index 61c309a..f5a20d3 100644 --- a/src/front/static/ts/views/Tetris.ts +++ b/src/front/static/ts/views/Tetris.ts @@ -509,13 +509,10 @@ export default class extends Aview { this.movePiece(this.direction, 0); this.move = false; } - - /*if (this.keys["ArrowDown"]) - this.softDrop();*/ } - registerListeners() { - window.addEventListener("keydown", (e) => { + removeListeners() { + window.removeEventListener("keydown", (e) => { this.keys[e.key] = true; if (this.isGameOver) return; @@ -553,13 +550,55 @@ export default class extends Aview { } }); + document.removeEventListener("keyup", (e) => { + this.keys[e.key] = false; + }); + } + + registerListeners() { + window.addEventListener("keydown", (e) => { + this.keys[e.key] = true; + + if (this.isGameOver) return; + + if (e.key === "p" || e.key === "P" || e.key === "Escape") + this.isPaused = !this.isPaused; + + if (this.isPaused) return; + + if (e.key === "ArrowLeft") + { + this.inputTimestamp = Date.now(); + this.direction = -1;//this.movePiece(-1, 0); + this.move = true; + } + else if (e.key === "ArrowRight") + { + this.inputTimestamp = Date.now(); + this.direction = 1;//this.movePiece(1, 0); + this.move = true; + } + else if (e.key === "ArrowDown") this.softDrop(); + else if (e.code === "Space") { + //e.preventDefault(); + this.hardDrop(); + } else if (e.key === "Shift" || e.key === "c" || e.key === "C") { + this.hold(); + } else if (e.key === "x" || e.key === "X" || e.key === "ArrowUp") { + //e.preventDefault(); + this.rotatePiece("cw"); + } else if (e.key === "z" || e.key === "Z" || e.key === "Control") { + this.rotatePiece("ccw"); + } + }); + document.addEventListener("keyup", (e) => { this.keys[e.key] = false; }); } async loop(timestamp: number) { - if (!view.running) return; + if (!view.running) return this.removeListeners(); if (!this.lastDrop) this.lastDrop = timestamp; if (!this.isPaused) { @@ -596,9 +635,7 @@ export default class extends Aview { credentials: "include", body: JSON.stringify({ "game": "tetris", - "opponent": "xd", "myScore": this.score, - "opponentScore": 0, "date": Date.now(), }), }); diff --git a/src/front/static/ts/views/TetrisVersus.ts b/src/front/static/ts/views/TetrisVersus.ts index 330b8e6..f9debac 100644 --- a/src/front/static/ts/views/TetrisVersus.ts +++ b/src/front/static/ts/views/TetrisVersus.ts @@ -606,16 +606,16 @@ export default class extends Aview { } else if (this.id === 0 ? e.code === "KeyS" : e.code === "Numpad5") this.softDrop(); else if (this.id === 0 ? e.code === "Space" : e.code === "Numpad0") { - e.preventDefault(); + //e.preventDefault(); this.hardDrop(); } else if (this.id === 0 ? e.code === "ShiftLeft" : e.code === "NumpadEnter") { - e.preventDefault(); + //e.preventDefault(); this.hold(); } else if (this.id === 0 ? (e.code === "KeyE" || e.code === "KeyW") : (e.code === "Numpad9" || e.code === "Numpad8")) { - e.preventDefault(); + //e.preventDefault(); this.rotatePiece("cw"); } else if (this.id === 0 ? (e.code === "KeyQ" || e.code === "ControlLeft") : e.code === "Numpad7") { - e.preventDefault(); + //e.preventDefault(); this.rotatePiece("ccw"); } }); diff --git a/src/front/static/ts/views/TournamentMenu.ts b/src/front/static/ts/views/TournamentMenu.ts index bc2bbd6..00538a8 100644 --- a/src/front/static/ts/views/TournamentMenu.ts +++ b/src/front/static/ts/views/TournamentMenu.ts @@ -1,4 +1,5 @@ import Aview from "./Aview.ts" +import { dragElement } from "./drag.ts"; import { setOnekoState, setBallPos } from "../oneko.ts" export default class extends Aview { @@ -12,18 +13,37 @@ export default class extends Aview { async getHTML() { return ` -
-

how many players ?

-
- - -
+
+
+ pong_game.ts +
+ + + × +
+ +
+

how many players ?

+
+ + +
+
+
`; } async run() { + dragElement(document.getElementById("window")); const generateBracket = async (playerCount: number) => { document.getElementById("bracket").innerHTML = ""; @@ -32,12 +52,10 @@ export default class extends Aview { const byes = totalSlots - playerCount; let odd = 0; - if (playerCount % 2) + if (playerCount > 9 || (playerCount != 3 && playerCount % 2 != 0)) { - //console.error("odd numbers are temporarily invalids"); - //return ; - ++odd; - --playerCount; + console.error("odd numbers are temporarily invalids"); + return ; } let notPowPlayersCount = 0; @@ -46,9 +64,8 @@ export default class extends Aview { notPowPlayersCount = playerCount - (2 ** Math.floor(Math.log2(playerCount))); - let initialPlayers = Array.from({ length: 2 ** Math.floor(Math.log2(playerCount))}, (_, i) => `Player ${i + 1}`); + let initialPlayers = Array.from({ length: 2 ** Math.floor(Math.log2(playerCount))}, (_, i) => `player ${i + 1}`); playerCount = 2 ** Math.floor(Math.log2(playerCount)); - //let initialPlayers = Array.from({ length: playerCount }, (_, i) => `Player ${i + 1}`); const bracketWrapper = document.createElement("div"); bracketWrapper.className = "flex space-x-8 overflow-x-auto"; @@ -63,8 +80,7 @@ export default class extends Aview { input.id = `playerName${i}`; input.value = ""; input.placeholder = name; - input.className = - "w-32 h-10 p-2 text-sm border rounded bg-white shadow disabled:bg-gray-200"; + input.className = "w-32 h-10 p-2 text-sm bg-white disabled:bg-gray-200 input-border"; playerInputColumn.appendChild(input); }); @@ -87,10 +103,10 @@ export default class extends Aview { const input = document.createElement("input"); input.type = "text"; input.id = `playerName${playerCount}`; - input.value = ""; - input.placeholder = `Player ${++playerCount}`; + input.value = `player ${++playerCount}`; + input.placeholder = `player ${++playerCount}`; input.className = - "w-32 h-10 p-2 text-sm border rounded bg-white shadow disabled:bg-gray-200"; + "w-32 h-10 p-2 text-sm bg-white disabled:bg-gray-200 input-border"; roundColumn.appendChild(input); odd--; nextRound.push(""); @@ -103,9 +119,9 @@ export default class extends Aview { input.type = "text"; input.id = `playerName${playerCount}`; input.value = ""; - input.placeholder = `Player ${++playerCount}`; + input.placeholder = `player ${++playerCount}`; input.className = - "w-32 h-10 p-2 text-sm border rounded bg-white shadow disabled:bg-gray-200"; + "w-32 h-10 p-2 text-sm bg-white disabled:bg-gray-200 input-border"; roundColumn.appendChild(input); --notPowPlayersCount; nextRound.push(""); @@ -118,7 +134,7 @@ export default class extends Aview { const matchDiv = document.createElement("div"); matchDiv.className = - "w-32 h-10 flex items-center justify-center bg-white border rounded shadow text-center text-sm"; + "w-32 h-10 flex items-center justify-center bg-white text-center text-sm input-border"; matchDiv.textContent = ""; nextRound.push(""); @@ -130,11 +146,22 @@ export default class extends Aview { currentRound = nextRound; } + document.getElementById("bracket").appendChild(document.createElement("hr")).classList.add("my-4", "mb-8", "w-64", "reverse-border"); document.getElementById("bracket").appendChild(bracketWrapper); + const btn = document.getElementById("bracket").appendChild(document.createElement("button")); + btn.classList.add("default-button", "w-full"); + btn.id = "tournament-play"; + btn.onclick = () => { + console.log("ok"); + }; + btn.innerText = "start tournament !!"; + }; document.getElementById("bracket-generate")?.addEventListener("click", () => { const input: HTMLInputElement = document.getElementById("playerNumber") as HTMLInputElement; + if (input.value == "") + return; generateBracket(+input.value); }); diff --git a/tailwind.config.js b/tailwind.config.js index 9bd8368..53376f7 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,7 +1,13 @@ export default { content: ['./src/front/**/*.{html,js,ts,css}'], theme: { - extend: {}, + extend: { + fontFamily: { + jersey: ['"Jersey 10"', 'sans-serif'], + }, + }, + }, + }, }, plugins: [], }