This commit is contained in:
Xamora 2024-02-01 12:58:17 +01:00
commit ab325ae489
15 changed files with 127 additions and 51 deletions

View File

@ -18,6 +18,6 @@ class LoginView(APIView):
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
user = serializer.get_user(data) user = serializer.get_user(data)
if user is None: if user is None:
return Response({'login': ['Invalid username or password.']}, status.HTTP_400_BAD_REQUEST) return Response({'login': ['Invalid username or password.']}, status.HTTP_401_UNAUTHORIZED)
login(request, user) login(request, user)
return Response({'id': user.pk}, status=status.HTTP_200_OK) return Response({'id': user.pk}, status=status.HTTP_200_OK)

View File

@ -1,11 +1,17 @@
import { reloadView } from '../index.js'
export default class LanguageManager { export default class LanguageManager {
constructor() { constructor() {
this.availableLanguages = ['en', 'fr']; this.availableLanguages = ['en', 'fr'];
this.dict = null;
this.currentLang = 'en' this.currentLang = 'en'
this.chosenLang = localStorage.getItem('preferedLanguage') || this.currentLang; this.chosenLang = localStorage.getItem('preferedLanguage') || this.currentLang;
if (this.chosenLang !== this.currentLang && this.availableLanguages.includes(this.chosenLang)) { if (this.chosenLang !== this.currentLang && this.availableLanguages.includes(this.chosenLang)) {
this.translatePage(); this.translatePage();
this.currentLang = this.chosenLang;
} else {
this.loadDict(this.chosenLang);
} }
} }
@ -13,33 +19,48 @@ export default class LanguageManager {
if (this.currentLang === this.chosenLang) if (this.currentLang === this.chosenLang)
return; return;
let dictUrl = `${location.origin}/static/js/lang/${this.chosenLang}.json`; await this.loadDict(this.chosenLang);
let translation = await fetch(dictUrl).then(response => { if (!this.dict)
if (response.status !== 200)
return null;
return response.json();
});
if (!translation) {
console.log(`No translation found for language ${this.chosenLang}`);
return 1; return 1;
}
document.querySelectorAll('[data-i18n]').forEach(el => { document.querySelectorAll('[data-i18n]').forEach(el => {
let key = el.getAttribute('data-i18n'); let key = el.getAttribute('data-i18n');
el.innerHTML = translation[key]; el.innerHTML = this.dict[key];
}) })
await reloadView();
this.currentLang = this.chosenLang;
return 0; return 0;
} }
async changeLanguage(lang) { async changeLanguage(lang) {
if (lang === this.currentLang || !this.availableLanguages.includes(lang)) if (lang === this.currentLang || !this.availableLanguages.includes(lang))
return; return 1;
this.chosenLang = lang; this.chosenLang = lang;
if (await this.translatePage() !== 0) if (await this.translatePage() !== 0)
return; return 1;
this.currentLang = this.chosenLang;
localStorage.setItem('preferedLanguage', lang); localStorage.setItem('preferedLanguage', lang);
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();
}
get(key, defaultTxt) {
if (!this.dict)
return defaultTxt;
return this.dict[key] || defaultTxt;
} }
} }

View File

@ -15,7 +15,7 @@ class MyProfile extends Profile
/** /**
* *
* @param {*} form_data * @param {*} form_data
* @returns * @returns {Promise<Object>}
*/ */
async change_avatar(form_data) async change_avatar(form_data)
{ {

View File

@ -16,7 +16,7 @@ class Account
/** /**
* @param {String} username * @param {String} username
* @param {String} password * @param {String} password
* @returns * @returns {?Promise<Object>}
*/ */
async create(username, password) async create(username, password)
{ {
@ -33,7 +33,7 @@ class Account
/** /**
* @param {String} password * @param {String} password
* @returns * @returns {?Promise<Object>}
*/ */
async delete(password) async delete(password)
{ {
@ -52,7 +52,7 @@ class Account
/** /**
* Get account data (username) * Get account data (username)
* @returns * @returns {?Promise<Object>}
*/ */
async get() async get()
{ {
@ -71,7 +71,7 @@ class Account
* *
* @param {*} data * @param {*} data
* @param {Number} password * @param {Number} password
* @returns * @returns {?Object}
*/ */
async update(data, password) async update(data, password)
{ {

View File

@ -78,7 +78,7 @@ class Client
/** /**
* The only right way to determine is the user is logged * The only right way to determine is the user is logged
* @returns {Boolean} * @returns {Promise<Boolean>}
*/ */
async isAuthentificate() async isAuthentificate()
{ {
@ -91,7 +91,7 @@ class Client
* Send a GET request to %uri% * Send a GET request to %uri%
* @param {String} uri * @param {String} uri
* @param {*} data * @param {*} data
* @returns {Response} * @returns {Promise<Response>}
*/ */
async _get(uri, data) async _get(uri, data)
{ {
@ -106,7 +106,7 @@ class Client
* Send a POST request * Send a POST request
* @param {String} uri * @param {String} uri
* @param {*} data * @param {*} data
* @returns {Response} * @returns {Promise<Response>}
*/ */
async _post(uri, data) async _post(uri, data)
{ {
@ -115,6 +115,7 @@ class Client
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
"X-CSRFToken": getCookie("csrftoken"), "X-CSRFToken": getCookie("csrftoken"),
'Accept-Language': this.lang.currentLang,
}, },
body: JSON.stringify(data), body: JSON.stringify(data),
}); });
@ -125,7 +126,7 @@ class Client
* Send a DELETE request * Send a DELETE request
* @param {String} uri * @param {String} uri
* @param {String} data * @param {String} data
* @returns {Response} * @returns {Promise<Response>}
*/ */
async _delete(uri, data) async _delete(uri, data)
{ {
@ -144,7 +145,7 @@ class Client
* Send a PATCH request with json * Send a PATCH request with json
* @param {String} uri * @param {String} uri
* @param {*} data * @param {*} data
* @returns {Response} * @returns {Promise<Response>}
*/ */
async _patch_json(uri, data) async _patch_json(uri, data)
{ {
@ -163,7 +164,7 @@ class Client
* Send a PATCH request with file * Send a PATCH request with file
* @param {String} uri * @param {String} uri
* @param {*} file * @param {*} file
* @returns {Response} * @returns {Promise<Response>}
*/ */
async _patch_file(uri, file) async _patch_file(uri, file)
{ {
@ -179,7 +180,7 @@ class Client
/** /**
* Change logged state. Use It if you recv an 403 error * Change logged state. Use It if you recv an 403 error
* @param {Boolean} state * @param {Promise<?>} state
* @returns * @returns
*/ */
async _update_logged(state) async _update_logged(state)
@ -211,7 +212,7 @@ class Client
* Loggin the user * Loggin the user
* @param {String} username * @param {String} username
* @param {String} password * @param {String} password
* @returns * @returns {Promise<Response>}
*/ */
async login(username, password) async login(username, password)
{ {
@ -224,6 +225,7 @@ class Client
/** /**
* Logout the user * Logout the user
* @returns {Promise<?>}
*/ */
async logout() async logout()
{ {
@ -233,7 +235,7 @@ class Client
/** /**
* Determine if the user is logged. NEVER USE IT, USE isAuthentificated() * Determine if the user is logged. NEVER USE IT, USE isAuthentificated()
* @returns {Boolean} * @returns {Promise<Boolean>}
*/ */
async _test_logged() async _test_logged()
{ {

View File

@ -19,7 +19,7 @@ class MatchMaking
* @param {CallableFunction} receive_func * @param {CallableFunction} receive_func
* @param {CallableFunction} disconnect_func * @param {CallableFunction} disconnect_func
* @param {Number} mode The number of players in a game * @param {Number} mode The number of players in a game
* @returns {undefined} * @returns {Promise<?>}
*/ */
async start(receive_func, disconnect_func, mode) async start(receive_func, disconnect_func, mode)
{ {
@ -50,6 +50,9 @@ class MatchMaking
this.disconnect_func(event); this.disconnect_func(event);
} }
/**
* @returns {Promise<?>}
*/
async stop() async stop()
{ {
if (this._socket) if (this._socket)

View File

@ -34,6 +34,10 @@ class Profile
this.isFriend = false; this.isFriend = false;
} }
/**
*
* @returns {Promise<*>}
*/
async init() async init()
{ {
let response; let response;

View File

@ -15,7 +15,7 @@ class Profiles
/** /**
* *
* @returns {[Profile]} * @returns {Promise<[Profile]>}
*/ */
async all() async all()
{ {
@ -53,7 +53,7 @@ class Profiles
/** /**
* Block a user * Block a user
* @param {Number} user_id * @param {Number} user_id
* @returns * @returns {Promise<Object>}
*/ */
async block(user_id) { async block(user_id) {
@ -70,7 +70,7 @@ class Profiles
/** /**
* Unblock a user * Unblock a user
* @param {Number} user_id * @param {Number} user_id
* @returns * @returns {Promise<Object>}
*/ */
async deblock(user_id) { async deblock(user_id) {

View File

@ -73,6 +73,10 @@ class Tourmanent
this.connected = false; this.connected = false;
} }
/**
*
* @returns {Promise<?>}
*/
async init() async init()
{ {
let response = await this.client._get(`/api/tournaments/${id}`); let response = await this.client._get(`/api/tournaments/${id}`);
@ -108,6 +112,12 @@ class Tourmanent
this._socket.send(JSON.stringify({participate: ""})); this._socket.send(JSON.stringify({participate: ""}));
} }
/**
* Join the tournament Websocket
* @param {CallableFunction} receive_func
* @param {CallableFunction} disconnect_func
* @returns {?}
*/
async join(receive_func, disconnect_func) async join(receive_func, disconnect_func)
{ {
if (!await this.client.isAuthentificate()) if (!await this.client.isAuthentificate())

View File

@ -17,7 +17,7 @@ class Tourmanents
/** /**
* *
* @param {Number} id * @param {Number} id
* @returns * @returns {?Promise<Tournament>}
*/ */
async getTournament(id) async getTournament(id)
{ {
@ -47,6 +47,7 @@ class Tourmanents
/** /**
* @param {String} state must be "finished", or "started", or "waiting". Any other return all elements * @param {String} state must be "finished", or "started", or "waiting". Any other return all elements
* @returns {?Promise<[Tourmanent]>}
*/ */
async search(state) async search(state)
{ {
@ -76,6 +77,10 @@ class Tourmanents
return tournaments; return tournaments;
} }
/**
* Get all tournaments
* @returns {?Promise<[Tourmanent]>}
*/
async all() async all()
{ {
return await this.search(""); return await this.search("");

View File

@ -19,10 +19,10 @@ import TournamentPageView from "./views/tournament/TournamentPageView.js";
import TournamentsView from "./views/tournament/TournamentsListView.js"; import TournamentsView from "./views/tournament/TournamentsListView.js";
import TournamentCreateView from "./views/tournament/TournamentCreateView.js"; import TournamentCreateView from "./views/tournament/TournamentCreateView.js";
let client = new Client(location.protocol + "//" + location.host) let client = new Client(location.origin);
let lastView = undefined let lastView = undefined;
let lastPageUrlBeforeLogin = undefined let lastPageUrlBeforeLogin = undefined;
const pathToRegex = path => new RegExp("^" + path.replace(/\//g, "\\/").replace(/:\w+/g, "(.+)") + "$"); const pathToRegex = path => new RegExp("^" + path.replace(/\//g, "\\/").replace(/:\w+/g, "(.+)") + "$");
@ -48,9 +48,11 @@ const navigateTo = async (uri) => {
} }
}; };
const reloadView = async _ => renderView(lastView);
async function renderView(view) async function renderView(view)
{ {
let content = await view.getHtml(); let content = await view?.getHtml();
if (content == null) if (content == null)
return 1; return 1;
@ -108,14 +110,16 @@ const router = async(uri) => {
if (lastView !== undefined) if (lastView !== undefined)
await lastView.leavePage(); await lastView.leavePage();
if (uri !== '/login' && uri !== '/register' && uri !== '/logout')
lastPageUrlBeforeLogin = uri;
else
lastPageUrlBeforeLogin = undefined;
const view = new match.route.view(getParams(match), lastPageUrlBeforeLogin); const view = new match.route.view(getParams(match), lastPageUrlBeforeLogin);
if (view instanceof AbstractRedirectView && await view.redirect()) if (view instanceof AbstractRedirectView && await view.redirect())
return 1; return 1;
lastView = view; lastView = view;
if (uri !== '/login' && uri !== '/register' && uri !== '/logout')
lastPageUrlBeforeLogin = uri;
if (await renderView(view)) if (await renderView(view))
return 1; return 1;
@ -139,12 +143,19 @@ document.addEventListener("DOMContentLoaded", async () => {
//Languages //Languages
Array.from(document.getElementById('languageSelector').children).forEach(el => { Array.from(document.getElementById('languageSelector').children).forEach(el => {
el.onclick = _ => client.lang.changeLanguage(el.value); el.onclick = async _ => {
if (await client.lang.changeLanguage(el.value))
return;
document.querySelector('#languageSelector > .active')?.classList.remove('active');
el.classList.add('active');
};
}); });
document.querySelector(`#languageSelector > [value=${client.lang.chosenLang}]`)
?.classList.add('active');
await client.isAuthentificate(); await client.isAuthentificate();
router(location.pathname); router(location.pathname);
document.querySelector('a[href=\'' + location.pathname + '\']')?.classList.add('active'); document.querySelector('a[href=\'' + location.pathname + '\']')?.classList.add('active');
}); });
export { client, navigateTo } export { client, navigateTo, reloadView }

View File

@ -5,5 +5,11 @@
"navbarRegister": "Register", "navbarRegister": "Register",
"navbarProfile": "My Profile", "navbarProfile": "My Profile",
"navbarSettings": "Settings", "navbarSettings": "Settings",
"navbarLogout": "Logout" "navbarLogout": "Logout",
"homeWindowTitle": "Home",
"homeTitle": "Home",
"homeOnline": "Play online",
"homeOffline": "Play offline",
"homeMe": "Me",
"homeLogout": "Logout"
} }

View File

@ -1,9 +1,15 @@
{ {
"navbarSearch": "Recherche", "navbarSearch": "Recherche",
"navbarHome": "Maison", "navbarHome": "Maison",
"navbarLogin": "Se connecter", "navbarLogin": "Connexion",
"navbarRegister": "S'inscrire", "navbarRegister": "S'inscrire",
"navbarProfile": "Mon Profil", "navbarProfile": "Mon Profil",
"navbarSettings": "Paramètres", "navbarSettings": "Paramètres",
"navbarLogout": "Se déconnecter" "navbarLogout": "Déconnexion",
"homeWindowTitle": "Maison",
"homeTitle": "Maison",
"homeOnline": "Jouer en ligne",
"homeOffline": "Jouer hors ligne",
"homeMe": "Moi",
"homeLogout": "Déconnexion"
} }

View File

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

View File

@ -13,6 +13,7 @@ https://docs.djangoproject.com/en/4.2/ref/settings/
import os import os
from pathlib import Path from pathlib import Path
from django.utils.translation import gettext_lazy as _
# Build paths inside the project like this: BASE_DIR / 'subdir'. # Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent BASE_DIR = Path(__file__).resolve().parent.parent
@ -79,6 +80,7 @@ MIDDLEWARE = [
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.locale.LocaleMiddleware',
] ]
ROOT_URLCONF = 'transcendence.urls' ROOT_URLCONF = 'transcendence.urls'
@ -137,6 +139,11 @@ AUTH_PASSWORD_VALIDATORS = [
LANGUAGE_CODE = 'en-us' LANGUAGE_CODE = 'en-us'
LANGUAGES = [
('en', _('English')),
('fr', _('French')),
]
TIME_ZONE = 'UTC' TIME_ZONE = 'UTC'
USE_I18N = True USE_I18N = True