mirror of
https://github.com/KeyZox71/knl_meowscendence.git
synced 2025-12-31 21:56:41 +01:00
「🏗️」 wip(front): profile page
This commit is contained in:
@ -42,8 +42,8 @@
|
||||
<div class="flex px-4 items-center content-center space-x-2">
|
||||
<button id="profile-button" class="taskbar-button flex flex-row justify-center items-center"><img class="object-scale-down mr-2 h-5 w-5" src="https://api.kanel.ovh/id?id=65" /> start</button>
|
||||
<div class="text-neutral-700 dark:text-neutral-400">|</div>
|
||||
<a class="taskbar-button" href="https://rusty.42angouleme.fr/">rusty</a>
|
||||
<a class="taskbar-button" href="/tetris" data-link>tetris</a>
|
||||
<a target="_blank" class="taskbar-button" href="https://rusty.42angouleme.fr/">rusty</a>
|
||||
<a target="_blank" class="taskbar-button" href="https://dn720004.ca.archive.org/0/items/2009-tetris-variant-concepts_202201/2009%20Tetris%20Design%20Guideline.pdf">tetris-guideline.pdf</a>
|
||||
</div>
|
||||
<div class="reverse-border m-1.5 h-8/10 content-center">
|
||||
<span id="taskbar-clock" class="text- neutral-900 dark:text-white px-4">12:37</span>
|
||||
|
||||
@ -1,4 +1,13 @@
|
||||
@import "tailwindcss";
|
||||
@layer utilities {
|
||||
.no-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.no-scrollbar {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Kubasta;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { oneko } from "./oneko.ts";
|
||||
import Profile from "./views/Profile.ts";
|
||||
let profile_view = new Profile;
|
||||
import ProfileMenu from "./views/ProfileMenu.ts";
|
||||
let profile_view = new ProfileMenu;
|
||||
|
||||
export async function isLogged(): Promise<boolean> {
|
||||
let uuid_req = await fetch("http://localhost:3001/me", {
|
||||
@ -40,6 +40,9 @@ const routes = [
|
||||
|
||||
{ path: "/login", view: () => import("./views/LoginPage.ts") },
|
||||
{ path: "/register", view: () => import("./views/RegisterPage.ts") },
|
||||
|
||||
{ path: "/profile", view: () => import("./views/Profile.ts") },
|
||||
{ path: "/settings", view: () => import("./views/Settings.ts") },
|
||||
];
|
||||
|
||||
const router = async () => {
|
||||
@ -97,8 +100,6 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
router();
|
||||
});
|
||||
|
||||
oneko();
|
||||
|
||||
function updateClock()
|
||||
{
|
||||
const days = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
|
||||
@ -116,3 +117,5 @@ function updateClock()
|
||||
|
||||
setInterval(updateClock, 5000);
|
||||
updateClock();
|
||||
|
||||
oneko();
|
||||
|
||||
@ -18,20 +18,23 @@ export function setOnekoState(state: string) {
|
||||
default:
|
||||
oneko_state = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
export function setOnekoOffset() {
|
||||
if (oneko_state == 1)
|
||||
if (oneko_state != 0)
|
||||
{
|
||||
offsetX = document.getElementById("window").offsetLeft + 44;
|
||||
offsetY = document.getElementById("window").offsetTop + 44 + 24;
|
||||
console.log(offsetX, offsetY);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
export function setBallPos(x: number, y: number) {
|
||||
export function setBallPos(x: number, y: number)
|
||||
{
|
||||
mousePosX = x + offsetX;
|
||||
mousePosY = y + offsetY;
|
||||
return;
|
||||
}
|
||||
|
||||
export function oneko() {
|
||||
@ -39,7 +42,7 @@ export function oneko() {
|
||||
window.matchMedia(`(prefers-reduced-motion: reduce)`) === true ||
|
||||
window.matchMedia(`(prefers-reduced-motion: reduce)`).matches === true;
|
||||
|
||||
if (isReducedMotion) return;
|
||||
if (isReducedMotion) return ;
|
||||
|
||||
const nekoEl = document.createElement("div");
|
||||
|
||||
@ -141,15 +144,15 @@ export function oneko() {
|
||||
{
|
||||
mousePosX = event.clientX;
|
||||
mousePosY = event.clientY;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
window.requestAnimationFrame(onAnimationFrame);
|
||||
}
|
||||
|
||||
let lastFrameTimestamp;
|
||||
let lastFrameTimestamp: number;
|
||||
|
||||
function onAnimationFrame(timestamp) {
|
||||
function onAnimationFrame(timestamp: number) {
|
||||
// Stops execution if the neko element is removed from DOM
|
||||
if (!nekoEl.isConnected) {
|
||||
return;
|
||||
|
||||
@ -4,7 +4,7 @@ import { dragElement } from "./drag.js"
|
||||
import { setOnekoState, setBallPos, setOnekoOffset } from "../oneko.ts"
|
||||
|
||||
export default class extends Aview {
|
||||
|
||||
|
||||
running: boolean;
|
||||
|
||||
constructor()
|
||||
@ -27,7 +27,7 @@ export default class extends Aview {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div id="main-div" class="bg-neutral-200 dark:bg-neutral-800 text-center p-10 space-y-4 reverse-border">
|
||||
<div id="player-inputs" class="flex flex-col space-y-4">
|
||||
<div class="flex flex-row">
|
||||
@ -46,6 +46,7 @@ export default class extends Aview {
|
||||
|
||||
async run() {
|
||||
dragElement(document.getElementById("window"));
|
||||
let uuid: string;
|
||||
|
||||
let start: number = 0;
|
||||
let elapsed: number;
|
||||
@ -103,7 +104,7 @@ export default class extends Aview {
|
||||
ballSpeedY = ballSpeed * Math.sin(theta);
|
||||
}
|
||||
|
||||
function moveBall() {
|
||||
async function moveBall() {
|
||||
let length = Math.sqrt(ballSpeedX * ballSpeedX + ballSpeedY * ballSpeedY);
|
||||
let scale = ballSpeed / length;
|
||||
ballX += (ballSpeedX * scale) * elapsed;
|
||||
@ -142,6 +143,17 @@ export default class extends Aview {
|
||||
|
||||
if (p1_score === 3 || p2_score === 3)
|
||||
{
|
||||
console.log(isLogged());
|
||||
if (await isLogged())
|
||||
{
|
||||
let uuid = document.cookie.match(new RegExp('(^| )' + "uuid" + '=([^;]+)'))[2];
|
||||
fetch(`http://localhost:3002/users/${uuid}/matchHistory`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json", },
|
||||
credentials: "include",
|
||||
body: JSON.stringify({ "opponent": p2_name, "myScore": p1_score, "opponentScore": p2_score })
|
||||
});
|
||||
}
|
||||
// ------------------------------------------------------------------------------------------------------------------------------------------
|
||||
//
|
||||
// insert the fetch to the ScoreStore api here
|
||||
@ -225,13 +237,13 @@ export default class extends Aview {
|
||||
}
|
||||
}
|
||||
|
||||
const gameLoop = (timestamp: number) => {
|
||||
const gameLoop = async (timestamp: number) => {
|
||||
elapsed = (timestamp - start) / 1000;
|
||||
start = timestamp;
|
||||
if (game_playing)
|
||||
{
|
||||
movePaddles();
|
||||
moveBall();
|
||||
await moveBall();
|
||||
}
|
||||
draw();
|
||||
if (!game_playing)
|
||||
@ -257,13 +269,26 @@ export default class extends Aview {
|
||||
|
||||
p2_input.value = "Player 2";
|
||||
if (await isLogged())
|
||||
p1_input.value = document.cookie.match(new RegExp('(^| )' + "uuid" + '=([^;]+)'))[2];
|
||||
{
|
||||
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();
|
||||
p1_input.value = userdata.displayName;
|
||||
}
|
||||
else
|
||||
p1_input.value = "Player 1";
|
||||
|
||||
document.getElementById("game-start")?.addEventListener("click", () => {
|
||||
p1_name = p1_input.value;
|
||||
p2_name = p2_input.value;
|
||||
p1_name = p1_input.value.length > 16 ? p1_input.value.substring(0, 16) + "." : p1_input.value;
|
||||
p2_name = p2_input.value.length > 16 ? p2_input.value.substring(0, 16) + "." : p2_input.value;
|
||||
document.getElementById("player-inputs").remove();
|
||||
|
||||
canvas = document.createElement("canvas");
|
||||
@ -272,7 +297,7 @@ export default class extends Aview {
|
||||
|
||||
document.getElementById("main-div").prepend(canvas);
|
||||
|
||||
ctx = canvas.getContext("2d");
|
||||
ctx = canvas.getContext("2d", {alpha: false});
|
||||
ctx.canvas.width = 600;
|
||||
ctx.canvas.height = 600;
|
||||
|
||||
|
||||
@ -24,14 +24,30 @@ export default class extends Aview {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="dialog" class="bg-neutral-200 dark:bg-neutral-800 text-center pb-10 pt-5 px-10 space-y-4 reverse-border">
|
||||
<h1 class="text-gray-900 dark:text-white text-lg pt-0 pb-4">welcome back ! please login.</h1>
|
||||
<input type="text" id="username" placeholder="username" class="bg-white text-neutral-900 px-4 py-2 input-border" required></input>
|
||||
<input type="password" id="password" placeholder="password" class="bg-white text-neutral-900 px-4 py-2 input-border" required></input>
|
||||
<p id="login-error-message" class="hidden text-red-700 dark:text-red-500"></p>
|
||||
</br>
|
||||
<button id="login-button" type="submit" class="default-button w-full">login</button>
|
||||
</form>
|
||||
<div class="bg-neutral-200 dark:bg-neutral-800 text-center pb-10 pt-5 px-10 reverse-border flex flex-col items-center">
|
||||
<form method="dialog" class="space-y-4">
|
||||
<h1 class="text-gray-900 dark:text-white text-lg pt-0 pb-4">welcome back ! please login.</h1>
|
||||
<input type="text" id="username" placeholder="username" class="bg-white text-neutral-900 px-4 py-2 input-border" required></input>
|
||||
<input type="password" id="password" placeholder="password" class="bg-white text-neutral-900 px-4 py-2 input-border" required></input>
|
||||
<p id="login-error-message" class="hidden text-red-700 dark:text-red-500"></p>
|
||||
</br>
|
||||
<button id="login-button" type="submit" class="default-button w-full">login</button>
|
||||
</form>
|
||||
|
||||
<hr class="my-4 w-64 reverse-border">
|
||||
|
||||
<div class="flex flex-col space-y-4 w-full">
|
||||
<a target="_blank" href="http://localhost:3001/login/google" class="default-button inline-flex items-center justify-center w-full">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/c/c1/Google_%22G%22_logo.svg" height=20 width=20 class="mr-2 justify-self-start" />
|
||||
login with John Google
|
||||
</a>
|
||||
<a target="_blank" href="http://localhost:3001/login/google" class="default-button inline-flex items-center justify-center w-full">
|
||||
<img src="https://rusty.42angouleme.fr/assets/favicon-bb06adc80c8495db.ico" height=20 width=20 class="mr-2 justify-self-start" />
|
||||
login with Rusty
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@ -49,7 +65,6 @@ export default class extends Aview {
|
||||
credentials: "include",
|
||||
body: JSON.stringify({ user: username, password: password }),
|
||||
});
|
||||
const data = await data_req.json();
|
||||
|
||||
if (data_req.status === 200)
|
||||
{
|
||||
@ -58,6 +73,7 @@ export default class extends Aview {
|
||||
}
|
||||
else if (data_req.status === 400)
|
||||
{
|
||||
const data = await data_req.json();
|
||||
document.getElementById("login-error-message").innerHTML = "error: " + data.error;
|
||||
document.getElementById("login-error-message").classList.remove("hidden");
|
||||
}
|
||||
|
||||
@ -1,29 +1,38 @@
|
||||
import Aview from "./Aview.ts"
|
||||
import { dragElement } from "./drag.ts";
|
||||
import { setOnekoState } from "../oneko.ts"
|
||||
import { isLogged, navigationManager } from "../main.ts"
|
||||
|
||||
|
||||
export default class extends Aview {
|
||||
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
this.setTitle("profile");
|
||||
setOnekoState("default");
|
||||
}
|
||||
|
||||
async getHTML() {
|
||||
return `
|
||||
<div id="main-window" class="default-border shadow-2x1 bg-neutral-200 dark:bg-neutral-800">
|
||||
<div class="flex flex-row items-stretch">
|
||||
<div class="inline-block bg-linear-to-b from-orange-200 to-orange-300 min-h-84 w-6 relative">
|
||||
<!--div class="absolute bottom-1 left-full whitespace-nowrap origin-bottom-left -rotate-90 font-bold">knl_meowscendence</div-->
|
||||
<div class="absolute bottom-1 left-full whitespace-nowrap origin-bottom-left -rotate-90 font-bold">girls kissing :3</div>
|
||||
<div id="window" class="absolute default-border">
|
||||
<div id="window-header" class="bg-linear-to-r from-orange-200 to-orange-300 flex flex-row min-w-75 justify-between px-2">
|
||||
<span class="font-[Kubasta]">profile.ts</span>
|
||||
<div>
|
||||
<button> - </button>
|
||||
<button> □ </button>
|
||||
<a href="/" data-link> × </a>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col items-center">
|
||||
<div id="profile-items" class="flex flex-col items-center">
|
||||
</div>
|
||||
<div class="bg-neutral-200 dark:bg-neutral-800 text-center pb-10 pt-5 px-10 space-y-4 reverse-border">
|
||||
<div class="flex flex-col space-y-4 w-full">
|
||||
<div id="profile-profile" class="default-border h-24 flex flex-row place-content-stretch content-center items-center space-x-6 pr-4">
|
||||
</div>
|
||||
<div id="menu-bottom-div" class="hidden mt-auto flex flex-col items-center">
|
||||
<hr class="my-2 w-32 reverse-border">
|
||||
<button id="menu-logout" class="menu-default-button">logout</button>
|
||||
<div class="flex flex-row space-x-4 w-full min-w-145">
|
||||
<ul id="profile-scorelist" class="reverse-border bg-neutral-300 dark:bg-neutral-900 h-48 w-full overflow-scroll no-scrollbar">
|
||||
</ul>
|
||||
<div id="graph-ig-idk-im-scared" class="reverse-border bg-neutral-300 dark:bg-neutral-900 h-48 w-full">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -31,43 +40,15 @@ export default class extends Aview {
|
||||
`;
|
||||
}
|
||||
|
||||
open: boolean = false;
|
||||
async run() {
|
||||
if (!await isLogged())
|
||||
navigationManager("/");
|
||||
|
||||
async run() {
|
||||
let uuid: String;
|
||||
if (this.open)
|
||||
{
|
||||
this.open = false;
|
||||
document.getElementById("taskbar-menu").innerHTML = "";
|
||||
return ;
|
||||
}
|
||||
this.open = true;
|
||||
document.getElementById("taskbar-menu").innerHTML = await this.getHTML();
|
||||
dragElement(document.getElementById("window"));
|
||||
let uuid: String;
|
||||
uuid = document.cookie.match(new RegExp('(^| )' + "uuid" + '=([^;]+)'))[2];
|
||||
|
||||
async function getMainHTML() {
|
||||
if (!(await isLogged()))
|
||||
{
|
||||
document.getElementById("menu-bottom-div").classList.add("hidden");
|
||||
return `
|
||||
<a class="menu-default-button inline-flex items-center justify-center" href="/login" data-link>login</a>
|
||||
<a class="menu-default-button inline-flex items-center justify-center" href="/register" data-link>register</a>
|
||||
`;
|
||||
}
|
||||
document.getElementById("menu-bottom-div").classList.remove("hidden");
|
||||
|
||||
uuid = document.cookie.match(new RegExp('(^| )' + "uuid" + '=([^;]+)'))[2];
|
||||
return `
|
||||
<span class="menu-default-label inline-flex items-center justify-center">hi, ${uuid} !</span>
|
||||
<hr class="my-2 w-32 reverse-border">
|
||||
<button class="menu-default-button">profile</button>
|
||||
<button class="menu-default-button">settings</button>
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
document.getElementById("profile-items").innerHTML = await getMainHTML();
|
||||
|
||||
/*const userdata_req = await fetch(`http://localhost:3002/users/${uuid}`, {
|
||||
const userdata_req = await fetch(`http://localhost:3002/users/${uuid}`, {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
});
|
||||
@ -78,28 +59,56 @@ export default class extends Aview {
|
||||
}
|
||||
let userdata = await userdata_req.json();
|
||||
|
||||
console.log(userdata_req);*/
|
||||
const matchCount_req = await fetch(`http://localhost:3002/users/${uuid}/matchHistory/count`, {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
});
|
||||
const matchCount = await matchCount_req.json();
|
||||
|
||||
/*const main = document.getElementById("profile-profile");
|
||||
const nametag = main.appendChild(document.createElement("span"));
|
||||
const matches_req = await fetch(`http://localhost:3002/users/${uuid}/matchHistory?iStart=0&iEnd=${matchCount.n_matches}`, {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
});
|
||||
const matches = await matches_req.json();
|
||||
|
||||
nametag.innerHTML = `Hiiiiii ${userdata.displayName} ! :D`;
|
||||
const main = document.getElementById("profile-scorelist");
|
||||
if (!main)
|
||||
return console.error("what");
|
||||
|
||||
console.log(matches);
|
||||
if (matches.matchHistory)
|
||||
{
|
||||
for (let match of matches.matchHistory)
|
||||
{
|
||||
const newEntry = document.createElement("li");
|
||||
newEntry.classList.add("m-2", "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`;
|
||||
main.insertBefore(newEntry, main.firstChild);
|
||||
console.log(match.tx);
|
||||
}
|
||||
}
|
||||
|
||||
const profile = document.getElementById("profile-profile");
|
||||
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 nametag = profile.appendChild(document.createElement("div"));
|
||||
nametag.innerHTML = `
|
||||
<div class="text-lg">Hi ${userdata.displayName} ! :D</div>
|
||||
<div class="italic">${uuid}<div>
|
||||
`;
|
||||
nametag.classList.add("text-neutral-900", "dark:text-white");
|
||||
|
||||
const winrate = main.appendChild(document.createElement("div"));
|
||||
const winrate = profile.appendChild(document.createElement("div"));
|
||||
|
||||
winrate.innerHTML = `wins: ${userdata.wins} | losses: ${userdata.losses} | winrate: ${userdata.wins / (userdata.wins + userdata.losses)}`;
|
||||
winrate.classList.add("text-neutral-900", "dark:text-white");*/
|
||||
//console.log(document.getElementById("menu-logout"));
|
||||
document.getElementById("menu-logout").addEventListener("click", async () => {
|
||||
let req = await fetch("http://localhost:3001/logout", {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
});
|
||||
if (req.status === 200)
|
||||
this.run();
|
||||
else
|
||||
console.error("logout failed");
|
||||
});
|
||||
}
|
||||
winrate.innerHTML = `
|
||||
<div> wins: ${userdata.wins} </div>
|
||||
<div> losses: ${userdata.losses} </div>
|
||||
<div> winrate: ${Math.round(userdata.wins / (userdata.wins + userdata.losses) * 100)} % </div>
|
||||
`;
|
||||
winrate.classList.add("text-neutral-900", "dark:text-white", "grow", "content-center");
|
||||
}
|
||||
}
|
||||
|
||||
86
src/front/static/ts/views/ProfileMenu.ts
Normal file
86
src/front/static/ts/views/ProfileMenu.ts
Normal file
@ -0,0 +1,86 @@
|
||||
import Aview from "./Aview.ts"
|
||||
import { isLogged, navigationManager } from "../main.ts"
|
||||
|
||||
export default class extends Aview {
|
||||
|
||||
async getHTML() {
|
||||
return `
|
||||
<div id="main-window" class="default-border shadow-2x1 bg-neutral-200 dark:bg-neutral-800">
|
||||
<div class="flex flex-row items-stretch">
|
||||
<div class="inline-block bg-linear-to-b from-orange-200 to-orange-300 min-h-84 w-6 relative">
|
||||
<!--div class="absolute bottom-1 left-full whitespace-nowrap origin-bottom-left -rotate-90 font-bold">knl_meowscendence</div-->
|
||||
<div class="absolute bottom-1 left-full whitespace-nowrap origin-bottom-left -rotate-90 font-bold">girls kissing :3</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col items-center">
|
||||
<div id="profile-items" class="flex flex-col items-center">
|
||||
</div>
|
||||
<div id="menu-bottom-div" class="hidden mt-auto flex flex-col items-center">
|
||||
<hr class="my-2 w-32 reverse-border">
|
||||
<button id="menu-logout" class="menu-default-button">logout</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
open: boolean = false;
|
||||
|
||||
async run() {
|
||||
let uuid: String;
|
||||
if (this.open)
|
||||
{
|
||||
this.open = false;
|
||||
document.getElementById("taskbar-menu").innerHTML = "";
|
||||
return ;
|
||||
}
|
||||
this.open = true;
|
||||
document.getElementById("taskbar-menu").innerHTML = await this.getHTML();
|
||||
|
||||
async function getMainHTML() {
|
||||
if (!(await isLogged()))
|
||||
{
|
||||
document.getElementById("menu-bottom-div").classList.add("hidden");
|
||||
return `
|
||||
<a class="menu-default-button inline-flex items-center justify-center" href="/login" data-link>login</a>
|
||||
<a class="menu-default-button inline-flex items-center justify-center" href="/register" data-link>register</a>
|
||||
`;
|
||||
}
|
||||
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}`, {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
});
|
||||
if (userdata_req.status == 404)
|
||||
{
|
||||
console.error("invalid user");
|
||||
return ;
|
||||
}
|
||||
let userdata = await userdata_req.json();
|
||||
|
||||
return `
|
||||
<span class="menu-default-label inline-flex items-center justify-center">hi, ${ userdata.displayName.length > 8 ? userdata.displayName.substring(0, 8) + "." : userdata.displayName } !</span>
|
||||
<hr class="my-2 w-32 reverse-border">
|
||||
<a class="menu-default-button inline-flex items-center justify-center" href="/profile" data-link>profile</a>
|
||||
<a class="menu-default-button inline-flex items-center justify-center" href="/settings" data-link>settings</a>
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
document.getElementById("profile-items").innerHTML = await getMainHTML();
|
||||
|
||||
document.getElementById("menu-logout").addEventListener("click", async () => {
|
||||
let req = await fetch("http://localhost:3001/logout", {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
});
|
||||
if (req.status === 200)
|
||||
this.run();
|
||||
else
|
||||
console.error("logout failed");
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
import Aview from "./Aview.ts"
|
||||
import { dragElement } from "./drag.ts";
|
||||
import { setOnekoState } from "../oneko.ts"
|
||||
import { isLogged, navigationManager } from "../main.ts"
|
||||
import { dragElement } from "./drag.ts";
|
||||
|
||||
export default class extends Aview {
|
||||
|
||||
@ -24,14 +24,30 @@ export default class extends Aview {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="dialog" class="bg-neutral-200 dark:bg-neutral-800 text-center pb-10 pt-5 px-10 space-y-4 reverse-border">
|
||||
<p class="text-gray-900 dark:text-white text-lg pt-0 pb-4">welcome ! please register.</p>
|
||||
<input type="text" id="username" placeholder="username" class="bg-white text-neutral-900 px-4 py-2 input-border" required></input>
|
||||
<input type="password" id="password" placeholder="password" class="bg-white text-neutral-900 px-4 py-2 input-border" required></input>
|
||||
<p id="login-error-message" class="hidden text-red-700 dark:text-red-500"></p>
|
||||
</br>
|
||||
<button id="register-button" type="submit" class="default-button w-full">register</button>
|
||||
</form>
|
||||
<div class="bg-neutral-200 dark:bg-neutral-800 text-center pb-10 pt-5 px-10 reverse-border flex flex-col items-center">
|
||||
<form method="dialog" class="space-y-4">
|
||||
<p class="text-gray-900 dark:text-white text-lg pt-0 pb-4">welcome ! please register.</p>
|
||||
<input type="text" id="username" placeholder="username" class="bg-white text-neutral-900 px-4 py-2 input-border" required></input>
|
||||
<input type="password" id="password" placeholder="password" class="bg-white text-neutral-900 px-4 py-2 input-border" required></input>
|
||||
<p id="login-error-message" class="hidden text-red-700 dark:text-red-500"></p>
|
||||
</br>
|
||||
<button id="register-button" type="submit" class="default-button w-full">register</button>
|
||||
</form>
|
||||
|
||||
<hr class="my-4 w-64 reverse-border">
|
||||
|
||||
<div class="flex flex-col space-y-4 w-full">
|
||||
<a target="_blank" href="http://localhost:3001/register/google" class="default-button inline-flex items-center justify-center w-full">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/c/c1/Google_%22G%22_logo.svg" height=20 width=20 class="mr-2 justify-self-start" />
|
||||
register with John Google
|
||||
</a>
|
||||
<a target="_blank" href="https://rusty.42angouleme.fr/issues/all" class="default-button inline-flex items-center justify-center w-full">
|
||||
<img src="https://rusty.42angouleme.fr/assets/favicon-bb06adc80c8495db.ico" height=20 width=20 class="mr-2 justify-self-start" />
|
||||
register with Rusty
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
84
src/front/static/ts/views/Settings.ts
Normal file
84
src/front/static/ts/views/Settings.ts
Normal file
@ -0,0 +1,84 @@
|
||||
import Aview from "./Aview.ts"
|
||||
import { dragElement } from "./drag.ts";
|
||||
import { setOnekoState } from "../oneko.ts"
|
||||
import { isLogged, navigationManager } from "../main.ts"
|
||||
|
||||
|
||||
export default class extends Aview {
|
||||
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
this.setTitle("profile");
|
||||
setOnekoState("default");
|
||||
}
|
||||
|
||||
async getHTML() {
|
||||
return `
|
||||
<div id="window" class="absolute default-border">
|
||||
<div id="window-header" class="bg-linear-to-r from-orange-200 to-orange-300 flex flex-row min-w-75 justify-between px-2">
|
||||
<span class="font-[Kubasta]">settings.ts</span>
|
||||
<div>
|
||||
<button> - </button>
|
||||
<button> □ </button>
|
||||
<a href="/" data-link> × </a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-neutral-200 dark:bg-neutral-800 text-center pb-10 pt-5 px-10 space-y-4 reverse-border">
|
||||
<input type="text" id="displayName-input" class="bg-white text-neutral-900 px-4 py-2 input-border" required></input>
|
||||
<button id="displayName-button" type="submit" class="default-button w-full">change display name</button>
|
||||
<button id="deleteAccount-button" type="submit" class="default-button w-full">delete your account</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
async run() {
|
||||
if (!await isLogged())
|
||||
navigationManager("/");
|
||||
|
||||
dragElement(document.getElementById("window"));
|
||||
|
||||
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-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:3002/users/${uuid}`, {
|
||||
method: "DELETE",
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (delete_req.status == 200)
|
||||
navigationManager("/");
|
||||
else
|
||||
console.error("xd"); // should never happen, wtf
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
import Aview from "./Aview.ts";
|
||||
import { dragElement } from "./drag.js";
|
||||
import { setOnekoState, setBallPos, setOnekoOffset } from "../oneko.ts"
|
||||
|
||||
export default class extends Aview {
|
||||
running: boolean;
|
||||
@ -7,6 +8,7 @@ export default class extends Aview {
|
||||
constructor() {
|
||||
super();
|
||||
this.setTitle("tetris (local match)");
|
||||
setOnekoState("tetris");
|
||||
this.running = true;
|
||||
}
|
||||
|
||||
@ -274,33 +276,42 @@ export default class extends Aview {
|
||||
canvas: HTMLCanvasElement | null;
|
||||
holdCanvas: HTMLCanvasElement | null;
|
||||
queueCanvas: HTMLCanvasElement | null;
|
||||
ctx: CanvasRenderingContext2D;
|
||||
holdCtx: CanvasRenderingContext2D;
|
||||
queueCtx: CanvasRenderingContext2D;
|
||||
ctx: CanvasRenderingContext2D | null;
|
||||
holdCtx: CanvasRenderingContext2D | null;
|
||||
queueCtx: CanvasRenderingContext2D | null;
|
||||
piece: Piece | null = null;
|
||||
holdPiece: Piece | null = null;
|
||||
canHold: boolean = true;
|
||||
nextQueue: string[] = [];
|
||||
score = 0;
|
||||
level = 1;
|
||||
lines = 0;
|
||||
dropInterval = 1000;
|
||||
lastDrop = 0;
|
||||
isGameOver = false;
|
||||
isPaused = false;
|
||||
score: number = 0;
|
||||
level: number = 1;
|
||||
lines: number = 0;
|
||||
dropInterval: number = 1000;
|
||||
lastDrop: number = 0;
|
||||
isLocking: boolean = false;
|
||||
lockRotationCount: number = 0;
|
||||
lockLastRotationCount: number = 0;
|
||||
isGameOver: boolean = false;
|
||||
isPaused: boolean = false;
|
||||
|
||||
constructor(canvasId: string) {
|
||||
const el = document.getElementById(
|
||||
canvasId,
|
||||
) as HTMLCanvasElement | null;
|
||||
this.canvas = el;
|
||||
if (!this.canvas)
|
||||
throw console.error("no canvas :c");
|
||||
this.canvas.width = COLS * BLOCK;
|
||||
this.canvas.height = ROWS * BLOCK;
|
||||
const ctx = this.canvas.getContext("2d");
|
||||
this.ctx = ctx;
|
||||
if (!this.ctx)
|
||||
throw console.error("no ctx D:");
|
||||
|
||||
this.holdCanvas = document.getElementById("hold");
|
||||
this.queueCanvas = document.getElementById("queue");
|
||||
this.holdCanvas = document.getElementById("hold") as HTMLCanvasElement;
|
||||
this.queueCanvas = document.getElementById("queue") as HTMLCanvasElement;
|
||||
if (!this.holdCanvas || !this.queueCanvas)
|
||||
throw console.error("no canvas :c");
|
||||
this.holdCtx = this.holdCanvas.getContext("2d");
|
||||
this.queueCtx = this.queueCanvas.getContext("2d");
|
||||
|
||||
@ -337,6 +348,7 @@ export default class extends Aview {
|
||||
|
||||
[this.piece, this.holdPiece] = [this.holdPiece, this.piece];
|
||||
if (!this.piece) this.spawnPiece();
|
||||
if (!this.piece) return;
|
||||
|
||||
this.piece.x = Math.floor((COLS - this.piece.shape[0].length) / 2);
|
||||
this.piece.y = -2;
|
||||
@ -352,7 +364,6 @@ export default class extends Aview {
|
||||
if (this.nextQueue.length < 7) this.fillBag();
|
||||
const type = this.nextQueue.shift()!;
|
||||
this.piece = new Piece(type);
|
||||
// If spawn collides immediately -> game over
|
||||
if (this.collides(this.piece)) {
|
||||
this.isGameOver = true;
|
||||
}
|
||||
@ -374,12 +385,11 @@ export default class extends Aview {
|
||||
let y: number = 0;
|
||||
while (true) {
|
||||
for (const cell of piece.getCells()) {
|
||||
console.log(cell.y + y);
|
||||
if (
|
||||
cell.y + y >= ROWS ||
|
||||
(cell.y + y >= 0 && this.board[cell.y + y][cell.x])
|
||||
)
|
||||
return y - 1;
|
||||
return y - 1;
|
||||
}
|
||||
|
||||
y++;
|
||||
@ -388,11 +398,12 @@ export default class extends Aview {
|
||||
|
||||
lockPiece() {
|
||||
if (!this.piece) return;
|
||||
this.isLocking = false;
|
||||
let isValid: boolean = false;
|
||||
for (const cell of this.piece.getCells()) {
|
||||
if (cell.y >= 0 && cell.y < ROWS && cell.x >= 0 && cell.x < COLS)
|
||||
this.board[cell.y][cell.x] = cell.val;
|
||||
if (cell.y < 20) isValid = true;
|
||||
if (cell.y > 0) isValid = true;
|
||||
}
|
||||
if (!isValid) this.isGameOver = true;
|
||||
|
||||
@ -427,6 +438,8 @@ export default class extends Aview {
|
||||
|
||||
rotatePiece(dir: "cw" | "ccw") {
|
||||
if (!this.piece) return;
|
||||
if (this.isLocking && this.lockRotationCount < 15)
|
||||
this.lockRotationCount++;
|
||||
// Try rotation with wall kicks
|
||||
const originalIndex = this.piece.rotationIndex;
|
||||
if (dir === "cw") this.piece.rotateCW();
|
||||
@ -446,6 +459,7 @@ export default class extends Aview {
|
||||
if (!this.piece) return;
|
||||
this.piece.x += dx;
|
||||
this.piece.y += dy;
|
||||
|
||||
if (this.collides(this.piece)) {
|
||||
this.piece.x -= dx;
|
||||
this.piece.y -= dy;
|
||||
@ -470,6 +484,26 @@ export default class extends Aview {
|
||||
}
|
||||
|
||||
keys: Record<string, boolean> = {};
|
||||
direction: number = 0;
|
||||
inputDelay = 200;
|
||||
inputTimestamp = Date.now();
|
||||
move: boolean = false;
|
||||
|
||||
inputManager() {
|
||||
if (this.move || Date.now() > this.inputTimestamp + this.inputDelay)
|
||||
{
|
||||
if (this.keys["ArrowLeft"] && !this.keys["ArrowRight"])
|
||||
this.movePiece(-1, 0);
|
||||
else if (!this.keys["ArrowLeft"] && this.keys["ArrowRight"])
|
||||
this.movePiece(1, 0);
|
||||
else if (this.keys["ArrowLeft"] && this.keys["ArrowRight"])
|
||||
this.movePiece(this.direction, 0);
|
||||
this.move = false;
|
||||
}
|
||||
|
||||
/*if (this.keys["ArrowDown"])
|
||||
this.softDrop();*/
|
||||
}
|
||||
|
||||
registerListeners() {
|
||||
window.addEventListener("keydown", (e) => {
|
||||
@ -482,8 +516,18 @@ export default class extends Aview {
|
||||
|
||||
if (this.isPaused) return;
|
||||
|
||||
if (e.key === "ArrowLeft") this.movePiece(-1, 0);
|
||||
else if (e.key === "ArrowRight") this.movePiece(1, 0);
|
||||
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();
|
||||
@ -504,9 +548,25 @@ export default class extends Aview {
|
||||
|
||||
loop(timestamp: number) {
|
||||
if (!this.lastDrop) this.lastDrop = timestamp;
|
||||
if (!this.isPaused && !this.isGameOver) {
|
||||
if (timestamp - this.lastDrop > this.dropInterval) {
|
||||
if (!this.movePiece(0, 1)) this.lockPiece();
|
||||
if (!this.isPaused && !this.isGameOver)
|
||||
{
|
||||
this.inputManager();
|
||||
if (this.isLocking ? timestamp - this.lastDrop > 500 : timestamp - this.lastDrop > this.dropInterval)
|
||||
{
|
||||
if (this.isLocking && this.lockRotationCount == this.lockLastRotationCount)
|
||||
this.lockPiece();
|
||||
this.lockLastRotationCount = this.lockRotationCount;
|
||||
if (!this.movePiece(0, 1))
|
||||
{
|
||||
if (!this.isLocking)
|
||||
{
|
||||
this.lockRotationCount = 0;
|
||||
this.lockLastRotationCount = 0;
|
||||
this.isLocking = true;
|
||||
}
|
||||
}
|
||||
else if (this.isLocking)
|
||||
this.lockRotationCount = 0;
|
||||
this.lastDrop = timestamp;
|
||||
}
|
||||
}
|
||||
@ -516,6 +576,8 @@ export default class extends Aview {
|
||||
|
||||
drawGrid() {
|
||||
const ctx = this.ctx;
|
||||
if (!ctx || !this.canvas)
|
||||
return;
|
||||
ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
ctx.strokeStyle = "#222";
|
||||
for (let r = 0; r <= ROWS; r++) {
|
||||
@ -556,7 +618,7 @@ export default class extends Aview {
|
||||
}
|
||||
|
||||
drawHold() {
|
||||
if (!this.holdPiece) return;
|
||||
if (!this.holdPiece || !this.holdCtx) return;
|
||||
|
||||
this.holdCtx.clearRect(0, 0, 200, 200);
|
||||
let y: number = 0;
|
||||
@ -567,6 +629,7 @@ export default class extends Aview {
|
||||
this.holdCtx.fillStyle = this.canHold
|
||||
? COLORS[this.holdPiece.findColorIndex()]
|
||||
: "gray";
|
||||
console.log(this.holdCtx.fillStyle);
|
||||
this.holdCtx.fillRect(
|
||||
x * BLOCK +
|
||||
1 +
|
||||
@ -584,9 +647,9 @@ export default class extends Aview {
|
||||
}
|
||||
|
||||
drawQueue() {
|
||||
if (!this.queueCtx) return ;
|
||||
this.queueCtx.clearRect(0, 0, 500, 500);
|
||||
let placement: number = 0;
|
||||
console.log(this.nextQueue.slice(0, 5));
|
||||
for (const nextPiece of this.nextQueue.slice(0, 5)) {
|
||||
let y: number = 0;
|
||||
for (const row of TETROMINOES[nextPiece][0]) {
|
||||
@ -620,22 +683,26 @@ export default class extends Aview {
|
||||
}
|
||||
|
||||
fillBlock(x: number, y: number, color: string) {
|
||||
if (!this.ctx) return;
|
||||
const ctx = this.ctx;
|
||||
ctx.fillStyle = color;
|
||||
ctx.fillRect(x * BLOCK + 1, y * BLOCK + 1, BLOCK - 2, BLOCK - 2);
|
||||
}
|
||||
fillGhostBlock(x: number, y: number, color: string) {
|
||||
if (!this.ctx) return;
|
||||
const ctx = this.ctx;
|
||||
ctx.strokeStyle = color;
|
||||
ctx.strokeRect(x * BLOCK + 1, y * BLOCK + 1, BLOCK - 2, BLOCK - 2);
|
||||
}
|
||||
|
||||
clearBlock(x: number, y: number) {
|
||||
if (!this.ctx) return;
|
||||
const ctx = this.ctx;
|
||||
ctx.clearRect(x * BLOCK + 1, y * BLOCK + 1, BLOCK - 2, BLOCK - 2);
|
||||
}
|
||||
|
||||
drawHUD() {
|
||||
if (!this.ctx || !this.canvas) return;
|
||||
const ctx = this.ctx;
|
||||
ctx.fillStyle = "rgba(0,0,0,0.6)";
|
||||
ctx.fillRect(4, 4, 120, 60);
|
||||
@ -675,6 +742,7 @@ export default class extends Aview {
|
||||
}
|
||||
|
||||
draw() {
|
||||
if (!this.ctx || !this.canvas) return;
|
||||
// clear everything
|
||||
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
|
||||
@ -698,6 +766,7 @@ export default class extends Aview {
|
||||
this.drawBoard();
|
||||
this.drawPiece();
|
||||
this.drawHUD();
|
||||
this.drawQueue();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user