Compare commits

..

No commits in common. "651fbbd67fc4d0bd43a261fe5bee2c92dd05d15a" and "609b04531555ab77ec8b911d9d8537589f35caed" have entirely different histories.

18 changed files with 171 additions and 197 deletions

View File

@ -24,7 +24,7 @@ pip install -r requirements.txt
``` ```
python manage.py makemigrations games python manage.py makemigrations games
python manage.py makemigrations profiles python manage.py makemigrations profiles
python manage.py makemigrations chat python manage.py runserver makemigrations chat
python manage.py migrate python manage.py migrate
``` ```
- Start the developpement server - Start the developpement server

View File

@ -1,7 +1,7 @@
body { body {
margin: 0.5em; margin: 20px;
font-family: 'Quicksand', sans-serif; font-family: 'Quicksand', sans-serif;
font-size: 40px; font-size: 60px;
} }
a { a {

View File

@ -1,18 +1,19 @@
#app #main .account #app .account
{ {
background-color: red; background-color: red;
} }
#app #main #app .account, #app .profile
{ {
width: 60%; width: 60%;
display: flex; display: flex;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
flex-direction: column; flex-direction: column;
flex-wrap: wrap;
} }
#app #main .profile #app .profile
{ {
background-color: green; background-color: green;
} }

View File

@ -45,7 +45,7 @@
} }
#app #messages { #app #messages {
max-height: 40em; max-height: 50em;
overflow: scroll; overflow: scroll;
overflow-y: scroll; overflow-y: scroll;
overflow-x: hidden; overflow-x: hidden;

View File

@ -1,19 +0,0 @@
import { Profile } from "./profile.js";
class MyProfile extends Profile
{
async change_avatar(form_data)
{
let response = await this.client._patch_file(`/api/profiles/me`, form_data);
let response_data = await response.json()
return response_data;
}
async init()
{
super.init("me");
}
}
export {MyProfile}

View File

@ -1,15 +1,7 @@
import { Client } from "./client.js";
class Account class Account
{ {
/**
* @param {Client} client
*/
constructor (client) constructor (client)
{ {
/**
* @type {Client} client
*/
this.client = client; this.client = client;
} }
@ -36,6 +28,7 @@ class Account
this.client._logged = false; this.client._logged = false;
return null; return null;
} }
console.log(response_data)
if (response_data == "user deleted") if (response_data == "user deleted")
this.client._logged = false; this.client._logged = false;
return response_data; return response_data;
@ -62,7 +55,7 @@ class Account
if (JSON.stringify(response_data) == JSON.stringify({'detail': 'Authentication credentials were not provided.'})) if (JSON.stringify(response_data) == JSON.stringify({'detail': 'Authentication credentials were not provided.'}))
{ {
this.client._; this.client._logged = false;
return null; return null;
} }
return response_data; return response_data;

View File

@ -1,8 +1,8 @@
import { Account } from "./account.js"; import { Account } from "./account.js";
import { MatchMaking } from "./matchmaking.js"; import { MatchMaking } from "./matchmaking.js";
import { Profile } from "./profile.js";
import { Profiles } from "./profiles.js"; import { Profiles } from "./profiles.js";
import { Channels } from './chat/channels.js'; import { Channels } from './chat/channels.js';
import { MyProfile } from "./MyProfile.js";
function getCookie(name) function getCookie(name)
{ {
@ -94,23 +94,15 @@ class Client
return response; return response;
} }
async _update_logged(state)
{
if (!this.logged && state)
{
this.me = new MyProfile(this);
await this.me.init();
}
this.logged = state;
}
async login(username, password) async login(username, password)
{ {
let response = await this._post("/api/accounts/login", {username: username, password: password}) let response = await this._post("/api/accounts/login", {username: username, password: password})
let data = await response.json(); let data = await response.json();
if (data.id != undefined) if (data.id != undefined)
{ {
await this._update_logged(true); this.me = new Profile(this)
await this.me.init(data.id)
this.logged = true;
return null; return null;
} }
return data; return data;
@ -119,7 +111,7 @@ class Client
async logout() async logout()
{ {
await this._get("/api/accounts/logout"); await this._get("/api/accounts/logout");
await this._update_logged(false); this.logged = false;
} }
async _test_logged() async _test_logged()
@ -128,7 +120,10 @@ class Client
let data = await response.json(); let data = await response.json();
if (data.id !== undefined) if (data.id !== undefined)
await this._update_logged(true); {
this.me = new Profile(this)
await this.me.init(data.id)
}
return data.id !== undefined; return data.id !== undefined;
} }
} }

View File

@ -1,9 +1,9 @@
import { Client } from "./client.js"; import { client, navigateTo } from "../index.js"
class MatchMaking class MatchMaking
{ {
/** /**
* @param {Client} client * @param {client} client
*/ */
constructor(client) constructor(client)
{ {

View File

@ -1,15 +1,7 @@
import { Client } from "./client.js";
class Profile class Profile
{ {
/**
* @param {Client} client
*/
constructor (client, username = undefined, avatar_url = undefined, user_id = undefined) constructor (client, username = undefined, avatar_url = undefined, user_id = undefined)
{ {
/**
* @type {Client} client
*/
this.client = client; this.client = client;
this.username = username; this.username = username;
this.avatar_url = avatar_url this.avatar_url = avatar_url
@ -21,10 +13,23 @@ class Profile
let response = await this.client._get(`/api/profiles/${user_id}`); let response = await this.client._get(`/api/profiles/${user_id}`);
let response_data = await response.json(); let response_data = await response.json();
this.user_id = response.user_id; this.user_id = user_id;
this.username = response_data.username; this.username = response_data.username;
this.avatar_url = response_data.avatar_url; this.avatar_url = response_data.avatar_url;
} }
async change_avatar(form_data)
{
let response = await this.client._patch_file(`/api/profiles/${this.user_id}`, form_data);
let response_data = await response.json()
return response_data;
}
async setData (data)
{
}
} }
export {Profile} export {Profile}

View File

@ -2,14 +2,8 @@ import { Profile } from "./profile.js";
class Profiles class Profiles
{ {
/**
* @param {Client} client
*/
constructor (client) constructor (client)
{ {
/**
* @type {Client} client
*/
this.client = client this.client = client
} }

View File

@ -79,7 +79,6 @@ const router = async (uri) => {
lastView = view; lastView = view;
await client.isAuthentificate();
let content = await view.getHtml(); let content = await view.getHtml();
if (content == null) if (content == null)
return 1; return 1;

View File

@ -1,18 +0,0 @@
function clear(property_name, elements_id)
{
elements_id.forEach(element_id => {
let element = document.getElementById(element_id)
element[property_name] = ""
});
}
function fill_errors(errors, property_name)
{
Object.keys(errors).forEach(error_field =>
{
let element = document.getElementById(error_field);
element[property_name] = errors[error_field];
});
}
export {fill_errors, clear}

View File

@ -1,5 +1,4 @@
import { client, navigateTo } from "../index.js"; import { client, navigateTo } from "../index.js";
import { clear, fill_errors } from "../utils/formUtils.js";
import AbstractAuthentificateView from "./AbstractAuthentifiedView.js"; import AbstractAuthentificateView from "./AbstractAuthentifiedView.js";
export default class extends AbstractAuthentificateView export default class extends AbstractAuthentificateView
@ -11,40 +10,29 @@ export default class extends AbstractAuthentificateView
async postInit() async postInit()
{ {
document.getElementById("save-account-button").onclick = this.save_account; if (this.fill() === null)
document.getElementById("delete-account-button").onclick = this.delete_account; return;
document.getElementById("save-profile-button").onclick = this.save_profile; document.getElementById("save-account-button").onclick = this.acccount_save;
document.getElementById("delete-account-button").onclick = this.account_delete_accounts;
} }
async delete_account() async fill()
{ {
let current_password = document.getElementById("current_password-input").value; let data = await client.account.get();
let response_data = await client.account.delete(current_password); if (data === null)
console.log(await client.isAuthentificate())
if (response_data === null || response_data === "user deleted")
{ {
navigateTo("/login"); navigateTo("/login")
return; return;
} }
clear("innerHTML", ["current_password-input"]) document.getElementById("username").value = data.username;
fill_errors({"current_password-input": response_data["password"]}, "innerHTML")
} }
async save_account() async delete_accounts()
{ {
let username = document.getElementById("username-input").value; let current_password = document.getElementById("current_password").value;
let new_password = document.getElementById("new_password-input").value;
let current_password = document.getElementById("current_password-input").value; let response_data = await client.account.delete(current_password);
let data = {};
data.username = username;
if (new_password.length != 0)
data.new_password = new_password;
let response_data = await client.account.update(data, current_password);
if (response_data === null) if (response_data === null)
{ {
@ -52,54 +40,92 @@ export default class extends AbstractAuthentificateView
return; return;
} }
if (response_data === "data has been alterate") ["delete", "current_password"].forEach(error_field => {
response_data = {"save-account": "saved"} let error_display = document.getElementById(`error_${error_field}`);
if (error_display != null)
error_display.innerHTML = "";
});
clear("innerHTML", ["username", "new_password", "current_password", "save-account", "delete-account"]) if (response_data === "user deleted")
fill_errors(response_data, "innerHTML") {
document.getElementById(`error_delete`).innerHTML = "OK";
navigateTo("/login")
return;
}
document.getElementById("error_current_password").innerHTML = response_data["password"]
} }
async save_profile() async save()
{ {
let avatar = document.getElementById("avatar-input"); let username = document.getElementById("username").value;
let new_password = document.getElementById("new_password").value;
let current_password = document.getElementById("current_password").value;
let data = {};
data.username = username;
if (new_password.length != 0)
data.new_password = new_password;
let response_data = await client.account.update(data, current_password);
if (response_data === null)
{
navigateTo("/login");
return;
}
else if (response_data === "data has been alterate")
{
navigateTo("/me");
return;
}
["username", "new_password", "current_password"].forEach(error_field => {
let error_display = document.getElementById(`error_${error_field}`);
if (error_display != null)
error_display.innerHTML = "";
});
Object.keys(response_data).forEach(error_field => {
let error_display = document.getElementById(`error_${error_field}`);
if (error_display != null)
error_display.innerHTML = response_data[error_field];
});
let avatar = document.getElementById("avatar");
if (avatar.files[0] !== undefined) if (avatar.files[0] !== undefined)
{ {
let form_data = new FormData(); let form_data = new FormData();
form_data.append("file", avatar.files[0]); form_data.append("file", avatar.files[0]);
await client.me.change_avatar(form_data); await client.me.change_avatar(form_data)
} }
document.getElementById("save-profile").innerHTML = "Saved";
} }
async getHtml() async getHtml()
{ {
return ` return `
<link rel="stylesheet" href="/static/css/me.css"> <link rel="stylesheet" href="static/css/me.css">
<h1>ME</h1> <h1>ME</h1>
<div id="main"> <div class="account">
<div class="account"> <h3>Account</h3>
<h3>Account</h3> <input type="text" placeholder="username" id="username">
<input type="text" placeholder="username" id="username-input" text=${client.me.username}> <span id="error_username"></span>
<span id="username"></span> <input type=password placeholder="new password" id="new_password">
<input type=password placeholder="new_password" id="new_password-input"> <span id="error_new_password"></span>
<span id="new_password"></span> <input type=password placeholder="current password" id="current_password">
<input type=password placeholder="current_password" id="current_password-input"> <span id="error_current_password"></span>
<span id="current_password"></span> <input type="button" value="Save Credentials" id="save-account-button">
<span id="error_save"></span>
<input type="button" value="Save Credentials" id="save-account-button"> <input type="button" value="Delete Account" id="delete-account-button">
<span id="save-account"></span> <span id="error_delete"></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/png, image/jpeg">
<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> </div>
<div class="profile">
<h3>Profile</h3>
<input type="file" id="avatar" accept="image/png, image/jpeg">
<input type="button" value="Save profile" id="save-profile-button">
<span id="error_save"></span>
</div>
<a href="/logout" class="nav__link" data-link>Logout</a>
`; `;
} }
} }

View File

@ -52,7 +52,6 @@ export default class extends AbstractView {
// chat // chat
if (logged && client.me.user_id != user.user_id) { if (logged && client.me.user_id != user.user_id) {
let add_chat = document.createElement("a"); let add_chat = document.createElement("a");
add_chat.href = "";
add_chat.id = "add_chat_off"; add_chat.id = "add_chat_off";
add_chat.onclick = async () => { add_chat.onclick = async () => {
if (client.channel != undefined) { if (client.channel != undefined) {

View File

@ -1,11 +1,10 @@
import { client, navigateTo } from "../../index.js"; import { client, navigateTo } from "../../index.js";
import { clear, fill_errors } from "../../utils/formUtils.js";
import AbstractNonAuthentifiedView from "../AbstractNonAuthentified.js"; import AbstractNonAuthentifiedView from "../AbstractNonAuthentified.js";
async function login() async function login()
{ {
let username = document.getElementById("username-input").value; let username = document.getElementById("username").value;
let password = document.getElementById("password-input").value; let password = document.getElementById("password").value;
let response_data = await client.login(username, password); let response_data = await client.login(username, password);
@ -15,8 +14,17 @@ async function login()
return; return;
} }
clear("innerHTML", ["username", "user", "password"]); ["username", "user", "password"].forEach(error_field => {
fill_errors(response_data, "innerHTML"); let error_display = document.getElementById(`error_${error_field}`);
if (error_display != null)
error_display.innerHTML = "";
});
Object.keys(response_data).forEach(error_field => {
let error_display = document.getElementById(`error_${error_field}`);
if (error_display != null)
error_display.innerHTML = response_data[error_field];
});
} }
export default class extends AbstractNonAuthentifiedView { export default class extends AbstractNonAuthentifiedView {
@ -26,20 +34,20 @@ export default class extends AbstractNonAuthentifiedView {
async postInit() async postInit()
{ {
document.getElementById("login-button").onclick = login; document.getElementById("button").onclick = login;
} }
async getHtml() { async getHtml() {
return ` return `
<div class=form> <div class=form>
<label>Login</label> <label>Login</label>
<link rel="stylesheet" href="/static/css/accounts/login.css"> <link rel="stylesheet" href="static/css/accounts/login.css">
<input type="text" id="username-input" placeholder="username"> <input type="text" id="username" placeholder="username">
<span id="username"></span> <span id="error_username"></span>
<input type="password" id="password-input" placeholder="password"> <input type="password" id="password" placeholder="password">
<span id="password"></span> <span id="error_password"></span>
<input type="button" value="Login" id="login-button"> <input type="button" value="login" id="button">
<span id="user"></span> <span id="error_user"></span>
<a href="/register" class="nav__link" data-link>Register</a> <a href="/register" class="nav__link" data-link>Register</a>
</div> </div>
`; `;

View File

@ -1,11 +1,10 @@
import { client, navigateTo } from "../../index.js"; import { client, navigateTo } from "../../index.js";
import { clear, fill_errors } from "../../utils/formUtils.js";
import AbstractNonAuthentifiedView from "../AbstractNonAuthentified.js"; import AbstractNonAuthentifiedView from "../AbstractNonAuthentified.js";
async function register() async function register()
{ {
let username = document.getElementById("username-input").value; let username = document.getElementById("username").value;
let password = document.getElementById("password-input").value; let password = document.getElementById("password").value;
let response_data = await client.account.create(username, password); let response_data = await client.account.create(username, password);
@ -15,8 +14,17 @@ async function register()
return; return;
} }
clear("innerHTML", ["username", "user", "password"]); ["username", "user", "password"].forEach(error_field => {
fill_errors(response_data, "innerHTML"); let error_display = document.getElementById(`error_${error_field}`);
if (error_display != null)
error_display.innerHTML = "";
});
Object.keys(response_data).forEach(error_field => {
let error_display = document.getElementById(`error_${error_field}`);
if (error_display != null)
error_display.innerHTML = response_data[error_field];
});
} }
export default class extends AbstractNonAuthentifiedView { export default class extends AbstractNonAuthentifiedView {
@ -26,20 +34,20 @@ export default class extends AbstractNonAuthentifiedView {
async postInit() async postInit()
{ {
document.getElementById("register-button").onclick = register; document.getElementById("button").onclick = register;
} }
async getHtml() { async getHtml() {
return ` return `
<div class=form> <div class=form>
<label>Register</label> <label>Register</label>
<link rel="stylesheet" href="/static/css/accounts/register.css"> <link rel="stylesheet" href="static/css/accounts/register.css">
<input type="text" id="username-input" placeholder="username"> <input type="text" id="username" placeholder="username">
<span id="username"></span> <span id="error_username"></span>
<input type="password" id="password-input" placeholder="password"> <input type="password" id="password" placeholder="password">
<span id="password"></span> <span id="error_password"></span>
<input type="button" value="Register" id="register-button"> <input type="button" value="register" id="button">
<span id="user"></span> <span id="error_user"></span>
<a href="/login" class="nav__link" data-link>Login</a> <a href="/login" class="nav__link" data-link>Login</a>
</div> </div>
`; `;

View File

@ -5,7 +5,7 @@ from django.conf.urls.static import static
from . import viewsets from . import viewsets
urlpatterns = [ urlpatterns = [
path("me", viewsets.MyProfileViewSet.as_view({'patch': 'partial_update', 'get': 'retrieve'}), name="my_profile_page"), path("<int:pk>", viewsets.ProfileViewSet.as_view({'get': 'retrieve', 'patch': 'partial_update'}), name="profile_page"),
path("<int:pk>", viewsets.ProfileViewSet.as_view({'get': 'retrieve'}), name="profile_page"),
path("", viewsets.ProfileViewSet.as_view({'get': 'list'}), name="profiles_list"), path("", viewsets.ProfileViewSet.as_view({'get': 'list'}), name="profiles_list"),
#path("me", viewsets.ProfileViewSet.as_view(), name="my_profile_page"),
] + static("/static/avatars/", document_root="./avatars") ] + static("/static/avatars/", document_root="./avatars")

View File

@ -3,7 +3,6 @@ from rest_framework.parsers import MultiPartParser, FormParser
from rest_framework import permissions, status from rest_framework import permissions, status
from rest_framework import viewsets from rest_framework import viewsets
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.authentication import SessionAuthentication
from django.http import HttpRequest from django.http import HttpRequest
from django.db.models import QuerySet from django.db.models import QuerySet
@ -18,9 +17,7 @@ class ProfileViewSet(viewsets.ModelViewSet):
permission_classes = (permissions.IsAuthenticatedOrReadOnly,) permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
def retrieve(self, request: HttpRequest, pk=None): def retrieve(self, request: HttpRequest, pk=None):
if (not self.queryset().filter(pk=pk).exists()): instance = ProfileModel.objects.get(pk=pk)
return Response({"detail": "Profile not found."}, status=status.HTTP_404_NOT_FOUND)
instance = self.queryset().get(pk=pk)
instance.avatar_url.name = instance.avatar_url.name[instance.avatar_url.name.find("static") - 1:] instance.avatar_url.name = instance.avatar_url.name[instance.avatar_url.name.find("static") - 1:]
return Response(self.serializer_class(instance).data, return Response(self.serializer_class(instance).data,
status=status.HTTP_200_OK) status=status.HTTP_200_OK)
@ -34,28 +31,14 @@ class ProfileViewSet(viewsets.ModelViewSet):
def perform_create(self, serializer): def perform_create(self, serializer):
serializer.save(user=self.request.user) serializer.save(user=self.request.user)
class MyProfileViewSet(viewsets.ModelViewSet): def perform_update(self, serializer):
query: QuerySet = ProfileModel.objects.filter(pk=self.request.user.pk)
permission_classes = (permissions.IsAuthenticated,) if (not query.exists()):
authentication_classes = (SessionAuthentication,) return Response("profile not found", status=status.HTTP_400_BAD_REQUEST)
serializer_class = ProfileSerializer profile: ProfileModel = ProfileModel.objects.get(pk=self.request.user.pk)
queryset = ProfileModel.objects.all
def get_object(self):
obj = self.queryset().get(pk=self.request.user.pk)
return obj
def perform_update(self, serializer, pk=None):
profile: ProfileModel = self.get_object()
avatar = self.request.data.get("file", None) avatar = self.request.data.get("file", None)
if (avatar is not None): if (avatar is not None):
if (profile.avatar_url.name != "./profiles/static/avatars/default.avif"): if (profile.avatar_url.name != "./profiles/static/avatars/default.avif"):
profile.avatar_url.storage.delete(profile.avatar_url.name) profile.avatar_url.storage.delete(profile.avatar_url.name)
profile.avatar_url = avatar profile.avatar_url = avatar
profile.save() profile.save()
def retrieve(self, request: HttpRequest, pk=None):
instance: ProfileModel = self.get_object()
instance.avatar_url.name = instance.avatar_url.name[instance.avatar_url.name.find("static") - 1:]
return Response(self.serializer_class(instance).data,
status=status.HTTP_200_OK)