Merge pull request 'update malouze cooking' (#6) from main into malouze-cooking

Reviewed-on: https://codeberg.org/adrien-lsh/ft_transcendence/pulls/6
This commit is contained in:
kbz_8
2024-02-14 17:53:57 +00:00
91 changed files with 2665 additions and 1360 deletions

View File

@ -1,12 +1,3 @@
*{
color: #cccccc;
font-size: 35px;
background-color: #1a1a1a;
}
body {
}
#app #avatar {
max-height: 10em;
max-width: 10em;
@ -34,3 +25,7 @@ body {
opacity: 0;
transition: opacity 0.25s;
}
#languageSelector > .dropdown-item.active {
background-color: transparent;
}

View File

@ -1,17 +0,0 @@
#app #main .account
{
color: #1a1a1a;
}
#app #main
{
width: 60%;
display: flex;
flex-direction: column;
color: #1a1a1a;
}
#app #main .profile
{
color: #1a1a1a;
}

View File

@ -1,14 +1,25 @@
#app * {
font-size: 30px;
}
#app #username
{
font-size: 0.8em;
}
#app #block {
#app #block, #app #friend {
cursor: pointer;
font-size: 0.7em;
text-decoration: underline;
}
#app {
margin-top: 20px;
margin-top: 1em;
}
#app #yes, #app #no {
display:inline;
cursor: pointer;
font-size: 0.7em;
text-decoration: underline;
}

View File

@ -1,3 +1,7 @@
#app * {
font-size: 40px;
}
#app img
{
@ -78,7 +82,6 @@
border: none;
outline: none;
border-bottom: 0.15em solid green;
caret-color: green;
color: green;
font-size: 0.8em;
}
@ -106,9 +109,8 @@
word-wrap: break-word;
}
#app #invite {
#app #invite, #app #yes, #app #no {
position: relative;
background-color: green;
border: none;
color: white;
text-align: center;
@ -118,3 +120,15 @@
width: 4em;
cursor: pointer;
}
#app #yes, #app #no {
position: relative;
border: none;
color: white;
text-align: center;
text-decoration: none;
font-size: 0.8em;
height: 2em;
width: 2em;
cursor: pointer;
}

View File

@ -0,0 +1,11 @@
#app * {
font-size: 30px;
}
#app #main
{
width: 60%;
display: flex;
flex-direction: column;
}

View File

@ -1,45 +1,74 @@
import { reloadView } from '../index.js'
export default class LanguageManager {
constructor() {
this.availableLanguages = ['en', 'fr'];
this.availableLanguages = ['en', 'fr', 'tp', 'cr'];
this.dict = null;
this.currentLang = 'en'
this.chosenLang = localStorage.getItem('preferedLanguage') || this.currentLang;
if (this.chosenLang !== this.currentLang && this.availableLanguages.includes(this.chosenLang)) {
this.translatePage();
this.loading = this.translatePage();
this.currentLang = this.chosenLang;
} else {
this.loading = this.loadDict(this.chosenLang);
}
document.getElementById('languageDisplay').innerHTML =
document.querySelector(`#languageSelector > [value=${this.currentLang}]`)?.innerHTML;
}
async translatePage() {
if (this.currentLang === this.chosenLang)
return;
let dictUrl = `${location.origin}/static/js/lang/${this.chosenLang}.json`;
let translation = await fetch(dictUrl).then(response => {
if (response.status !== 200)
return null;
return response.json();
});
if (!translation) {
console.log(`No translation found for language ${this.chosenLang}`);
await this.loadDict(this.chosenLang);
if (!this.dict)
return 1;
}
document.querySelectorAll('[data-i18n]').forEach(el => {
let key = el.getAttribute('data-i18n');
el.innerHTML = translation[key];
el.innerHTML = this.dict[key];
})
await reloadView();
this.currentLang = this.chosenLang;
return 0;
}
async changeLanguage(lang) {
if (lang === this.currentLang || !this.availableLanguages.includes(lang))
return;
return 1;
this.chosenLang = lang;
if (await this.translatePage() !== 0)
return;
return 1;
this.currentLang = this.chosenLang;
localStorage.setItem('preferedLanguage', lang);
document.getElementById('languageDisplay').innerHTML =
document.querySelector(`#languageSelector > [value=${this.currentLang}]`)?.innerHTML;
return 0;
}
async loadDict(lang) {
let dictUrl = `${location.origin}/static/js/lang/${lang}.json`;
let response = await fetch(dictUrl);
if (response.status !== 200) {
console.log(`No translation found for language ${lang}`);
return;
}
this.dict = await response.json();
}
async waitLoading() {
await this.loading;
}
get(key, defaultTxt) {
if (!this.dict)
return defaultTxt;
return this.dict[key] || defaultTxt;
}
}

View File

@ -9,7 +9,7 @@ class MyProfile extends Profile
*/
constructor (client)
{
super(client, "me")
super(client, "../me")
}
/**
@ -19,7 +19,7 @@ class MyProfile extends Profile
*/
async change_avatar(form_data)
{
let response = await this.client._patch_file(`/api/profiles/me`, form_data);
let response = await this.client._patch_file(`/api/profiles/settings`, form_data);
let response_data = await response.json()
return response_data;
@ -27,4 +27,4 @@ class MyProfile extends Profile
}
export {MyProfile}
export {MyProfile}

View File

@ -16,19 +16,16 @@ class Account
/**
* @param {String} username
* @param {String} password
* @returns {?Promise<Object>}
* @returns {Response}
*/
async create(username, password)
{
let response = await this.client._post("/api/accounts/register", {username: username, password: password});
let response_data = await response.json()
if (response_data == "user created")
{
if (response.status === 201)
await this.client._update_logged(true);
return null;
}
return response_data
return response;
}
/**

View File

@ -1,3 +1,4 @@
import { navigateTo } from "../../index.js";
import {create_popup} from "../../utils/noticeUtils.js";
class Notice {
@ -5,11 +6,10 @@ class Notice {
this.client = client;
this.data = {};
// users online, invited by, asked friend by,
let data_variable = ["online", "invited", "asked"];
for (let i in data_variable) {
// users online, invited by ..., asked by ..., asker to ...
let data_variable = ["online", "invited", "asked", "asker"];
for (let i in data_variable)
this.data[data_variable[i]] = [];
}
this.connect();
@ -20,15 +20,20 @@ class Notice {
this.chatSocket = new WebSocket(url);
this.chatSocket.onmessage = (event) =>{
let data = JSON.parse(event.data);
//console.log("notice: ", data);
if (data.type == "invite")
this.receiveInvite(data);
else if (data.type == "online_users" || data.type == "disconnect")
this.receiveOnlineUser(data);
let send = JSON.parse(event.data);
//console.log("notice: ", send);
try {
this["receive_" + send.type](send);
}
catch (error) {
console.log("receive_" + send.type + ": Function not found");
}
}
this.chatSocket.onopen = (event) => {
this.getOnlineUser();
this.ask_friend();
}
}
@ -37,64 +42,126 @@ class Notice {
return ;
this.chatSocket.close();
}
async reconnect() {
this.disconnect();
this.connect();
}
async accept_invite(invitedBy) {
this.sendRequest({
type: "accept_invite",
targets: [invitedBy],
});
}
async sendInvite(id_inviter, id_inviteds) {
if (this.chatSocket == undefined)
return;
async receive_accept_invite(send) {
this.chatSocket.send(JSON.stringify({
this.data["invited"] = send.invites;
let id_game = send["id_game"];
navigateTo("/game/" + id_game);
}
async refuse_invite(invitedBy) {
this.sendRequest({
type: "refuse_invite",
targets: [invitedBy],
});
}
async receive_refuse_invite(send) {
this.data["invited"] = send.invites;
if (send.author_id == this.client.me.id) {
if (this.rewrite_invite !== undefined)
this.rewrite_invite();
}
else {
let sender = await this.client.profiles.getProfileId(send.author_id);
create_popup(sender.username + " refuse your invitation");
}
}
async send_invite(id_inviteds) {
this.sendRequest({
type: "invite",
targets: id_inviteds,
}));
time: new Date().getTime(),
});
}
async receiveInvite(data) {
async receive_invite(send) {
if (data.content === "notice return") {
if (data.status == 200) {
for (let target in data.targets)
this.data["invited"].push(target);
if (this.client.me == undefined)
return ;
let content = send.invites;
if (send.author_id == this.client.me.id) {
if (send.status == 200) {
for (let target in send.targets)
return create_popup("Invitation send");
}
else if (data.status == 404)
else if (send.status == 444)
return create_popup("User not connected");
else if (data.status == 409)
else if (send.status == 409)
return create_popup("Already invited");
}
else {
let sender = await this.client.profiles.getProfile(data.author_id);
this.inviter.push(data.author_id);
// Regarder qu'il est bien invité par l'auteur
// Et qu'il n'est pas déjà invité
if (!content.includes(send.author_id) ||
this.data["invited"].includes(send.author_id))
return;
this.data["invited"] = content;
let sender = await this.client.profiles.getProfileId(send.author_id);
create_popup("Invitation received by " + sender.username);
// Géré la reception de l'invitation
if (this.rewrite_invite !== undefined)
this.rewrite_invite();
}
}
async getOnlineUser() {
if (this.chatSocket == undefined)
return;
this.online_users = {};
this.chatSocket.send(JSON.stringify({
this.sendRequest({
type: "online_users",
targets: "all",
}));
targets: [],
time: new Date().getTime(),
});
}
async receiveOnlineUser(data) {
if (data.content !== undefined) {
async receive_online_users(send) {
let content = send.online;
if (content !== undefined) {
if (this.online_users.length > 0) {
if (this.data["online"].length > 0) {
// get all disconnect user
let disconnects = this.online_users.filter(id => !Object.keys(data.content).includes(id));
//let disconnects = this.data["online"].filter(id => !Object.keys(content).includes(id));
let disconnects = [];
for (const [key, value] of Object.entries(this.data["online"])) {
if (content[key] == "red" && value == "green")
disconnects.push(key);
}
// delete invite
this.data["invited"] = this.data["invited"].filter(id => !disconnects.includes(id));
@ -102,12 +169,139 @@ class Notice {
//console.log(this.data["invited"]);
}
this.data["online"] = Object.keys(data.content);
this.data["online"] = content;
if (this.rewrite_usernames !== undefined)
this.rewrite_usernames();
}
}
async ask_friend(user_id=undefined) {
this.sendRequest({
type: "ask_friend",
targets: [user_id],
time: new Date().getTime(),
});
}
async receive_ask_friend(send) {
let my_id = (this.client.me && this.client.me.id) || send.author_id;
if (send.author_id == my_id) {
if (send.status == 400)
create_popup("Friend ask error");
else if (send.status == 409)
create_popup("Already asked friend");
}
//if (!send.asked.includes(send.author_id) ||
//this.data["asked"].includes(send.author_id))
//return;
//if (!send.asker.includes(send.author_id) ||
//this.data["asker"].includes(send.author_id))
//return;
this.data["asked"] = send.asked;
this.data["asker"] = send.asker;
if (send.author_id != my_id) {
let sender = await this.client.profiles.getProfileId(send.author_id);
if (this.data["asker"].includes(send.author_id))
create_popup(sender.username + " ask you as friend");
if (this.rewrite_profile !== undefined)
await this.rewrite_profile();
}
}
async remove_friend(user_id) {
this.sendRequest({
type: "remove_friend",
targets: [user_id],
time: new Date().getTime(),
});
}
async receive_remove_friend(send) {
if (send.author_id == this.client.me.id) {
if (send.status == 400)
create_popup("Error remove Friend");
else if (send.status == 409)
create_popup("Not friend, wtf");
}
if (this.rewrite_profile !== undefined)
await this.rewrite_profile();
this.receive_online_users(send);
}
async accept_friend(user_id) {
this.sendRequest({
type: "accept_friend",
targets: [user_id],
time: new Date().getTime(),
});
}
async receive_accept_friend(send) {
this.data["asked"] = send.asked;
this.data["asker"] = send.asker;
let sender = await this.client.profiles.getProfileId(send.author_id);
if (send.author_id == this.client.me.id) {
if (send.status == 400)
create_popup("Error accept Friend");
else if (send.status == 404)
create_popup("Not found request Friend");
else if (send.status == 409)
create_popup("Already Friend, wtf");
}
else {
create_popup(sender.username + " accept your friend request");
}
if (this.rewrite_profile !== undefined)
await this.rewrite_profile();
this.receive_online_users(send);
}
async refuse_friend(user_id) {
this.sendRequest({
type: "refuse_friend",
targets: [user_id],
time: new Date().getTime(),
});
}
async receive_refuse_friend(send) {
this.data["asked"] = send.asked;
this.data["asker"] = send.asker;
let sender = await this.client.profiles.getProfileId(send.author_id);
if (send.author_id == this.client.me.id) {
if (send.status == 400)
create_popup("Error refuse Friend");
else if (send.status == 404)
create_popup("Not found request Friend");
else if (send.status == 409)
create_popup("Already Friend, WTF");
}
if (this.rewrite_profile !== undefined)
await this.rewrite_profile();
}
async sendRequest(content) {
if (this.chatSocket == undefined)
return;
this.chatSocket.send(JSON.stringify(content));
}
}
export {Notice}

View File

@ -53,7 +53,7 @@ class Client
this.tournaments = new Tourmanents(this);
/**
* @type {Boolean} A private var represent if the is is log NEVER USE IT use await isAuthentificated()
* @type {Boolean} A private var represent if the is is log NEVER USE IT use await isAuthenticated()
*/
this._logged = undefined;
@ -73,13 +73,14 @@ class Client
this.notice = new Notice(this);
this.lang = new LanguageManager;
}
/**
* The only right way to determine is the user is logged
* @returns {Promise<Boolean>}
*/
async isAuthentificate()
async isAuthenticated()
{
if (this._logged == undefined)
this._logged = await this._test_logged();
@ -114,7 +115,8 @@ class Client
headers: {
"Content-Type": "application/json",
"X-CSRFToken": getCookie("csrftoken"),
},
'Accept-Language': this.lang.currentLang,
},
body: JSON.stringify(data),
});
return response;
@ -190,6 +192,7 @@ class Client
{
this.me = new MyProfile(this);
await this.me.init();
this.notice.reconnect();
document.getElementById('navbarLoggedOut').classList.add('d-none');
document.getElementById('navbarLoggedIn').classList.remove('d-none');
document.getElementById('navbarDropdownButton').innerHTML = this.me.username;
@ -198,6 +201,7 @@ class Client
else
{
this.me = undefined;
this.notice.reconnect();
document.getElementById('navbarLoggedOut').classList.remove('d-none');
document.getElementById('navbarLoggedIn').classList.add('d-none');
document.getElementById('navbarDropdownButton').innerHTML = 'Me';
@ -232,7 +236,7 @@ class Client
}
/**
* Determine if the user is logged. NEVER USE IT, USE isAuthentificated()
* Determine if the user is logged. NEVER USE IT, USE isAuthenticated()
* @returns {Promise<Boolean>}
*/
async _test_logged()

View File

@ -4,6 +4,7 @@ import { GameConfig } from "./GameConfig.js"
import { Player } from "./Player.js";
import { Time } from "./Time.js";
import { Wall } from "./Wall.js";
import { Client } from "../client.js";
class Game
{
@ -21,7 +22,7 @@ class Game
/**
*
* @returns {Number}
* @returns {Promise<Number>}
*/
async init()
{
@ -32,23 +33,52 @@ class Game
let response_data = await response.json();
/**
* @type {[Number]}
*/
this.players_id = response_data.players_id;
/**
* @type {String}
*/
this.state = response_data.state;
/**
* @type {Boolean}
*/
this.started = response_data.started;
/**
* @type {Boolean}
*/
this.finished = response_data.finished;
/**
* @type {Number}
*/
this.winner_id = this.finished ? response_data.winner_id : undefined;
if (this.finished === true)
return 0;
/**
* @type {GameConfig}
*/
this.config = new GameConfig(this.client);
let ret = await this.config.init();
if (ret !== 0)
return ret;
/**
* @type {Time}
*/
this.time = new Time();
this.last_pos = null
/**
* @type {Boolean}
*/
this._inited = false;
return 0;
@ -72,14 +102,14 @@ class Game
*
* @param {CanvasRenderingContext2D} ctx
*/
draw(ctx)
render(ctx)
{
if(ctx instanceof CanvasRenderingContext2D)
{
ctx.clearRect(0, 0, this.config.size_x, this.config.size_y);
}
this.draw_sides(ctx);
this.ball.draw(ctx);
this.ball.render(ctx);
}
_send(data)
@ -93,50 +123,25 @@ class Game
}
}
/**
* @param {Number} position
* @param {Number} time
*/
_send_paddle_position(position, time)
{
if (this.last_pos !== null && this.last_pos.time >= time)
return;
this.last_pos = {"time": time, "position": position};
this._send({"detail": "update_my_paddle_pos", ...this.last_pos});
}
_receive_player_join(player_data)
{
console.log(player_data)
let index = this.players.indexOf((player) => player.id === player_data.user_id);
this.players[index].is_connected = true;
}
_receive_player_leave(player_data)
{
let index = this.players.indexOf((player) => player.id === player_data.user_id);
this.players[index].is_connected = false;
}
_receive_update_ball(data)
{
this.ball.position_x = data.position_x
this.ball.position_y = data.position_y
this.ball.position_x = data.position_x
this.ball.position_x = data.position_x
this._send({"detail": "update_my_paddle_pos", ...{"time": time, "position": position}});
}
_receive_update_paddle(data)
{
let player = this.players.find((player) => player.id === data.user_id);
if (player === null)
{
this._receive_player_join(data);
return;
}
player.is_connected = data.is_connected;
player.update_pos(data.position.position, data.position.time);
player.from_json(data);
}
_receive_ball(data)
{
this.ball.from_json(data);
}
_receive(data)
@ -144,26 +149,30 @@ class Game
if (data.detail === "update_paddle")
this._receive_update_paddle(data);
else if (data.detail === "update_ball")
this._receive_update_ball(data);
this._receive_ball(data)
else if (data.detail === "init_game")
this._init_game(data)
else if (data.detail === "player_join")
this._receive_player_join(data)
else if (data.detail === "player_leave")
this._receive_player_leave(data)
this._init_game(data);
}
_init_game(data)
{
const ball_data = data.ball;
this.ball = new Ball(this, ball_data.position_x, ball_data.position_y, ball_data.velocity_x, ball_data.velocity_y);
/**
* @type {Ball}
*/
this.ball = (new Ball(this)).from_json(data.ball)
/**
* @type {[Wall]}
*/
this.walls = [];
const walls_data = data.walls;
walls_data.forEach((wall_data) => {
this.walls.push(new Wall().from_json(wall_data));
});
/**
* @type {[Player]}
*/
this.players = []
const players_data = data.players;
players_data.forEach((player_data) => {

View File

@ -24,14 +24,17 @@ class GameConfig
* @type {Number}
*/
this.size_x = response_data.MAP_SIZE_X;
/**
* @type {Number}
*/
this.size_y = response_data.MAP_SIZE_Y;
/**
* @type {Number}
*/
this.center_x = this.size_x / 2;
/**
* @type {Number}
*/
@ -63,10 +66,12 @@ class GameConfig
* @type {Number}
*/
this.ball_size = response_data.BALL_SIZE;
/**
* @type {Number}
*/
this.ball_spawn_x = this.center_x;
/**
* @type {Number}
*/

View File

@ -9,8 +9,32 @@ class Segment
*/
constructor(start, stop)
{
/**
* @type {Point}
*/
this.start = start === undefined ? new Point() : start;
/**
* @type {Point}
*/
this.stop = stop === undefined ? new Point() : stop;
}
angle()
{
let x = this.start.x - this.stop.x,
y = this.start.y - this.stop.y;
return Math.atan2(y, x);
}
len()
{
let x = this.start.x - this.stop.x,
y = this.start.y - this.stop.y;
return (x ** 2 + y ** 2) ** (1 / 2);
}
/**

View File

@ -17,7 +17,9 @@ class Time
deltaTime()
{
return (this._current_frame - this._last_frame) !== NaN ? this._current_frame - this._last_frame : 0;
if (this._last_frame === undefined)
return 0;
return (this._current_frame - this._last_frame);
}
deltaTimeSecond()

View File

@ -23,7 +23,7 @@ class MatchMaking
*/
async start(receive_func, disconnect_func, mode)
{
if (!await this.client.isAuthentificate())
if (!await this.client.isAuthenticated())
return null;
let url = `${window.location.protocol[4] === 's' ? 'wss' : 'ws'}://${window.location.host}/ws/matchmaking/${mode}`;

View File

@ -5,7 +5,7 @@ class Profile
/**
* @param {Client} client
*/
constructor (client, username, id = undefined, avatar_url = undefined)
constructor (client, username=undefined, id=undefined, avatar_url=undefined)
{
/**
* @type {Client} client
@ -31,6 +31,7 @@ class Profile
* @type {Boolean}
*/
this.isBlocked = false;
this.isFriend = false;
}
/**
@ -39,7 +40,11 @@ class Profile
*/
async init()
{
let response = await this.client._get(`/api/profiles/${this.username}`);
let response;
if (this.username !== undefined)
response = await this.client._get(`/api/profiles/user/${this.username}`);
else
response = await this.client._get(`/api/profiles/id/${this.id}`);
if (response.status !== 200)
return response.status;
@ -47,25 +52,49 @@ class Profile
let response_data = await response.json();
this.id = response_data.user_id;
this.username = response_data.username;
this.avatar_url = response_data.avatar_url;
this.avatar_url = response_data.avatar;
if (this.client.me == undefined)
return;
await this.getBlock();
await this.getFriend();
}
async getBlock() {
let block_response = await this.client._get("/api/profiles/block");
if (block_response.status != 200)
return
let block_data = await block_response.json();
let block_list = JSON.parse(block_data);
let block_list = JSON.parse(block_data["blockeds"]);
let client_id = block_data["user_id"];
block_list.forEach(block => {
let blocker = block.fields.blocker;
let blocked = block.fields.blocked;
if (blocker == this.client.me.user_id && blocked == user_id)
if (blocker == client_id && blocked == this.id)
return this.isBlocked = true;
});
}
async getFriend() {
let friend_response = await this.client._get("/api/profiles/friend");
this.isFriend = false;
if (friend_response.status != 200)
return this.isFriend;
let friend_data = await friend_response.json();
let friends_list = friend_data["friends"];
let client_id = friend_data["user_id"];
friends_list.forEach(friend => {
if (friend == this.id) {
this.isFriend = true;
return this.isFriend;
}
});
return this.isFriend;
}
}
export {Profile}

View File

@ -24,7 +24,7 @@ class Profiles
let profiles = []
response_data.forEach((profile) => {
profiles.push(new Profile(this.client, profile.username, profile.user_id, profile.avatar_url))
profiles.push(new Profile(this.client, profile.username, profile.user_id, profile.avatar))
});
return profiles;
}
@ -32,7 +32,7 @@ class Profiles
/**
*
* @param {String} username
* @returns {?Profile}
* @returns {?Promise<Profile>}
*/
async getProfile(username)
{
@ -42,6 +42,14 @@ class Profiles
return profile;
}
async getProfileId(id)
{
let profile = new Profile(this.client, undefined, id);
if (await profile.init())
return null;
return profile;
}
/**
* Block a user
* @param {Number} user_id
@ -51,7 +59,7 @@ class Profiles
// blocker & blocked
let response = await this.client._post("/api/profiles/block", {
users_id:[this.client.me.user_id, user_id],
users_id:[this.client.me.id, user_id],
});
let data = await response.json();
@ -68,7 +76,7 @@ class Profiles
// blocker & blocked
let response = await this.client._delete("/api/profiles/block", {
users_id:[this.client.me.user_id, user_id],
users_id:[this.client.me.id, user_id],
});
let data = await response.json();

View File

@ -120,7 +120,7 @@ class Tourmanent
*/
async join(receive_func, disconnect_func)
{
if (!await this.client.isAuthentificate())
if (!await this.client.isAuthenticated())
return null;
let url = `${window.location.protocol[4] === 's' ? 'wss' : 'ws'}://${window.location.host}/ws/tournaments/${this.id}`;

View File

@ -1,10 +1,8 @@
import { Client } from "./api/client.js";
import LoginView from "./views/accounts/LoginView.js";
import Dashboard from "./views/Dashboard.js";
import Search from "./views/Search.js";
import HomeView from "./views/HomeView.js";
import RegisterView from "./views/accounts/RegisterView.js";
import LogoutView from "./views/accounts/LogoutView.js";
import GameOfflineView from "./views/GameOfflineView.js";
//import GameView from "./views/GameView.js";
@ -13,17 +11,19 @@ import GameView from "./views/GameView3D.js";
import PageNotFoundView from './views/PageNotFoundView.js'
import AbstractRedirectView from "./views/abstracts/AbstractRedirectView.js";
import MeView from "./views/MeView.js";
import SettingsView from "./views/SettingsView.js";
import ProfilePageView from "./views/ProfilePageView.js";
import MatchMakingView from "./views/MatchMakingView.js";
import TournamentPageView from "./views/tournament/TournamentPageView.js";
import TournamentsView from "./views/tournament/TournamentsListView.js";
import TournamentCreateView from "./views/tournament/TournamentCreateView.js";
import AuthenticationView from "./views/accounts/AuthenticationView.js";
let client = new Client(location.protocol + "//" + location.host)
let client = new Client(location.origin);
let lang = client.lang;
let lastView = undefined
let lastPageUrlBeforeLogin = undefined
let lastView = undefined;
let lastPageUrlBeforeLogin = undefined;
const pathToRegex = path => new RegExp("^" + path.replace(/\//g, "\\/").replace(/:\w+/g, "(.+)") + "$");
@ -37,11 +37,12 @@ const getParams = match => {
};
const navigateTo = async (uri) => {
if (await router(uri) !== 0)
return;
history.pushState(null, null, uri);
if (await router(uri) !== 0)
return;
let link = document.querySelector('a[href=\'' + location.pathname + '\']');
if (link) {
document.querySelector('[data-link].active')?.classList.remove('active');
@ -49,9 +50,14 @@ const navigateTo = async (uri) => {
}
};
const reloadView = async _ => {
await lastView?.leavePage();
await renderView(lastView);
}
async function renderView(view)
{
let content = await view.getHtml();
let content = await view?.getHtml();
if (content == null)
return 1;
@ -74,12 +80,12 @@ const router = async(uri) => {
{ path: "/tournaments/create", view: TournamentCreateView },
{ path: "/tournaments/:id", view: TournamentPageView },
{ path: "/tournaments/", view: TournamentsView },
{ path: "/login", view: LoginView },
{ path: "/login", view: AuthenticationView },
{ path: "/register", view: AuthenticationView },
{ path: "/logout", view: LogoutView },
{ path: "/register", view: RegisterView },
{ path: "/search", view: Search },
{ path: "/home", view: HomeView },
{ path: "/me", view: MeView },
{ path: "/settings", view: SettingsView },
{ path: "/matchmaking", view: MatchMakingView },
{ path: "/games/offline", view: GameOfflineView },
{ path: "/games/:id", view: GameView },
@ -110,12 +116,13 @@ const router = async(uri) => {
const view = new match.route.view(getParams(match), lastPageUrlBeforeLogin);
if (!(view instanceof AuthenticationView) && ! (view instanceof LogoutView))
lastPageUrlBeforeLogin = uri;
if (view instanceof AbstractRedirectView && await view.redirect())
return 1;
lastView = view;
if (uri !== '/login' && uri !== '/register' && uri !== '/logout')
lastPageUrlBeforeLogin = uri;
if (await renderView(view))
return 1;
@ -138,13 +145,22 @@ document.addEventListener("DOMContentLoaded", async () => {
});
//Languages
await lang.waitLoading();
Array.from(document.getElementById('languageSelector').children).forEach(el => {
el.onclick = _ => client.lang.changeLanguage(el.value);
el.onclick = async _ => {
if (await lang.changeLanguage(el.value))
return;
console.log(lang);
document.querySelector('#languageSelector > .active')?.classList.remove('active');
el.classList.add('active');
};
});
document.querySelector(`#languageSelector > [value=${lang.chosenLang}]`)
?.classList.add('active');
await client.isAuthentificate();
await client.isAuthenticated();
router(location.pathname);
document.querySelector('a[href=\'' + location.pathname + '\']')?.classList.add('active');
});
export { client, navigateTo }
export { client, lang, navigateTo, reloadView }

View File

@ -0,0 +1,39 @@
{
"navbarSearch": "cherchbeh",
"navbarHome": "Quoicoubouse",
"navbarLogin": "Quoicouconnec",
"navbarRegister": "Quoicougistré",
"navbarProfile": "Mon crampté Profile",
"navbarSettings": "Roue Crampté",
"navbarLogout": "Déconnexion crampté",
"homeWindowTitle": "Quoicoubouse",
"homeTitle": "Quoicoubouse",
"homeOnline": "Jouer en crampté",
"homeOffline": "Jouer hors crampté",
"homeSettings": "Roue Crampté",
"homeLogout": "Déconnexion crampté",
"loginWindowTitle": "Quoicouconnec",
"loginFormTitle": "Quoicouconnec",
"loginFormUsername": "Nom d'crampté",
"loginFormPassword": "Mot de crampté",
"loginFormButton": "Quoicouconnec",
"loginNoAccount": "Pas de compte encore crampté?",
"loginRegister": "Quoicougistré",
"errorEmptyField": "Ce champ ne peut pas être vide crampté.",
"logoutWindowTitle": "Déconnexion crampté",
"registerWindowTitle": "Quoicougistré",
"registerFormTitle": "Quoicougistré",
"registerFormUsername": "Nom d'crampté",
"registerFormPassword": "Mot de crampté",
"registerFormButton": "Quoicougistré",
"registerAlreadyAccount": "Déjà un compte crampté?",
"registerLogin": "Quoicouconnec",
"404WindowTitle": "Pas crampté",
"SearchWindowTitle": "cherchbeh",
"profileAddFriend": "Demander le cramptéman",
"profileRemoveFriend": "Supprimer le cramptéman",
"profileDenyRequest": "Refuser le cramptéman",
"profileAcceptRequest": "Accepter le cramptéman",
"profileUnblock": "Quoicoudebloquer",
"profileBlock": "Quoicoubloquer"
}

View File

@ -5,5 +5,35 @@
"navbarRegister": "Register",
"navbarProfile": "My Profile",
"navbarSettings": "Settings",
"navbarLogout": "Logout"
"navbarLogout": "Logout",
"homeWindowTitle": "Home",
"homeTitle": "Home",
"homeOnline": "Play online",
"homeOffline": "Play offline",
"homeSettings": "Settings",
"homeLogout": "Logout",
"loginWindowTitle": "Login",
"loginFormTitle": "Login",
"loginFormUsername": "Username",
"loginFormPassword": "Password",
"loginFormButton": "Login",
"loginNoAccount": "No account yet?",
"loginRegister": "Register",
"errorEmptyField": "This field may not be blank.",
"logoutWindowTitle": "Logout",
"registerWindowTitle": "Register",
"registerFormTitle": "Register",
"registerFormUsername": "Username",
"registerFormPassword": "Password",
"registerFormButton": "Register",
"registerAlreadyAccount": "Already have an account?",
"registerLogin": "Login",
"404WindowTitle": "Not Found",
"SearchWindowTitle": "Search",
"profileAddFriend": "Ask Friend",
"profileRemoveFriend": "Remove Friend",
"profileDenyRequest": "Decline Friend",
"profileAcceptRequest": "Accept Friend",
"profileUnblock": "Unblock",
"profileBlock": "Block"
}

View File

@ -1,9 +1,39 @@
{
"navbarSearch": "Recherche",
"navbarHome": "Maison",
"navbarLogin": "Se connecter",
"navbarLogin": "Connexion",
"navbarRegister": "S'inscrire",
"navbarProfile": "Mon Profil",
"navbarSettings": "Paramètres",
"navbarLogout": "Se déconnecter"
"navbarLogout": "Déconnexion",
"homeWindowTitle": "Maison",
"homeTitle": "Maison",
"homeOnline": "Jouer en ligne",
"homeOffline": "Jouer hors ligne",
"homeSettings": "Paramètres",
"homeLogout": "Déconnexion",
"loginWindowTitle": "Connexion",
"loginFormTitle": "Connexion",
"loginFormUsername": "Nom d'utilisateur",
"loginFormPassword": "Mot de passe",
"loginFormButton": "Connexion",
"loginNoAccount": "Pas de compte?",
"loginRegister": "S'inscrire",
"errorEmptyField": "Ce champ ne peut être vide.",
"logoutWindowTitle": "Déconnexion",
"registerWindowTitle": "S'inscrire",
"registerFormTitle": "S'inscrire",
"registerFormUsername": "Nom d'utilisateur",
"registerFormPassword": "Mot de passe",
"registerFormButton": "S'inscrire",
"registerAlreadyAccount": "Déjà un compte?",
"registerLogin": "Connexion",
"404WindowTitle": "Pas trouvé",
"SearchWindowTitle": "Recherche",
"profileAddFriend": "Demander en ami",
"profileRemoveFriend": "Retirer l'ami",
"profileDenyRequest": "Refuser l'ami",
"profileAcceptRequest": "Accepter l'ami",
"profileUnblock": "Débloquer",
"profileBlock": "Bloquer"
}

View File

@ -0,0 +1,40 @@
{
"navbarSearch": "Lukin",
"navbarHome": "Tomo",
"navbarLogin": "Open",
"navbarRegister": "Sitelen",
"navbarProfile": "Sitelen mi",
"navbarSettings": "Nasin",
"navbarLogout": "Tawa ala",
"homeWindowTitle": "Tomo",
"homeTitle": "Tomo",
"homeOnline": "Mute tawa",
"homeOffline": "Mute lon",
"homeSettings": "Nasin",
"homeLogout": "Tawa ala",
"loginWindowTitle": "Open",
"loginFormTitle": "Open",
"loginFormUsername": "nimi pi jan Open",
"loginFormPassword": "nimi nasa",
"loginFormButton": "Open",
"loginNoAccount": "sina wile ala wile jo e nimi pi jan Open?",
"loginRegister": "Sitelen",
"errorEmptyField": "nimi ni li wile sitelen.",
"logoutWindowTitle": "Tawa ala",
"registerWindowTitle": "Sitelen",
"registerFormTitle": "Sitelen",
"registerFormUsername": "nimi pi jan sin",
"registerFormPassword": "nimi nasa",
"registerFormButton": "Sitelen",
"registerAlreadyAccount": "sina jo ala jo e nimi pi jan sin?",
"registerLogin": "Open",
"404WindowTitle": "Ala o lukin e ni",
"SearchWindowTitle": "Lukin",
"profileAddFriend": "kama jo e jan",
"profileRemoveFriend": "tawa ala e jan",
"profileDenyRequest": "ante e ijo ni",
"profileAcceptRequest": "kama jo e ijo ni",
"profileUnblock": "Tawa ala e nimi pi jan ni",
"profileBlock": "Tawa e nimi pi jan ni"
}

View File

@ -19,6 +19,10 @@ export default class extends AbstractView {
document.getElementById('stopGameButton').onclick = this.stopGame.bind(this);
}
async leavePage() {
this.game?.cleanup();
}
startGame() {
if (this.game == null) {
document.getElementById('startGameButton').innerHTML = 'Reset Game';
@ -26,6 +30,7 @@ export default class extends AbstractView {
}
else {
document.getElementById('app').removeChild(this.game.canvas);
document.getElementById('app').removeChild(this.game.scoresDisplay);
this.game.cleanup();
this.game = new Game;
}

View File

@ -13,7 +13,7 @@ export default class extends AbstractView
this.my_player = undefined;
}
keyStretchHandler(event)
keyReleaseHandler(event)
{
const idx = this.keys_pressed.indexOf(event.key);
if (idx != -1)
@ -26,7 +26,7 @@ export default class extends AbstractView
this.keys_pressed.push(event.key);
}
draw()
render_game()
{
const canva = document.getElementById('canva');
@ -40,36 +40,39 @@ export default class extends AbstractView
ctx.beginPath();
this.game.draw(ctx);
this.game.render(ctx);
ctx.strokeStyle = "#000000";
ctx.lineWidth = 10;
ctx.lineWidth = 1;
ctx.stroke();
}
render_game()
render()
{
let loop_id = setInterval(() => {
if (this.game === undefined)
clearInterval(loop_id);
if (this.my_player)
this.my_player.update_paddle(this.keys_pressed);
this.draw();
this.render_game();
this.game?.time.new_frame();
//clearInterval(loop_id);
// 1 sec fps
}, 1000 / 60);
}
register_key()
{
document.addEventListener('keydown', this.keyPressHandler.bind(this));
document.addEventListener('keyup', this.keyStretchHandler.bind(this));
this.keyPressHandler = this.keyPressHandler.bind(this);
this.keyReleaseHandler = this.keyReleaseHandler.bind(this);
document.addEventListener('keydown', this.keyPressHandler);
document.addEventListener('keyup', this.keyReleaseHandler);
}
unregister_key()
{
document.removeEventListener('keydown', this.keyPressHandler);
document.removeEventListener('keyup', this.keyStretchHandler);
document.removeEventListener('keyup', this.keyReleaseHandler);
}
async join_game()
@ -99,7 +102,7 @@ export default class extends AbstractView
this.register_key()
this.render_game();
this.render();
}
async update_game_state()

View File

@ -1,18 +1,19 @@
import AbstractAuthentificateView from "./abstracts/AbstractAuthentifiedView.js";
import { lang } from "../index.js";
import AbstractAuthenticatedView from "./abstracts/AbstractAuthenticatedView.js";
export default class extends AbstractAuthentificateView {
export default class extends AbstractAuthenticatedView {
constructor(params) {
super(params, "Home");
super(params, 'homeWindowTitle');
this.redirect_url = "/login"
}
async getHtml() {
return /* HTML */ `
<h1>HOME</h1>
<a href="/matchmaking" class="nav__link" data-link>Play a game</a>
<a href="/games/offline" class="nav__link" data-link>Play offline</a>
<a href="/me" class="nav__link" data-link>Me</a>
<a href="/logout" class="nav__link" data-link>Logout</a>
<h1>${lang.get('homeTitle', 'Home')}</h1>
<a href="/matchmaking" data-link>${lang.get('homeOnline', 'Play online')}</a>
<a href="/games/offline" data-link>${lang.get('homeOffline', 'Play offline')}</a>
<a href="/settings" data-link>${lang.get('homeSettings', 'Settings')}</a>
<a href="/logout" data-link>${lang.get('homeLogout', 'Logout')}</a>
`;
}
}

View File

@ -1,8 +1,8 @@
import { client, navigateTo } from "../index.js";
import { clear, fill_errors } from "../utils/formUtils.js";
import AbstractAuthentifiedView from "./abstracts/AbstractAuthentifiedView.js";
import AbstractAuthenticatedView from "./abstracts/AbstractAuthenticatedView.js";
export default class extends AbstractAuthentifiedView {
export default class extends AbstractAuthenticatedView {
constructor(params)
{
super(params, "Matchmaking");

View File

@ -1,13 +1,15 @@
import AbstractView from "./abstracts/AbstractView.js";
import { lang } from '../index.js'
export default class extends AbstractView {
constructor(params) {
super(params, "Dashboard");
super(params, '404WindowTitle');
}
async getHtml() {
return `
<h1>404 Bozo</h1>
<img src="https://media.giphy.com/media/pm0BKtuBFpdM4/giphy.gif">
<p>Git gud</p>
`;
}

View File

@ -1,25 +1,26 @@
import AbstractView from "./abstracts/AbstractView.js";
import { client } from "../index.js"
import { client, lang } from "../index.js"
export default class extends AbstractView {
constructor(params) {
super(params, params.username);
this.username = params.username;
super(params, decodeURI(params.username));
this.username = decodeURI(params.username);
}
async postInit()
{
this.profile = await client.profiles.getProfile(this.username);
if (this.profile === null)
return 404;
this.userId = this.profile.id;
this.user_id = this.profile.id;
this.info = document.getElementById("info");
// Username
let username = document.createElement("a");
username.id = "username";
username.appendChild(document.createTextNode(this.profile.username));
username.appendChild(document.createTextNode(this.username));
this.info.appendChild(username);
this.info.appendChild(document.createElement("br"));
@ -31,30 +32,97 @@ export default class extends AbstractView {
this.info.appendChild(avatar);
await this.blockButton();
await this.friendButton();
client.notice.rewrite_profile = async () => {
let result = await this.profile.getFriend();
await this.profile.getBlock()
await this.friendButton();
}
}
async blockButton() {
// Block option
if (await client.isAuthentificate() === false)
if (await client.isAuthenticated() === false)
return;
if (client.me.id != this.userId) {
let block = document.getElementById("block") || document.createElement("a");
if (client.me.id != this.user_id) {
let block = document.getElementById("block");
if (block == undefined) {
block = document.createElement("p");
this.info.appendChild(block);
}
block.id = "block";
block.innerText = "";
block.onclick = async () => {
if (!this.profile.isBlocked)
await client.profiles.block(this.userId);
await client.profiles.block(this.user_id);
else
await client.profiles.deblock(this.userId);
await client.profiles.deblock(this.user_id);
this.profile = await client.profiles.getProfile(this.username);
this.blockButton();
};
if (this.profile.isBlocked)
block.appendChild(document.createTextNode("Deblock"));
block.textContent = lang.get('profileUnblock', 'Unblock');
else
block.appendChild(document.createTextNode("Block"));
this.info.appendChild(block);
block.textContent = lang.get('profileBlock', 'Block');
}
}
async friendButton() {
if (await client.isAuthenticated() === false)
return;
if (client.me.id != this.user_id) {
let yes = document.getElementById("yes") || document.createElement("p");
let no = document.getElementById("no") || document.createElement("p");
let friend = document.getElementById("friend") || document.createElement("p");
if (client.notice.data["asker"].includes(this.user_id)) {
if (friend)
friend.remove();
yes.id = "yes";
yes.textContent = lang.get('profileAcceptRequest', 'Accept Friend');
yes.onclick = async () => {
client.notice.accept_friend(this.user_id);
}
no.id = "no";
no.textContent = lang.get('profileDenyRequest', 'Decline Friend');
no.onclick = async () => {
client.notice.refuse_friend(this.user_id);
}
this.info.appendChild(yes);
this.info.appendChild(document.createTextNode(" "));
this.info.appendChild(no);
}
else {
if (yes && no)
yes.remove(); no.remove();
friend.id = "friend"
friend.onclick = async () => {
if (this.profile.isFriend)
await client.notice.remove_friend(this.user_id);
else
await client.notice.ask_friend(this.user_id);
await client.profiles.getProfile(this.username);
this.friendButton();
};
if (this.profile.isFriend)
friend.textContent = lang.get('profileRemoveFriend', 'Remove Friend');
else {
friend.textContent = lang.get('profileAddFriend', 'Ask Friend');
}
this.info.appendChild(friend);
}
}
}

View File

@ -1,27 +1,27 @@
import AbstractView from "./abstracts/AbstractView.js";
import {client} from "../index.js";
import { client, lang } from "../index.js";
import {Message} from "../api/chat/message.js"
export default class extends AbstractView {
constructor(params) {
super(params, "Search");
super(params, 'SearchWindowTitle');
}
async wait_get_online_users() {
return new Promise((resolve) => {
const checkInterval = setInterval(() => {
//console.log(client.notice.data["online"].length);
if (client.notice.data["online"].length > 0) {
console.log(client.notice.data["online"]);
if (Object.keys(client.notice.data["online"]).length > 0) {
clearInterval(checkInterval);
resolve();
}
}, 100);
}, 1);
});
}
async postInit() {
let logged = await client.isAuthentificate();
let logged = await client.isAuthenticated();
let profiles = await client.profiles.all();
//console.log(client.notice.data);
@ -29,11 +29,13 @@ export default class extends AbstractView {
return console.log("Error");
//await client.notice.getOnlineUser();
await this.wait_get_online_users();
//await this.wait_get_online_users();
client.notice.rewrite_usernames = this.rewrite_usernames;
client.notice.rewrite_invite = this.display_invite;
let search = document.getElementById("input_user");
search.oninput = () => this.display_users(logged, profiles);
if (search != undefined)
search.oninput = () => this.display_users(logged, profiles);
let chat_input = document.getElementById("input_chat");
//chat_input.addEventListener("keydown", this.display_chat_manager)
@ -66,7 +68,12 @@ export default class extends AbstractView {
username.setAttribute('data-link', '');
username.id = `username${user.id}`
username.href = `/profiles/${user.username}`;
username.style.color = client.notice.data["online"].includes(user.id.toString()) ? "green" : "red";
if (logged && user.id == client.me.id)
username.style.color = "green";
else {
let online = client.notice.data["online"][user.id];
username.style.color = online !== undefined ? online : "gray";
}
username.appendChild(document.createTextNode(user.username));
new_user.appendChild(username);
@ -135,8 +142,14 @@ export default class extends AbstractView {
profiles.filter(user => user.username.toLowerCase().startsWith(search) == true).forEach((user) => {
let username = document.getElementById(`username${user.id}`);
if (username !== null)
username.style.color = client.notice.data["online"].includes(user.id.toString()) ? "green" : "red";
if (username !== null) {
if (user.id == client.me.id)
username.style.color = "green";
else {
let online = client.notice.data["online"][user.id];
username.style.color = online !== undefined ? online : "gray";
}
}
});
}
@ -175,13 +188,15 @@ export default class extends AbstractView {
chat_input.maxLength=255;
chat.appendChild(chat_input);
let members_id = client.channels.channel.members_id;
chat_input.onkeydown = async () => {
if (event.keyCode == 13 && client.channels.channel != undefined) {
//let chat_input = document.getElementById("input_chat");
let chat_text = chat_input.value;
let receivers_id = [];
client.channels.channel.members_id.forEach((member_id) => {
members_id.forEach((member_id) => {
if (member_id != client.me.id)
receivers_id.push(profiles.filter(user => user.id == member_id)[0].id);
});
@ -196,7 +211,7 @@ export default class extends AbstractView {
// Scroll to the bottom of messages
messages.scrollTop = messages.scrollHeight;
this.display_invite(chat);
this.display_invite();
}
@ -242,33 +257,86 @@ export default class extends AbstractView {
}
async display_members(chat, profiles) {
let members_id = client.channels.channel.members_id;
let members = document.createElement("h2");
members.id = "members";
let usernames = "";
client.channels.channel.members_id.forEach((member_id) => {
members_id.forEach((member_id) => {
if (member_id != client.me.id) {
if (usernames.length > 0)
usernames += ", ";
usernames += (profiles.filter(user => user.id == member_id)[0].username);
}
});
members.appendChild(document.createTextNode(usernames));
members.textContent = usernames;
chat.appendChild(members);
return members
}
async display_invite(chat, profiles) {
async display_invite() {
let chat = document.getElementById("chat");
if (chat == undefined)
return ;
let members_id = client.channels.channel.members_id;
let others = members_id.filter(id => id !== client.me.id);
// Button to send invite to play
let invite = document.getElementById("invite") || document.createElement("button");
invite.id = "invite";
invite.innerText = "invite";
invite.onclick = async () => {
await client.notice.sendInvite(client.me.id,
client.channels.channel.members_id.filter(id => id !== client.me.id));
};
chat.appendChild(invite);
let yes = document.getElementById("yes") || document.createElement("button");
let no = document.getElementById("no") || document.createElement("button");
let invitedBy = undefined;
for (let x in others) {
if (client.notice.data["invited"].includes(others[x])) {
invitedBy = others[x];
}
}
if (invitedBy == undefined) {
if (yes && no) {
yes.remove();
no.remove();
}
// Button to send invite to play
invite.id = "invite";
invite.style.background = "orange";
invite.innerText = "invite";
invite.title = "Invite to play a game"
invite.onclick = async () => {
await client.notice.send_invite(others);
};
chat.appendChild(invite);
}
else {
if (invite)
invite.remove()
yes.id = "yes";
yes.style.background = "green";
yes.title = "Accept to play a game"
yes.onclick = async () => {
await client.notice.accept_invite(invitedBy);
};
no.id = "no";
no.style.background = "red";
no.title = "Refuse to play a game"
no.onclick = async () => {
await client.notice.refuse_invite(invitedBy);
};
chat.appendChild(yes);
chat.appendChild(no);
}
}

View File

@ -1,12 +1,13 @@
import { client, navigateTo } from "../index.js";
import { clear, fill_errors } from "../utils/formUtils.js";
import AbstractAuthentificateView from "./abstracts/AbstractAuthentifiedView.js";
import AbstractAuthenticatedView from "./abstracts/AbstractAuthenticatedView.js";
export default class extends AbstractAuthentificateView
export default class extends AbstractAuthenticatedView
{
constructor(params)
{
super(params, "Me");
super(params, "Settings");
this.PROFILE_PICTURE_MAX_SIZE = 2 * 1024 * 1024; // 2MB
}
async postInit()
@ -24,7 +25,7 @@ export default class extends AbstractAuthentificateView
document.getElementById("avatar").remove();
let avatar = document.createElement("img");
avatar.id = "avatar";
avatar.src = profile.avatar_url;
avatar.src = profile.avatar_url + '?t=' +new Date().getTime();
document.getElementsByClassName("avatar")[0].appendChild(avatar);
}
}
@ -35,7 +36,7 @@ export default class extends AbstractAuthentificateView
let response_data = await client.account.delete(current_password);
console.log(await client.isAuthentificate())
console.log(await client.isAuthenticated())
if (response_data === null || response_data === "user deleted")
{
navigateTo("/login");
@ -78,18 +79,24 @@ export default class extends AbstractAuthentificateView
if (avatar.files[0] !== undefined)
{
if (avatar.files[0].size > this.PROFILE_PICTURE_MAX_SIZE) {
document.getElementById("save-profile").classList.add('text-danger');
document.getElementById("save-profile").innerHTML = "Image too large :/";
return;
}
let form_data = new FormData();
form_data.append("file", avatar.files[0]);
form_data.append("avatar", avatar.files[0]);
await client.me.change_avatar(form_data);
this.display_avatar();
}
document.getElementById("save-profile").classList.remove('text-danger');
document.getElementById("save-profile").innerHTML = "Saved";
}
async getHtml()
{
return /* HTML */ `
<link rel="stylesheet" href="/static/css/me.css">
<link rel="stylesheet" href="/static/css/settings.css">
<h1>ME</h1>
<div id="main">
<div class="avatar">

View File

@ -2,13 +2,13 @@ import { client, navigateTo } from "../../index.js";
import AbstractRedirectView from "./AbstractRedirectView.js";
export default class extends AbstractRedirectView{
constructor(params, title) {
super(params, title, "/login");
constructor(params, titleKey, uri = "/login") {
super(params, titleKey, uri);
}
async redirect()
{
if (await client.isAuthentificate() === false)
if (await client.isAuthenticated() === false)
{
navigateTo(this.redirect_url);
return 1;

View File

@ -2,13 +2,13 @@ import { client, navigateTo } from "../../index.js";
import AbstractRedirectView from "./AbstractRedirectView.js";
export default class extends AbstractRedirectView{
constructor(params, title, url) {
super(params, title, url);
constructor(params, titleKey, uri = "/home") {
super(params, titleKey, uri);
}
async redirect()
{
if (await client.isAuthentificate() === false)
if (await client.isAuthenticated() === false)
return 0;
navigateTo(this.redirect_url);
return 1;

View File

@ -2,14 +2,14 @@ import { navigateTo } from "../../index.js";
import AbstractView from "./AbstractView.js";
export default class extends AbstractView{
constructor(params, title, url)
constructor(params, titleKey, uri)
{
super(params, title);
this.redirect_url = url;
super(params, titleKey);
this.redirect_url = uri;
}
async redirect()
{
navigateTo(url);
navigateTo(this.redirect_url);
}
}

View File

@ -1,7 +1,9 @@
import {lang} from '../../index.js'
export default class {
constructor(params, title) {
constructor(params, titleKey) {
this.params = params;
this.title = title;
this.titleKey = titleKey;
}
async postInit() {
@ -11,7 +13,7 @@ export default class {
}
setTitle() {
document.title = this.title;
document.title = lang.get(this.titleKey, 'Bozo Pong');
}
async getHtml() {

View File

@ -0,0 +1,177 @@
import { client, lang, navigateTo } from "../../index.js";
import { clear, fill_errors } from "../../utils/formUtils.js";
import AbstractNonAuthenticatedView from "../abstracts/AbstractNonAuthenticatedView.js";
export default class extends AbstractNonAuthenticatedView
{
constructor(params, lastUrlBeforeLogin = '/home')
{
super(params, 'loginWindowTitle', lastUrlBeforeLogin);
this.redirect_url = lastUrlBeforeLogin;
this.current_mode = undefined
}
async leavePage()
{
this.current_mode = undefined;
}
/**
* @returns {Promise}
*/
async postInit()
{
let element = document.getElementById("toggle-register-login");
element.onclick = this.toggle_register_login.bind(this);
let new_mode = location.pathname.slice(1);
this.update_mode(new_mode);
document.getElementById("button").onclick = this.authentication.bind(this);
let username_input = document.getElementById('username-input'),
password_input = document.getElementById('password-input');
[username_input, password_input].forEach(input => {
input.addEventListener('keydown', async ev => {
if (ev.key === 'Enter')
await this.authentication.bind(this)()
});
});
username_input.focus();
}
/**
* Check if field is normal
* @param username {String}
* @param password {String}
* @returns {Boolean}
*/
basic_verif(username, password)
{
if (username === '')
document.getElementById('username').innerHTML = lang.get('errorEmptyField', 'This field may not be blank.');
if (password === '')
document.getElementById('password').innerHTML = lang.get('errorEmptyField', 'This field may not be blank.');
if (username === '' || password === '')
return false;
return true
}
/**
* @returns { undefined }
*/
toggle_register_login(event)
{
event.preventDefault();
let new_mode = this.current_mode === "register" ? "login" : "register";
this.update_mode(new_mode);
}
/**
* @param {String} new_mode
*/
update_mode(new_mode)
{
if (new_mode === this.current_mode)
return;
this.current_mode = new_mode;
let title = document.getElementById("title"),
username_label = document.getElementById("username-label"),
password_label = document.getElementById("password-label"),
toggle_register_login = document.getElementById("toggle-register-login"),
toggle_register_login_label = document.getElementById("toggle-register-login-label"),
button = document.getElementById("button")
;
let title_text = this.current_mode === "register" ? "registerFormTitle" : "loginFormTitle";
title.innerText = lang.get(title_text, "ERROR LANG");
let username_label_text = this.current_mode === "register" ? "registerFormUsername" : "loginFormUsername";
username_label.innerText = lang.get(username_label_text, "ERROR LANG");
let password_label_text = this.current_mode === "register" ? "registerFormPassword" : "loginFormPassword";
password_label.innerText = lang.get(password_label_text, "ERROR LANG");
let toggle_register_login_label_text = this.current_mode === "register" ? "registerAlreadyAccount" : "loginNoAccount";
toggle_register_login_label.innerText = lang.get(toggle_register_login_label_text, "ERROR LANG");;
let toggle_register_login_text = this.current_mode === "register" ? "registerLogin" : "loginRegister";
toggle_register_login.innerText = lang.get(toggle_register_login_text, "ERROR LANG");
let button_text = this.current_mode === "register" ? "registerFormButton" : "loginFormButton";
button.innerText = lang.get(button_text, "ERROR LANG");
this.titleKey = this.current_mode === 'register' ? 'registerWindowTitle' : 'loginWindowTitle';
this.setTitle();
}
/**
* @returns {Promise}
*/
async authentication()
{
let username = document.getElementById("username-input").value,
password = document.getElementById("password-input").value;
if (!this.basic_verif())
return;
let response;
if (this.current_mode === "register")
response = await client.account.create(username, password)
else
response = await client.login(username, password);
if (response.status === 200 || response.status === 201)
{
navigateTo(this.redirect_url);
return;
}
let response_data = await response.json()
console.log(response_data);
clear("innerHTML", ["username", "password", 'login']);
fill_errors(response_data, "innerHTML");
}
async getHtml()
{
return /* HTML */ `
<div class='container-fluid'>
<form class='border border-2 rounded bg-light-subtle mx-auto p-2 col-md-7 col-lg-4'>
<h4 class='text-center fw-semibold mb-4' id="title">Loading...</h4>
<div class='form-floating mb-2'>
<input type='text' class='form-control' id='username-input' placeholder='Username'>
<label for='usernameInput' id='username-label'>Loading...</label>
<span class='text-danger' id='username'></span>
</div>
<div class='form-floating'>
<input type='password' class='form-control' id='password-input' placeholder='Password'>
<label for='password-input' id='password-label'>Loading...</label>
<span class='text-danger' id='password'></span>
</div>
<div class='d-flex'>
<button type='button' class='btn btn-primary mt-3 mb-2' id='button'>Loading...</button>
<span class='text-danger my-auto mx-2' id='login'></span>
<div class='ms-auto mt-auto flex-row d-flex gap-2' id="toggle">
<p id='toggle-register-login-label'>Loading...</p>
<a id="toggle-register-login" href='#'>Loading...</a>
</div>
</div>
</form>
</div>
`;
}
}

View File

@ -1,79 +0,0 @@
import { client, navigateTo } from "../../index.js";
import { clear, fill_errors } from "../../utils/formUtils.js";
import AbstractNonAuthentifiedView from "../abstracts/AbstractNonAuthentified.js";
async function login(redirectTo = '/home')
{
clear('innerHTML', ['username', 'password', 'login']);
let username = document.getElementById('usernameInput').value;
let password = document.getElementById('passwordInput').value;
if (username === '') {
document.getElementById('username').innerHTML = 'This field may not be blank.';
}
if (password === '') {
document.getElementById('password').innerHTML = 'This field may not be blank.';
}
if (username === '' || password === '')
return;
let response = await client.login(username, password);
if (response.status == 200) {
await client.notice.disconnect();
await client.notice.connect();
navigateTo(redirectTo);
} else {
let error = await response.json();
fill_errors(error, "innerHTML");
}
}
export default class extends AbstractNonAuthentifiedView {
constructor(params, lastUrlBeforeLogin = '/home') {
super(params, "Login", lastUrlBeforeLogin);
this.redirectTo = lastUrlBeforeLogin;
}
async postInit()
{
let usernameField = document.getElementById('usernameInput');
usernameField.addEventListener('keydown', ev => {
if (ev.key === 'Enter')
login(this.redirectTo);
});
usernameField.focus();
let passwordField = document.getElementById('passwordInput');
passwordField.addEventListener('keydown', ev => {
if (ev.key === 'Enter')
login(this.redirectTo);
});
document.getElementById('loginButton').onclick = _ => login(this.redirectTo);
}
async getHtml() {
return `
<div class='container-fluid'>
<form class='border border-2 rounded bg-light-subtle mx-auto p-2 col-md-7 col-lg-4'>
<h4 class='text-center fw-semibold mb-4'>Login</h4>
<div class='form-floating mb-2'>
<input type='text' class='form-control' id='usernameInput' placeholder='Username'>
<label for='usernameInput'>Username</label>
<span class='text-danger' id='username'></span>
</div>
<div class='form-floating'>
<input type='password' class='form-control' id='passwordInput' placeholder='Password'>
<label for='passwordInput'>Password</label>
<span class='text-danger' id='password'></span>
</div>
<div class='d-flex'>
<button type='button' class='btn btn-primary mt-3 mb-2' id='loginButton'>Login</button>
<span class='text-danger my-auto mx-2' id='login'></span>
<p class='ms-auto mt-auto'>No account yet? <a href='/register' data-link>Register</a></p>
</div>
</form>
</div>
`;
}
}

View File

@ -1,10 +1,10 @@
import { client, navigateTo } from "../../index.js";
import AbstractAuthentifiedView from "../abstracts/AbstractAuthentifiedView.js";
import AbstractAuthenticatedView from "../abstracts/AbstractAuthenticatedView.js";
export default class extends AbstractAuthentifiedView
export default class extends AbstractAuthenticatedView
{
constructor(params, lastPageUrl = '/login') {
super(params, "Logout");
super(params, 'logoutWindowTitle', lastPageUrl);
this.lastPageUrl = lastPageUrl;
}

View File

@ -1,77 +0,0 @@
import { client, navigateTo } from "../../index.js";
import { clear, fill_errors } from "../../utils/formUtils.js";
import AbstractNonAuthentifiedView from "../abstracts/AbstractNonAuthentified.js";
async function register(redirectTo = '/home')
{
let username = document.getElementById("usernameInput").value;
let password = document.getElementById("passwordInput").value;
if (username === '' || password === '') {
clear("innerHTML", ["username", "password"]);
if (username === '')
document.getElementById('username').innerHTML = 'This field may not be blank.';
if (password === '')
document.getElementById('password').innerHTML = 'This field may not be blank.';
return;
}
let response_data = await client.account.create(username, password);
if (response_data == null)
{
navigateTo(redirectTo);
return;
}
clear("innerHTML", ["username", "password", 'register']);
fill_errors(response_data, "innerHTML");
}
export default class extends AbstractNonAuthentifiedView {
constructor(params, lastUrlBeforeLogin = '/home') {
super(params, "Register", lastUrlBeforeLogin);
this.redirectTo = lastUrlBeforeLogin;
}
async postInit()
{
let usernameField = document.getElementById('usernameInput');
usernameField.addEventListener('keydown', ev => {
if (ev.key === 'Enter')
register(this.redirectTo);
});
usernameField.focus();
let passwordField = document.getElementById('passwordInput');
passwordField.addEventListener('keydown', ev => {
if (ev.key === 'Enter')
register(this.redirectTo);
});
document.getElementById("registerButton").onclick = _ => register(this.redirectTo);
}
async getHtml() {
return `
<div class='container-fluid'>
<form class='border border-2 rounded bg-light-subtle mx-auto p-2 col-md-7 col-lg-4'>
<h4 class='text-center fw-semibold mb-4'>Register</h4>
<div class='form-floating mb-2'>
<input type='text' class='form-control' id='usernameInput' placeholder='Username'>
<label for='usernameInput'>Username</label>
<span class='text-danger' id='username'></span>
</div>
<div class='form-floating'>
<input type='password' class='form-control' id='passwordInput' placeholder='Password'>
<label for='passwordInput'>Password</label>
<span class='text-danger' id='password'></span>
</div>
<div class='d-flex'>
<button type='button' class='btn btn-primary mt-3 mb-2' id='registerButton'>Register</button>
<span class='text-danger my-auto mx-2' id='register'></span>
<p class='ms-auto mt-auto'>Already have an account? <a href='/login' data-link>Login</a></p>
</div>
</form>
</div>
`;
}
}

View File

@ -1,8 +1,8 @@
import {client, navigateTo} from "../../index.js";
import { clear, fill_errors } from "../../utils/formUtils.js";
import AbstractAuthentifiedView from "../abstracts/AbstractAuthentifiedView.js";
import AbstractAuthenticatedView from "../abstracts/AbstractAuthenticatedView.js";
export default class extends AbstractAuthentifiedView
export default class extends AbstractAuthenticatedView
{
constructor(params)
{

View File

@ -1,7 +1,7 @@
import {client, navigateTo} from "../../index.js";
import AbstractAuthentifiedView from "../abstracts/AbstractAuthentifiedView.js";
import AbstractAuthenticatedView from "../abstracts/AbstractAuthenticatedView.js";
export default class extends AbstractAuthentifiedView
export default class extends AbstractAuthenticatedView
{
constructor(params)
{

View File

@ -1,7 +1,7 @@
import {client} from "../../index.js";
import AbstractAuthentifiedView from "../abstracts/AbstractAuthentifiedView.js";
import AbstractAuthenticatedView from "../abstracts/AbstractAuthenticatedView.js";
export default class extends AbstractAuthentifiedView
export default class extends AbstractAuthenticatedView
{
constructor(params)
{