This commit is contained in:
2024-03-12 10:29:25 +01:00
33 changed files with 433 additions and 309 deletions

View File

@ -1,11 +1,3 @@
#app #avatar {
max-height: 10em;
max-width: 10em;
min-height: 6em;
min-width: 6em;
}
#popup {
position: fixed;
font-size: 1.2em;

View File

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

View File

@ -48,41 +48,24 @@ class Account
}
/**
* Get account data (username)
* @returns {?Promise<Object>}
*/
async get()
* @param {String} newUsername
* @returns {?Promise<Object>}
*/
async updateUsername(newUsername)
{
let response = await this.client._get("/api/accounts/edit");
let response_data = await response.json();
const data = {
username: newUsername
};
const response = await this.client._patch_json(`/api/accounts/update_profile`, data);
const respondeData = await response.json();
if (response.status === 403)
{
this.client._update_logged(false);
if (response.status === 200) {
this.client.me.username = respondeData.username;
document.getElementById('navbarDropdownButton').innerHTML = respondeData.username;
document.getElementById('myProfileLink').href = '/profiles/' + respondeData.username;
return null;
}
return response_data;
}
/**
*
* @param {*} data
* @param {Number} password
* @returns {?Object}
*/
async update(data, password)
{
data.current_password = password;
let response = await this.client._patch_json("/api/accounts/edit", data);
let response_data = await response.json();
if (response.status === 403)
{
this.client._update_logged(false);
return null;
}
return response_data;
return respondeData['authorize'] || respondeData['detail'] || respondeData['username']?.join(' ') || 'Error.';
}
}

View File

@ -3,7 +3,6 @@ import { MatchMaking } from "./Matchmaking.js";
import { Profiles } from "./Profiles.js";
import { Channels } from './chat/Channels.js';
import { MyProfile } from "./MyProfile.js";
import { navigateTo } from "../index.js";
import { Tourmanents } from "./tournament/Tournaments.js";
import { Notice } from "./chat/Notice.js";
import { Channel } from "./chat/Channel.js";
@ -97,6 +96,9 @@ class Client
{
let response = await fetch(this._url + uri, {
method: "GET",
headers: {
'Accept-Language': this.lang.currentLang
},
body: JSON.stringify(data),
});
return response;
@ -135,7 +137,8 @@ class Client
headers: {
"Content-Type": "application/json",
"X-CSRFToken": getCookie("csrftoken"),
},
'Accept-Language': this.lang.currentLang,
},
body: JSON.stringify(data),
});
return response;
@ -154,7 +157,8 @@ class Client
headers: {
"X-CSRFToken": getCookie("csrftoken"),
"Content-Type": "application/json",
},
'Accept-Language': this.lang.currentLang,
},
body: JSON.stringify(data),
});
return response;
@ -172,7 +176,8 @@ class Client
method: "PATCH",
headers: {
"X-CSRFToken": getCookie("csrftoken"),
},
'Accept-Language': this.lang.currentLang,
},
body: file,
});
return response;

View File

@ -14,15 +14,35 @@ class MyProfile extends Profile
/**
*
* @param {*} form_data
* @returns {Promise<Object>}
* @param {File} selectedFile
* @returns {Promise<Response>}
*/
async change_avatar(form_data)
async changeAvatar(selectedFile)
{
let response = await this.client._patch_file(`/api/profiles/settings`, form_data);
let response_data = await response.json();
const formData = new FormData();
formData.append('avatar', selectedFile);
return response_data;
const response = await this.client._patch_file(`/api/profiles/settings`, formData);
const responseData = await response.json();
if (response.ok) {
console.log('save', responseData);
this.avatar_url = responseData.avatar.substr(responseData.avatar.indexOf('static') - 1);
return null;
}
return responseData;
}
async deleteAvatar() {
const response = await this.client._delete('/api/profiles/settings');
const responseData = await response.json();
if (response.ok) {
console.log('delete', responseData);
this.avatar_url = responseData.avatar.substr(responseData.avatar.indexOf('static') - 1);
return null;
}
return responseData;
}
}

View File

@ -79,7 +79,7 @@ class Tourmanent
*/
async init()
{
let response = await this.client._get(`/api/tournaments/${id}`);
let response = await this.client._get(`/api/tournaments/${this.id}`);
if (response.status !== 200)
return response.status;

View File

@ -17,12 +17,12 @@ class Tourmanents
/**
*
* @param {Number} id
* @returns {?Promise<Tournament>}
* @returns {Promise<Tournament>}
*/
async getTournament(id)
{
let tournament = new Tourmanent(this.client);
if (await tournament.init(id))
let tournament = new Tourmanent(this.client, id);
if (await tournament.init())
return null;
return tournament;
}
@ -32,17 +32,13 @@ class Tourmanents
* @param {Number} nb_players
* @param {Number} nb_players_by_game
* @param {String} name
* @returns
* @returns {Response}
*/
async createTournament(nb_players, nb_players_by_game, name = "")
{
let response = await this.client._post("/api/tournaments/", {nb_players: nb_players, nb_players_by_game: nb_players_by_game, name: name});
if (response.status !== 200)
return response.status;
let response_data = await response.json();
return response_data;
return response;
}
/**
@ -71,7 +67,8 @@ class Tourmanents
tournament_data.started,
tournament_data.finished,
tournament_data.levels,
tournament_data.id));
tournament_data.id,
tournament_data.state));
});
return tournaments;

View File

@ -15,7 +15,7 @@ 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 TournamentsListView from "./views/tournament/TournamentsListView.js";
import TournamentCreateView from "./views/tournament/TournamentCreateView.js";
import AuthenticationView from "./views/accounts/AuthenticationView.js";
import TicTacToeView from "./views/TicTacToeView.js";
@ -82,7 +82,7 @@ const router = async(uri) => {
{ path: "/profiles/:username", view: ProfilePageView },
{ path: "/tournaments/create", view: TournamentCreateView },
{ path: "/tournaments/:id", view: TournamentPageView },
{ path: "/tournaments/", view: TournamentsView },
{ path: "/tournaments/", view: TournamentsListView },
{ path: "/login", view: AuthenticationView },
{ path: "/register", view: AuthenticationView },
{ path: "/logout", view: LogoutView },
@ -155,7 +155,6 @@ document.addEventListener("DOMContentLoaded", async () => {
el.onclick = async _ => {
if (await lang.changeLanguage(el.value))
return;
console.log(lang);
document.querySelector('#languageSelector > .active')?.classList.remove('active');
el.classList.add('active');
};

View File

@ -42,5 +42,14 @@
"ruleTitle" : "Règles cramptés",
"ruleBase" : "cramptun. Vous devez quouicougagner sur une des 9 quoicougrilles pour gagner la croustipartie",
"ruleMovement" : "quoicoudeux. Vous quoicommencez sur le morpion quoicoucentral, et jouez sur le quoicoumorpion correspondant a votre croustichoix a votre prochain cramptour",
"ruleDraw" : "cramptrois. Si votre quoicouchoix rempli entièrement un quoicoumorpion et provoque une cramptégalité, vous perdez"
"ruleDraw" : "cramptrois. Si votre quoicouchoix rempli entièrement un quoicoumorpion et provoque une cramptégalité, vous perdez",
"matchmakingTitle": "Matchmaking crampté",
"matchmakingStartSearch": "Cramptrouver une partie",
"matchmakingStopSearch": "Crampter le matchmaking",
"matchmakingNbPlayers": "Nombre de crampteurs",
"TournamentCreateTitle": "Créer un cramptournoi",
"TournamentCreateButton": "Créer le cramptournoi",
"TournamentCreateTournamentName": "Nom du cramptournoi",
"TournamentCreateNbPlayerByGame": "Nombre de crampteurs en crampté",
"TournamentCreateNbPlayer": "Nombre de crampteurs dans le cramptournoi"
}

View File

@ -42,5 +42,14 @@
"ruleTitle" : "Rules",
"ruleBase" : "1. Win on one of the 9 tictactoe to win the game",
"ruleMovement" : "2. You start on the central tictactoe, and play on the one corresponding to your choice on the next turn",
"ruleDraw" : "3. If your play cause a tictactoe to be full and a draw, you lose the game"
"ruleDraw" : "3. If your play cause a tictactoe to be full and a draw, you lose the game",
"matchmakingTitle": "Matchmaking",
"matchmakingStartSearch": "Find a game",
"matchmakingStopSearch": "Stop matchmaking",
"matchmakingNbPlayers": "Number of players",
"TournamentCreateTitle": "Create tournament",
"TournamentCreateButton": "Create tournament",
"TournamentCreateTournamentName": "Tournament Name",
"TournamentCreateNbPlayerByGame": "Number of player in a game",
"TournamentCreateNbPlayer": "Number of players in the tournament"
}

View File

@ -42,5 +42,14 @@
"ruleTitle" : "Règles",
"ruleBase" : "1. Vous devez gagner sur une des 9 grilles pour gagner la partie",
"ruleMovement" : "2. Vous commencez sur le morpion central, et jouez sur le morpion correspondant a votre choix a votre prochain tour",
"ruleDraw" : "3. Si votre choix rempli entièrement un morpion et provoque une égalité, vous perdez"
"ruleDraw" : "3. Si votre choix rempli entièrement un morpion et provoque une égalité, vous perdez",
"matchmakingTitle": "Matchmaking",
"matchmakingStartSearch": "Trouver une partie",
"matchmakingStopSearch": "Arrêter le matchmaking",
"matchmakingNbPlayers": "Nombre de joueurs",
"TournamentCreateTitle": "Créer un tournoi",
"TournamentCreateButton": "Créer le tournoi",
"TournamentCreateTournamentName": "Nom du tournoi",
"TournamentCreateNbPlayerByGame": "Nombre de joueurs en jeu",
"TournamentCreateNbPlayer": "Nombre de joueurs dans le tournoi"
}

View File

@ -40,6 +40,15 @@
"ruleTitle" : "Rules",
"ruleBase" : "1. Win on wan pi the 9 tictactoe tawa win the game",
"ruleMovement" : "2. Sina open on the central tictactoe, en play on the wan corresponding tawa your choice on the next turn",
"ruleDraw" : "3. If your play cause a tictactoe tawa be full en a draw, sina lose the game"
"ruleDraw" : "3. If your play cause a tictactoe tawa be full en a draw, sina lose the game",
"matchmakingTitle": "Matchmaking",
"matchmakingStartSearch": "lukin e ilo musi",
"matchmakingStopSearch": "o pini e pana sona e jan pi pana sona e jan ante.",
"matchmakingNbPlayers": "nanpa pi jan ante",
"TournamentCreateTitle": "o pana e musi ante e musi",
"TournamentCreateButton": "jo ala pona li jo e ijo li pali e ijo li pana e ijo li toki e ijo li kama jo e ijo li kama pali e ijo li kama pana e ijo li kama toki e ijo li kama jo e ijo li kama pali e ijo li kama pana e ijo",
"TournamentCreateTournamentName": "ilo kipisi",
"TournamentCreateNbPlayerByGame": "ilo jan lon poki pi lon anpa en sike pimeja li kama.",
"TournamentCreateNbPlayer": "nanpa pi jan pona lon soweli musi"
}

View File

@ -12,7 +12,7 @@ export default class extends AbstractView {
Akel is a game engine designed to be easy to use. The purpose of the project is learning about game engine development, discovering new rendering processes and learning to use new tools. It is mainly coded on and for Linux but is cross-platform and has been tested on Windows and MacOS.
</p>
<p>
<a href="https://akel-engine.com" data-link>Akel Engine</a>.
<a href="https://cdn.discordapp.com/attachments/1198285289236463699/1211450599007064074/malonerd.png?ex=6600b34a&is=65ee3e4a&hm=359877a4259663411dc24383562193e0e8862774022ca9989b6960f6628f1e2c&">Akel Engine</a>.
<a href="/posts" data-link>View recent posts</a>.
</p>
`;

View File

@ -1,4 +1,4 @@
import { client, navigateTo } from "../index.js";
import { client, lang, navigateTo } from "../index.js";
import { clear, fill_errors } from "../utils/formUtils.js";
import AbstractAuthenticatedView from "./abstracts/AbstractAuthenticatedView.js";
@ -7,56 +7,36 @@ export default class extends AbstractAuthenticatedView {
constructor(params)
{
super(params, "Matchmaking");
this.game_mode = 0; // 0 -> 2D; 1 -> 3D
}
async press_button()
async toggle_search()
{
clear("innerText", ["detail"]);
if (client.matchmaking.searching)
{
client.matchmaking.stop();
document.getElementById("button").value = "Find a game";
this.button.innerHTML = lang.get("matchmakingStartSearch");
}
else
{
let nb_players = document.getElementById("nb_players-input").value;
let nb_players = this.input.value;
await client.matchmaking.start(this.onreceive.bind(this), this.ondisconnect.bind(this), nb_players);
document.getElementById("button").value = "Stop matchmaking";
this.button.innerHTML = lang.get("matchmakingStopSearch");
}
}
async press_button_game_mode()
{
if(this.game_mode === 0)
{
document.getElementById("game-mode").value = "3D";
this.game_mode = 1;
}
else
{
document.getElementById("game-mode").value = "2D";
this.game_mode = 0;
}
}
ondisconnect(event)
{
let button = document.getElementById("button")
if (button === null)
return
button.value = "Find a game";
this.button.innerHTML = lang.get("matchmakingStartSearch");
}
onreceive(data)
{
if (data.detail === "game_found")
{
navigateTo(`/games/${data.game_id}/${this.game_mode}`);
navigateTo(`/games/${data.game_id}`);
return;
}
this.display_data(data);
@ -70,46 +50,44 @@ export default class extends AbstractAuthenticatedView {
async postInit()
{
let button = document.getElementById("button");
this.button = document.getElementById("toggle-search");
this.input = document.getElementById("nb-players-input");
button.onclick = this.press_button.bind(this);
this.button.onclick = this.toggle_search.bind(this);
let input = document.getElementById("nb_players-input");
this.input.addEventListener('keydown', async ev => {
input.addEventListener('keydown', async ev => {
if (ev.key !== 'Enter')
return;
if (client.matchmaking.searching)
client.matchmaking.stop();
let nb_players = document.getElementById("nb_players-input").value;
await client.matchmaking.start(this.onreceive.bind(this), this.ondisconnect.bind(this), nb_players);
document.getElementById("button").value = "Stop matchmaking";
await this.toggle_search.bind(this);
});
let update = () => {
if (input.value < 2 || input.value > 4)
button.disabled = true;
else
button.disabled = false;
this.button.disabled = (this.input.value < 2 || this.input.value > 4);
};
["change", "oninput"].forEach((event_name) => {
input.addEventListener(event_name, update);
this.input.addEventListener(event_name, update);
});
document.getElementById("game-mode").onclick = this.press_button_game_mode.bind(this);
}
async getHtml() {
return `
<h1>Select mode</h1>
<input type="number" value="2" min="1" max="4" id="nb_players-input">
<input type="button" value="Find a game" id="button">
<input type="button" value="2D" id="game-mode">
<span id="detail"></span>
return /* HTML */ `
<div class='container-fluid'>
<div 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">${lang.get("matchmakingTitle")}</h4>
<div class='form-floating mb-2'>
<input type='number' min='2' value='2' max='4' class='form-control' id='nb-players-input' placeholder='${lang.get("matchmakingNbPlayers")}'>
<label for='nb-players-input' id='username-label'>${lang.get("matchmakingNbPlayers")}</label>
<span class='text-danger' id='username'></span>
</div>
<div class='d-flex'>
<button type='button' class='btn btn-primary mt-3 mb-2 mx-2' id='toggle-search'>${lang.get("matchmakingStartSearch")}</button>
<span class='text-danger my-auto mx-2' id='detail'></span>
</div>
</div>
</div>
`;
}

View File

@ -1,55 +1,82 @@
import { client, navigateTo } from "../index.js";
import { clear, fill_errors } from "../utils/formUtils.js";
import AbstractAuthenticatedView from "./abstracts/AbstractAuthenticatedView.js";
import { client, navigateTo } from '../index.js';
import { clear, fill_errors } from '../utils/formUtils.js';
import AbstractAuthenticatedView from './abstracts/AbstractAuthenticatedView.js';
export default class extends AbstractAuthenticatedView
{
constructor(params)
{
super(params, "Settings");
super(params, 'Settings');
this.PROFILE_PICTURE_MAX_SIZE = 2 * 1024 * 1024; // 2MB
}
async postInit()
{
this.display_avatar();
document.getElementById("save-account-button").onclick = () => this.save_account();
document.getElementById("delete-account-button").onclick = () => this.delete_account();
document.getElementById("save-profile-button").onclick = () => this.save_profile();
this.avatarInit();
this.usernameInit();
// document.getElementById('delete-account-button').onclick = () => this.delete_account();
}
async display_avatar() {
let profile = await client.profiles.getProfile(client.me.username);
if (profile !== undefined || profile !== null) {
if (document.getElementById("avatar") != undefined)
document.getElementById("avatar").remove();
let avatar = document.createElement("img");
avatar.id = "avatar";
avatar.src = profile.avatar_url + '?t=' +new Date().getTime();
document.getElementsByClassName("avatar")[0].appendChild(avatar);
usernameInit() {
const usernameInput = document.getElementById('usernameInput');
const usernameSave = document.getElementById('usernameSave');
usernameInput.oninput = e => {
const value = e.target.value;
if (value != client.me.username && value.length)
usernameSave.classList.remove('disabled');
else
usernameSave.classList.add('disabled');
}
usernameSave.onclick = _ => this.saveUsername();
}
avatarInit() {
const avatar = document.getElementById('avatar');
const avatarInput = document.getElementById('avatarInput');
const avatarUpload = document.getElementById('avatarUpload');
const avatarDelete = document.getElementById('avatarDelete');
avatar.onclick = _ => avatarInput.click();
avatarInput.onchange = function () {
const selectedFile = this.files[0];
if (!selectedFile)
return;
avatar.src = URL.createObjectURL(selectedFile);
avatarUpload.classList.remove('d-none');
}
avatarUpload.onclick = _ => this.saveAvatar();
avatarDelete.onclick = _ => this.deleteAvatar();
}
async displayAvatar() {
let avatar = document.getElementById('avatar');
avatar.src = client.me.avatar_url + '?t=' + new Date().getTime();
console.log(avatar.src);
}
async delete_account()
{
let current_password = document.getElementById("current_password-input").value;
let current_password = document.getElementById('current_password-input').value;
let response_data = await client.account.delete(current_password);
if (response_data === null || response_data === "user deleted")
if (response_data === null || response_data === 'user deleted')
{
navigateTo("/login");
navigateTo('/login');
return;
}
clear("innerHTML", ["current_password-input"]);
fill_errors({"current_password-input": response_data.password}, "innerHTML");
clear('innerHTML', ['current_password-input']);
fill_errors({'current_password-input': response_data.password}, 'innerHTML');
}
async save_account()
{
let username = document.getElementById("username-input").value;
let new_password = document.getElementById("new_password-input").value;
let current_password = document.getElementById("current_password-input").value;
let username = document.getElementById('username-input').value;
let new_password = document.getElementById('new_password-input').value;
let current_password = document.getElementById('current_password-input').value;
let data = {};
@ -61,67 +88,139 @@ export default class extends AbstractAuthenticatedView
if (response_data === null)
{
navigateTo("/login");
navigateTo('/login');
return;
}
if (response_data === "data has been alterate")
response_data = {"save-account": "saved"};
if (response_data === 'data has been alterate')
response_data = {'save-account': 'saved'};
clear("innerHTML", ["username", "new_password", "current_password", "save-account", "delete-account"]);
fill_errors(response_data, "innerHTML");
}
async save_profile()
{
let avatar = document.getElementById("avatar-input");
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("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";
clear('innerHTML', ['username', 'new_password', 'current_password', 'save-account', 'delete-account']);
fill_errors(response_data, 'innerHTML');
}
async getHtml()
{
return /* HTML */ `
<link rel="stylesheet" href="/static/css/settings.css">
<h1>ME</h1>
<div id="main">
<div class="avatar">
</div>
<div class="account">
<h3>Account</h3>
<input type="text" placeholder="username" id="username-input" text=${client.me.username}>
<span id="username"></span>
<input type=password placeholder="new_password" id="new_password-input">
<span id="new_password"></span>
<input type=password placeholder="current_password" id="current_password-input">
<span id="current_password"></span>
async saveUsername()
{
const usernameInput = document.getElementById('usernameInput');
const username = usernameInput.value;
const usernameDetail = document.getElementById('usernameDetail');
<input type="button" value="Save Credentials" id="save-account-button">
<span id="save-account"></span>
<input type="button" value="Delete Account" id="delete-account-button">
<span id="delete-account"></span>
</div>
<div class="profile">
<h3>Profile</h3>
<input type="file" id="avatar-input" accept="image/*">
<input type="button" value="Save profile" id="save-profile-button">
<span id="save-profile"></span>
</div>
<a href="/logout" class="nav__link" data-link>Logout</a>
</div>
`;
}
if (!username.length || username === client.me.username)
return;
const error = await client.account.updateUsername(username);
if (!error) {
usernameDetail.classList.remove('text-danger');
usernameDetail.classList.add('text-success');
usernameDetail.innerHTML = 'Username Saved.';
setTimeout(_ => usernameDetail.innerHTML = '', 2000);
document.getElementById('usernameSave').classList.add('disabled');
} else {
usernameDetail.classList.remove('text-success');
usernameDetail.classList.add('text-danger');
usernameDetail.innerHTML = error;
document.getElementById('usernameSave').classList.add('disabled');
console.log(error);
}
}
async saveAvatar()
{
const avatarInput = document.getElementById('avatarInput');
const selectedFile = avatarInput.files[0];
const avatarDetail = document.getElementById('avatarDetail');
if (!selectedFile)
return;
if (selectedFile.size > this.PROFILE_PICTURE_MAX_SIZE) {
avatarDetail.classList.remove('text-success');
avatarDetail.classList.add('text-danger');
avatarDetail.innerHTML = 'Image is too large.'; //to translate
return;
}
const error = await client.me.changeAvatar(selectedFile);
if (!error) {
avatarDetail.classList.remove('text-danger');
avatarDetail.classList.add('text-success');
avatarDetail.innerHTML = 'Avatar saved.'; //to translate
setTimeout(_ => avatarDetail.innerHTML = '', 2000);
document.getElementById('avatarDelete').classList.remove('d-none');
document.getElementById('avatarUpload').classList.add('d-none');
avatarInput.value = null;
} else {
avatarDetail.classList.remove('text-success');
avatarDetail.classList.add('text-danger');
avatarDetail.innerHTML = error.avatar[0];
document.getElementById('avatarUpload').classList.add('d-none');
avatarInput.value = null;
console.log(error);
}
this.displayAvatar();
}
async deleteAvatar() {
const avatarDetail = document.getElementById('avatarDetail');
const error = await client.me.deleteAvatar();
if (!error) {
avatarDetail.classList.remove('text-danger');
avatarDetail.classList.add('text-success');
avatarDetail.innerHTML = 'Avatar deleted.'; //to translate
setTimeout(_ => avatarDetail.innerHTML = '', 2000);
document.getElementById('avatarDelete').classList.add('d-none');
} else {
avatarDetail.classList.remove('text-success');
avatarDetail.classList.add('text-danger');
avatarDetail.innerHTML = 'Something went wrong.'; //to translate
}
this.displayAvatar();
}
async getHtml()
{
const avatarUnchanged = client.me.avatar_url === '/static/avatars/default.avif';
return /* HTML */ `
<div class='container col-sm-10 col-8 d-flex rounded border border-2 bg-light-subtle py-3'>
<div class='row col-4 bg-body-tertiary border rounded p-2 m-2 d-flex justify-content-center align-items-center'>
<h2 class='border-bottom'>Avatar</h2>
<img id='avatar' class='rounded p-0' src=${client.me.avatar_url} style='cursor: pointer;'>
<input id='avatarInput' class='d-none' type='file' accept='image/*'>
<div class='d-flex gap-2 mt-1 px-0'>
<span class='my-auto ms-1 me-auto' id='avatarDetail'></span>
<button class='btn btn-primary d-none' id='avatarUpload'>Save</button>
<button class='btn btn-danger${avatarUnchanged ? ' d-none' : ''}' id='avatarDelete'>Delete</button>
</div>
</div>
<div class='flex-grow-1'>
<h1 class='border-bottom ps-1 mb-3'>Account</h1>
<div>
<div class='input-group'>
<div class='form-floating'>
<input type='text' class='form-control' id='usernameInput' placeholder='username' value=${client.me.username}>
<label for='usernameInput'>Username</label>
</div>
<button class='input-group-text btn btn-success disabled' id='usernameSave'>Save</button>
</div>
<span class='form-text' id='usernameDetail'></span>
</div>
</div>
</div>
`;
// <input class='form-control' type='text' placeholder='Username' id='username-input' value=${client.me.username}>
// <h1>Settings</h1>
// <input class='form-control d-inline-block' type='text' placeholder='Username' id='username-input' value=${client.me.username}>
// <span id='username'></span>
// <input type=password placeholder='New Password' id='new_password-input'>
// <span id='new_password'></span>
// <input type=password placeholder='Current Password' id='current_password-input'>
// <span id='current_password'></span>
// <input type='button' value='Save Credentials' id='save-account-button'>
// <span id='save-account'></span>
//
// <input type='button' value='Delete Account' id='delete-account-button'>
// <span id='delete-account'></span>
}
}

View File

@ -1,4 +1,4 @@
import {client, navigateTo} from "../../index.js";
import {client, lang, navigateTo} from "../../index.js";
import { clear, fill_errors } from "../../utils/formUtils.js";
import AbstractAuthenticatedView from "../abstracts/AbstractAuthenticatedView.js";
@ -13,13 +13,11 @@ export default class extends AbstractAuthenticatedView
async create()
{
let name = document.getElementById("name-input").value;
let nb_players = document.getElementById("nb_players-input").value;
let nb_players_by_game = document.getElementById("nb_players_by_game-input").value;
let nb_players = document.getElementById("nb-players-input").value;
let nb_players_by_game = document.getElementById("nb-players-by-game-input").value;
let response_data = await client.tournaments.createTournament(nb_players, nb_players_by_game, name);
if (response_data === null)
return;
let response = await client.tournaments.createTournament(nb_players, nb_players_by_game, name);
let response_data = await response.json();
let id = response_data.id;
if (id !== undefined)
@ -37,16 +35,33 @@ export default class extends AbstractAuthenticatedView
document.getElementById("create-button").onclick = this.create;
}
async getHtml()
{
return `
<input type="text" id="name-input" placeholder="Tournament name">
<span id="name"></span>
<input type="number" id="nb_players-input" placeholder="Number of players in tournament">
<span id="nb_players"></span>
<input type="number" id="nb_players_by_game-input" placeholder="Number of players by game">
<span id="nb_players_by_game"></span>
<input type="button" id="create-button" value="Create tournament">
async getHtml() {
return /* HTML */ `
<div class='container-fluid'>
<div 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">${lang.get("TournamentCreateTitle")}</h4>
<div class='form-floating mb-2'>
<input type='text' class='form-control' id='name-input' placeholder='${lang.get("TournamentCreateTournamentName")}'>
<label for='name-input' id='name-label'>${lang.get("TournamentCreateTournamentName")}</label>
<span class='text-danger' id='name'></span>
</div>
<div class='form-floating mb-2'>
<input type='number' class='form-control' min='2' max='4' value='2' id='nb-players-by-game-input' placeholder='${lang.get("TournamentCreateNbPlayerByGame")}'>
<label for='nb-players-by-game-input' id='nb-players-by-game-label'>${lang.get("TournamentCreateNbPlayerByGame")}</label>
<span class='text-danger' id='nb_players_by_game'></span>
</div>
<div class='form-floating mb-2'>
<input type='number' class='form-control' min='2' value='4' id='nb-players-input' placeholder='${lang.get("TournamentCreateNbPlayer")}'>
<label for='nb-players-input' id='nb-players-label'>${lang.get("TournamentCreateNbPlayer")}</label>
<span class='text-danger' id='nb_players'></span>
</div>
<div class='d-flex'>
<button type='button' class='btn btn-primary mt-3 mb-2 mx-2' id='create-button'>${lang.get("TournamentCreateButton")}</button>
<span class='text-danger my-auto mx-2' id='detail'></span>
</div>
</div>
</div>
`;
}
}

View File

@ -55,7 +55,7 @@ export default class extends AbstractAuthenticatedView
document.getElementById("level").innerText = this.tournament.level;
document.getElementById("state").innerText = this.tournament.state;
if (this.tournament.state === "waiting")
if (this.tournament.started === false)
button.disabled = false;
}