merge
This commit is contained in:
commit
3b1f9d91b5
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-02-01 13:59+0100\n"
|
||||
"POT-Creation-Date: 2024-03-11 11:02+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -17,6 +17,11 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
|
||||
#: serializers/update_user.py:15
|
||||
msgid "You dont have permission for this user."
|
||||
msgstr "Vous n'avez pas de permissions pour cet utilisateur."
|
||||
|
||||
#: views/login.py:22
|
||||
msgid "Invalid username or password."
|
||||
msgstr "Nom d'utilisateur ou mot de passe incorect."
|
||||
|
20
accounts/serializers/update_user.py
Normal file
20
accounts/serializers/update_user.py
Normal file
@ -0,0 +1,20 @@
|
||||
from rest_framework.serializers import ModelSerializer, ValidationError
|
||||
from django.contrib.auth.models import User
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
|
||||
class UpdateUserSerializer(ModelSerializer):
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ['username']
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
user = self.context['request'].user
|
||||
|
||||
if user.pk != instance.pk:
|
||||
raise ValidationError({'authorize': _('You dont have permission for this user.')})
|
||||
|
||||
instance.username = validated_data.get('username', instance.username)
|
||||
|
||||
instance.save()
|
||||
return instance
|
@ -1,6 +1,6 @@
|
||||
from django.urls import path
|
||||
|
||||
from .views import register, login, logout, delete, edit, logged
|
||||
from .views import register, login, logout, delete, logged, update_profile
|
||||
|
||||
urlpatterns = [
|
||||
path("register", register.RegisterView.as_view(), name="register"),
|
||||
@ -8,6 +8,5 @@ urlpatterns = [
|
||||
path("logout", logout.LogoutView.as_view(), name="logout"),
|
||||
path("logged", logged.LoggedView.as_view(), name="logged"),
|
||||
path("delete", delete.DeleteView.as_view(), name="delete"),
|
||||
path("edit", edit.EditView.as_view(), name="change_password")
|
||||
|
||||
]
|
||||
path('update_profile', update_profile.UpdateProfileView.as_view(), name='update_profile')
|
||||
]
|
||||
|
@ -5,17 +5,18 @@ from django.contrib.auth import logout
|
||||
from django.http import HttpRequest
|
||||
from rest_framework.authentication import SessionAuthentication
|
||||
|
||||
|
||||
class DeleteView(APIView):
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
authentication_classes = (SessionAuthentication,)
|
||||
|
||||
def delete(self, request: HttpRequest):
|
||||
data: dict = request.data
|
||||
|
||||
password: str = data["password"]
|
||||
if (password is None):
|
||||
return Response({"password": ["This field may not be blank."]})
|
||||
if (request.user.check_password(password) == False):
|
||||
return Response({"password": ["Password wrong."]})
|
||||
if (request.user.check_password(password) is False):
|
||||
return Response({"password": ["Password incorrect."]},
|
||||
status.HTTP_401_UNAUTHORIZED)
|
||||
request.user.delete()
|
||||
logout(request)
|
||||
return Response("user deleted", status=status.HTTP_200_OK)
|
||||
return Response(status=status.HTTP_200_OK)
|
||||
|
@ -1,45 +0,0 @@
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import permissions, status
|
||||
from django.http import HttpRequest
|
||||
from django.contrib.auth import login
|
||||
from rest_framework.authentication import SessionAuthentication
|
||||
from django.contrib.auth.models import User
|
||||
import re
|
||||
|
||||
class EditView(APIView):
|
||||
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
authentication_classes = (SessionAuthentication,)
|
||||
|
||||
def get(self, request: HttpRequest):
|
||||
return Response({"username": request.user.username, "id": request.user.pk})
|
||||
|
||||
def patch(self, request: HttpRequest):
|
||||
data: dict = request.data
|
||||
|
||||
current_password: str = data.get("current_password")
|
||||
if (current_password is None):
|
||||
return Response({"current_password": ["This field may not be blank."]})
|
||||
|
||||
user_object = request.user
|
||||
|
||||
if (user_object.check_password(current_password) == False):
|
||||
return Response({"current_password": ["Password is wrong."]})
|
||||
|
||||
new_username = data.get("username", user_object.username)
|
||||
if (new_username != user_object.username):
|
||||
if (User.objects.filter(username=new_username).exists()):
|
||||
return Response({"username": ["A user with that username already exists."]})
|
||||
if (set(new_username) == {' '}):
|
||||
return Response({"username": ["This field may not be blank."]})
|
||||
if (re.search('^([a-z]||\@||\+||\-||\_)+$', new_username) is None):
|
||||
return Response({"username":["Enter a valid username. This value may contain only letters, numbers, and @/./+/-/_ characters."]})
|
||||
|
||||
new_password: str = data.get("password")
|
||||
if (new_password is not None):
|
||||
user_object.set_password(new_password)
|
||||
|
||||
user_object.save()
|
||||
|
||||
return Response("data has been alterate")
|
@ -2,15 +2,13 @@ from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import permissions, status
|
||||
from django.http import HttpRequest
|
||||
from django.contrib.auth import login
|
||||
from rest_framework.authentication import SessionAuthentication
|
||||
|
||||
from ..serializers.login import LoginSerializer
|
||||
|
||||
class LoggedView(APIView):
|
||||
|
||||
|
||||
permission_classes = (permissions.AllowAny,)
|
||||
authentication_classes = (SessionAuthentication,)
|
||||
|
||||
def get(self, request: HttpRequest):
|
||||
return Response(status = (status.HTTP_200_OK if request.user.is_authenticated else status.HTTP_400_BAD_REQUEST))
|
||||
return Response(status=status.HTTP_200_OK if request.user.is_authenticated else status.HTTP_400_BAD_REQUEST)
|
||||
|
@ -8,8 +8,9 @@ from django.utils.translation import gettext as _
|
||||
|
||||
from ..serializers.login import LoginSerializer
|
||||
|
||||
|
||||
class LoginView(APIView):
|
||||
|
||||
|
||||
permission_classes = (permissions.AllowAny,)
|
||||
authentication_classes = (SessionAuthentication,)
|
||||
|
||||
|
@ -5,9 +5,11 @@ from rest_framework.response import Response
|
||||
from django.http import HttpRequest
|
||||
from rest_framework.authentication import SessionAuthentication
|
||||
|
||||
|
||||
class LogoutView(APIView):
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
authentication_classes = (SessionAuthentication,)
|
||||
|
||||
def get(self, request: HttpRequest):
|
||||
logout(request)
|
||||
return Response("user unlogged", status=status.HTTP_200_OK)
|
||||
return Response("user logged out", status.HTTP_200_OK)
|
||||
|
@ -5,8 +5,10 @@ from rest_framework.response import Response
|
||||
from django.http import HttpRequest
|
||||
from django.contrib.auth import login
|
||||
|
||||
|
||||
class RegisterView(APIView):
|
||||
permission_classes = (permissions.AllowAny,)
|
||||
|
||||
def post(self, request: HttpRequest):
|
||||
data = request.data
|
||||
serializer = RegisterSerialiser(data=data)
|
||||
|
14
accounts/views/update_profile.py
Normal file
14
accounts/views/update_profile.py
Normal file
@ -0,0 +1,14 @@
|
||||
from ..serializers.update_user import UpdateUserSerializer
|
||||
from rest_framework.generics import UpdateAPIView
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
|
||||
class UpdateProfileView(UpdateAPIView):
|
||||
|
||||
queryset = User.objects.all()
|
||||
permission_classes = (IsAuthenticated,)
|
||||
serializer_class = UpdateUserSerializer
|
||||
|
||||
def get_object(self):
|
||||
return self.queryset.get(pk=self.request.user.pk)
|
@ -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;
|
||||
|
@ -1,11 +0,0 @@
|
||||
#app * {
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
|
||||
#app #main
|
||||
{
|
||||
width: 60%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
@ -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.';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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');
|
||||
};
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
`;
|
||||
|
@ -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>
|
||||
`;
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -3,13 +3,16 @@ from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
from django.db.models.signals import post_save, pre_delete
|
||||
from django.dispatch import receiver
|
||||
from django.conf import settings
|
||||
from django.db.models import IntegerField
|
||||
|
||||
from games.consumers import game_manager
|
||||
|
||||
from os.path import splitext
|
||||
|
||||
|
||||
def upload_to(instance, filename: str):
|
||||
return f"./profiles/static/avatars/{instance.pk}.{filename.split('.')[1]}"
|
||||
return f"./profiles/static/avatars/{instance.pk}{splitext(filename)[1]}"
|
||||
|
||||
|
||||
# Create your models here.
|
||||
class ProfileModel(models.Model):
|
||||
|
@ -1,6 +1,8 @@
|
||||
from rest_framework import serializers
|
||||
from .models import ProfileModel
|
||||
from django.conf import settings
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
|
||||
class ProfileSerializer(serializers.ModelSerializer):
|
||||
|
||||
@ -16,5 +18,5 @@ class ProfileSerializer(serializers.ModelSerializer):
|
||||
Check that the image is not too large
|
||||
'''
|
||||
if value.size > settings.PROFILE_PICTURE_MAX_SIZE:
|
||||
raise serializers.ValidationError('Image is too large.');
|
||||
return value;
|
||||
raise serializers.ValidationError(_('Image is too large.'))
|
||||
return value
|
||||
|
@ -4,7 +4,7 @@ from . import viewsets
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path("settings", viewsets.MyProfileViewSet.as_view({'patch': 'partial_update'}), name="my_profile_page"),
|
||||
path("settings", viewsets.MyProfileViewSet.as_view({'patch': 'partial_update', 'delete': 'delete_avatar'}), name="my_profile_page"),
|
||||
path("me", viewsets.MyProfileViewSet.as_view({'get': 'retrieve'}), name="my_profile_page"),
|
||||
path("", viewsets.ProfileViewSet.as_view({'get': 'list'}), name="profiles_list"),
|
||||
path("block", views.BlocksView.as_view(), name="block_page"),
|
||||
@ -12,5 +12,4 @@ urlpatterns = [
|
||||
path("friend", views.FriendsView.as_view(), name="friend_page"),
|
||||
path("user/<str:username>", viewsets.ProfileViewSet.as_view({'get': 'retrieve'}), name="profile_page"),
|
||||
path("id/<int:pk>", viewsets.ProfileViewSet.as_view({'get': 'retrieve_id'}), name="profile_page"),
|
||||
|
||||
]
|
||||
|
@ -1,4 +1,3 @@
|
||||
from rest_framework import permissions
|
||||
from rest_framework.parsers import MultiPartParser, FormParser
|
||||
from rest_framework import permissions, status
|
||||
from rest_framework import viewsets
|
||||
@ -42,28 +41,35 @@ class ProfileViewSet(viewsets.ModelViewSet):
|
||||
profile["avatar"] = profile["avatar"][profile["avatar"].find("static") - 1:]
|
||||
return Response(serializer.data)
|
||||
|
||||
class MyProfileViewSet(viewsets.ModelViewSet):
|
||||
|
||||
class MyProfileViewSet(viewsets.ModelViewSet):
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
authentication_classes = (SessionAuthentication,)
|
||||
serializer_class = ProfileSerializer
|
||||
queryset = ProfileModel.objects.all
|
||||
queryset = ProfileModel.objects.all()
|
||||
|
||||
def get_object(self):
|
||||
obj = self.queryset().get(pk=self.request.user.pk)
|
||||
obj = self.queryset.get(pk=self.request.user.pk)
|
||||
return obj
|
||||
|
||||
def perform_update(self, serializer, pk=None):
|
||||
serializer.is_valid(raise_exception=True);
|
||||
profile: ProfileModel = self.get_object();
|
||||
avatar = serializer.validated_data.get('avatar');
|
||||
def perform_update(self, serializer: ProfileSerializer, pk=None):
|
||||
serializer.is_valid(raise_exception=True)
|
||||
avatar = serializer.validated_data.get('avatar')
|
||||
profile: ProfileModel = self.get_object()
|
||||
|
||||
if (avatar is not None):
|
||||
if (profile.avatar.name != "./profiles/static/avatars/default.avif"):
|
||||
profile.avatar.storage.delete(profile.avatar.name)
|
||||
profile.avatar = avatar
|
||||
profile.save()
|
||||
|
||||
serializer.save()
|
||||
|
||||
def delete_avatar(self, request, pk=None):
|
||||
profile = self.get_object()
|
||||
if (profile.avatar.name != './profiles/static/avatars/default.avif'):
|
||||
profile.avatar.storage.delete(profile.avatar.name)
|
||||
profile.avatar.name = './profiles/static/avatars/default.avif'
|
||||
profile.save()
|
||||
return Response(ProfileSerializer(profile).data)
|
||||
|
||||
def retrieve(self, request: HttpRequest, pk=None):
|
||||
instance: ProfileModel = self.get_object()
|
||||
instance.avatar.name = instance.avatar.name[instance.avatar.name.find("static") - 1:]
|
||||
|
@ -5,6 +5,7 @@ from games.serializers import GameSerializer
|
||||
class TournamentSerializer(serializers.ModelSerializer):
|
||||
|
||||
levels = serializers.SerializerMethodField(read_only=True, required=False)
|
||||
state = serializers.SerializerMethodField(read_only=True, required=False)
|
||||
level = serializers.ReadOnlyField()
|
||||
started = serializers.ReadOnlyField()
|
||||
finished = serializers.ReadOnlyField()
|
||||
@ -12,7 +13,10 @@ class TournamentSerializer(serializers.ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = TournamentModel
|
||||
fields = ["name", "nb_players", "nb_players_by_game", "level", "started", "finished", "levels", "id"]
|
||||
fields = ["name", "nb_players", "nb_players_by_game", "level", "started", "finished", "levels", "id", "state"]
|
||||
|
||||
def get_state(self, instance: TournamentModel):
|
||||
return ["waiting", "started", "finished"][instance.started + instance.finished]
|
||||
|
||||
def get_levels(self, instance: TournamentModel):
|
||||
levels: list[list[int]] = []
|
||||
|
@ -43,7 +43,7 @@ class TournamentViewSet(viewsets.ModelViewSet):
|
||||
query = TournamentModel.objects.filter(started=False, finished=False)
|
||||
case _:
|
||||
query = TournamentModel.objects.all()
|
||||
serializer = TournamentSerializer(query, many=True)
|
||||
serializer = self.serializer_class(query, many=True)
|
||||
return Response(serializer.data)
|
||||
|
||||
def retrieve(self, request: HttpRequest, pk):
|
||||
|
Loading…
Reference in New Issue
Block a user