From 1f41e62a863a2290e371d6daff28f0830421b2ee Mon Sep 17 00:00:00 2001 From: starnakin Date: Sat, 16 Dec 2023 16:42:30 +0100 Subject: [PATCH 1/2] core: refonte profile --- frontend/static/css/me.css | 7 +- frontend/static/js/api/MyProfile.js | 19 ++++ frontend/static/js/api/account.js | 11 ++- frontend/static/js/api/client.js | 23 +++-- frontend/static/js/api/matchmaking.js | 4 +- frontend/static/js/api/profile.js | 23 ++--- frontend/static/js/api/profiles.js | 6 ++ frontend/static/js/index.js | 1 + frontend/static/js/utils/formUtils.js | 18 ++++ frontend/static/js/views/MeView.js | 128 ++++++++++---------------- profiles/urls.py | 4 +- profiles/viewsets.py | 28 ++++-- 12 files changed, 154 insertions(+), 118 deletions(-) create mode 100644 frontend/static/js/api/MyProfile.js create mode 100644 frontend/static/js/utils/formUtils.js diff --git a/frontend/static/css/me.css b/frontend/static/css/me.css index f7c2313..09a18c8 100644 --- a/frontend/static/css/me.css +++ b/frontend/static/css/me.css @@ -1,19 +1,18 @@ -#app .account +#app #main .account { background-color: red; } -#app .account, #app .profile +#app #main { width: 60%; display: flex; margin-left: auto; margin-right: auto; flex-direction: column; - flex-wrap: wrap; } -#app .profile +#app #main .profile { background-color: green; } \ No newline at end of file diff --git a/frontend/static/js/api/MyProfile.js b/frontend/static/js/api/MyProfile.js new file mode 100644 index 0000000..d1299f7 --- /dev/null +++ b/frontend/static/js/api/MyProfile.js @@ -0,0 +1,19 @@ +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} \ No newline at end of file diff --git a/frontend/static/js/api/account.js b/frontend/static/js/api/account.js index 1004c1c..db22a63 100644 --- a/frontend/static/js/api/account.js +++ b/frontend/static/js/api/account.js @@ -1,7 +1,15 @@ +import { Client } from "./client.js"; + class Account { + /** + * @param {Client} client + */ constructor (client) { + /** + * @type {Client} client + */ this.client = client; } @@ -28,7 +36,6 @@ class Account this.client._logged = false; return null; } - console.log(response_data) if (response_data == "user deleted") this.client._logged = false; return response_data; @@ -55,7 +62,7 @@ class Account if (JSON.stringify(response_data) == JSON.stringify({'detail': 'Authentication credentials were not provided.'})) { - this.client._logged = false; + this.client._; return null; } return response_data; diff --git a/frontend/static/js/api/client.js b/frontend/static/js/api/client.js index fe29c1a..7191880 100644 --- a/frontend/static/js/api/client.js +++ b/frontend/static/js/api/client.js @@ -1,8 +1,8 @@ import { Account } from "./account.js"; import { MatchMaking } from "./matchmaking.js"; -import { Profile } from "./profile.js"; import { Profiles } from "./profiles.js"; import { Channels } from './chat/channels.js'; +import { MyProfile } from "./MyProfile.js"; function getCookie(name) { @@ -94,15 +94,23 @@ class Client 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) { let response = await this._post("/api/accounts/login", {username: username, password: password}) let data = await response.json(); if (data.id != undefined) { - this.me = new Profile(this) - await this.me.init(data.id) - this.logged = true; + await this._update_logged(true); return null; } return data; @@ -111,7 +119,7 @@ class Client async logout() { await this._get("/api/accounts/logout"); - this.logged = false; + await this._update_logged(false); } async _test_logged() @@ -120,10 +128,7 @@ class Client let data = await response.json(); if (data.id !== undefined) - { - this.me = new Profile(this) - await this.me.init(data.id) - } + await this._update_logged(true); return data.id !== undefined; } } diff --git a/frontend/static/js/api/matchmaking.js b/frontend/static/js/api/matchmaking.js index 806349e..6943077 100644 --- a/frontend/static/js/api/matchmaking.js +++ b/frontend/static/js/api/matchmaking.js @@ -1,9 +1,9 @@ -import { client, navigateTo } from "../index.js" +import { Client } from "./client.js"; class MatchMaking { /** - * @param {client} client + * @param {Client} client */ constructor(client) { diff --git a/frontend/static/js/api/profile.js b/frontend/static/js/api/profile.js index 3f4aea7..dfa29e6 100644 --- a/frontend/static/js/api/profile.js +++ b/frontend/static/js/api/profile.js @@ -1,7 +1,15 @@ +import { Client } from "./client.js"; + class Profile { + /** + * @param {Client} client + */ constructor (client, username = undefined, avatar_url = undefined, user_id = undefined) { + /** + * @type {Client} client + */ this.client = client; this.username = username; this.avatar_url = avatar_url @@ -13,23 +21,10 @@ class Profile let response = await this.client._get(`/api/profiles/${user_id}`); let response_data = await response.json(); - this.user_id = user_id; + this.user_id = response.user_id; this.username = response_data.username; 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} \ No newline at end of file diff --git a/frontend/static/js/api/profiles.js b/frontend/static/js/api/profiles.js index c439abc..f093c62 100644 --- a/frontend/static/js/api/profiles.js +++ b/frontend/static/js/api/profiles.js @@ -2,8 +2,14 @@ import { Profile } from "./profile.js"; class Profiles { + /** + * @param {Client} client + */ constructor (client) { + /** + * @type {Client} client + */ this.client = client } diff --git a/frontend/static/js/index.js b/frontend/static/js/index.js index 3e378e9..b0d3107 100644 --- a/frontend/static/js/index.js +++ b/frontend/static/js/index.js @@ -79,6 +79,7 @@ const router = async (uri) => { lastView = view; + await client.isAuthentificate(); let content = await view.getHtml(); if (content == null) return 1; diff --git a/frontend/static/js/utils/formUtils.js b/frontend/static/js/utils/formUtils.js new file mode 100644 index 0000000..8b00592 --- /dev/null +++ b/frontend/static/js/utils/formUtils.js @@ -0,0 +1,18 @@ +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} \ No newline at end of file diff --git a/frontend/static/js/views/MeView.js b/frontend/static/js/views/MeView.js index 1b0c5a2..dd44b81 100644 --- a/frontend/static/js/views/MeView.js +++ b/frontend/static/js/views/MeView.js @@ -1,4 +1,5 @@ import { client, navigateTo } from "../index.js"; +import { clear, fill_errors } from "../utils/formUtils.js"; import AbstractAuthentificateView from "./AbstractAuthentifiedView.js"; export default class extends AbstractAuthentificateView @@ -10,63 +11,39 @@ export default class extends AbstractAuthentificateView async postInit() { - if (this.fill() === null) - return; - document.getElementById("save-account-button").onclick = this.acccount_save; - document.getElementById("delete-account-button").onclick = this.account_delete_accounts; + 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; } - async fill() + async delete_account() { - let data = await client.account.get(); - - if (data === null) - { - navigateTo("/login") - return; - } - document.getElementById("username").value = data.username; - } - - async delete_accounts() - { - let current_password = document.getElementById("current_password").value; + let current_password = document.getElementById("current_password-input").value; let response_data = await client.account.delete(current_password); - if (response_data === null) + console.log(await client.isAuthentificate()) + if (response_data === null || response_data === "user deleted") { navigateTo("/login"); return; } - - ["delete", "current_password"].forEach(error_field => { - let error_display = document.getElementById(`error_${error_field}`); - if (error_display != null) - error_display.innerHTML = ""; - }); - - if (response_data === "user deleted") - { - document.getElementById(`error_delete`).innerHTML = "OK"; - navigateTo("/login") - return; - } - - document.getElementById("error_current_password").innerHTML = response_data["password"] + clear("innerHTML", ["current_password-input"]) + fill_errors({"current_password-input": response_data["password"]}, "innerHTML") } - async save() + async save_account() { - let username = document.getElementById("username").value; - let new_password = document.getElementById("new_password").value; - let current_password = document.getElementById("current_password").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 = {}; 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) @@ -74,58 +51,55 @@ export default class extends AbstractAuthentificateView navigateTo("/login"); return; } - else if (response_data === "data has been alterate") - { - navigateTo("/me"); - return; - } + + 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") + } - ["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"); + async save_profile() + { + let avatar = document.getElementById("avatar-input"); if (avatar.files[0] !== undefined) { let form_data = new FormData(); 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() { return ` - +

ME

-
-

Account

- - - - - - - - - - +
+ +
+

Profile

+ + + +
+ Logout
-
-

Profile

- - - -
- Logout `; } } \ No newline at end of file diff --git a/profiles/urls.py b/profiles/urls.py index e984ab4..db385c2 100644 --- a/profiles/urls.py +++ b/profiles/urls.py @@ -5,7 +5,7 @@ from django.conf.urls.static import static from . import viewsets urlpatterns = [ - path("", viewsets.ProfileViewSet.as_view({'get': 'retrieve', 'patch': 'partial_update'}), name="profile_page"), + path("me", viewsets.MyProfileViewSet.as_view({'patch': 'partial_update', 'get': 'retrieve'}), name="my_profile_page"), + path("", viewsets.ProfileViewSet.as_view({'get': 'retrieve'}), name="profile_page"), 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") \ No newline at end of file diff --git a/profiles/viewsets.py b/profiles/viewsets.py index b324dda..74e9be8 100644 --- a/profiles/viewsets.py +++ b/profiles/viewsets.py @@ -17,9 +17,7 @@ class ProfileViewSet(viewsets.ModelViewSet): permission_classes = (permissions.IsAuthenticatedOrReadOnly,) def retrieve(self, request: HttpRequest, pk=None): - if (not ProfileModel.objects.filter(pk=pk).exists()): - return Response({"detail": "Profile not found."}, status=status.HTTP_404_NOT_FOUND) - instance = ProfileModel.objects.get(pk=pk) + instance = 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) @@ -33,13 +31,27 @@ class ProfileViewSet(viewsets.ModelViewSet): def perform_create(self, serializer): serializer.save(user=self.request.user) - def perform_update(self, serializer): - if (not ProfileModel.objects.filter(pk=pk).exists()): - return Response({"detail": "Profile not found."}, status=status.HTTP_404_NOT_FOUND) - profile: ProfileModel = ProfileModel.objects.get(pk=self.request.user.pk) +class MyProfileViewSet(viewsets.ModelViewSet): + + serializer_class = ProfileSerializer + 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) if (avatar is not None): if (profile.avatar_url.name != "./profiles/static/avatars/default.avif"): profile.avatar_url.storage.delete(profile.avatar_url.name) profile.avatar_url = avatar - profile.save() \ No newline at end of file + profile.save() + + def retrieve(self, request: HttpRequest, pk=None): + print("test") + 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) \ No newline at end of file From b5d73e59fd4c93a4250e3550a3ce98319a38e002 Mon Sep 17 00:00:00 2001 From: starnakin Date: Sat, 16 Dec 2023 17:41:31 +0100 Subject: [PATCH 2/2] core: simplified login and register, fix: me --- .../static/js/views/accounts/LoginView.js | 32 +++++++------------ .../static/js/views/accounts/RegisterView.js | 27 ++++++---------- profiles/viewsets.py | 3 ++ 3 files changed, 24 insertions(+), 38 deletions(-) diff --git a/frontend/static/js/views/accounts/LoginView.js b/frontend/static/js/views/accounts/LoginView.js index 133a000..6c81dc6 100644 --- a/frontend/static/js/views/accounts/LoginView.js +++ b/frontend/static/js/views/accounts/LoginView.js @@ -1,10 +1,11 @@ import { client, navigateTo } from "../../index.js"; +import { clear, fill_errors } from "../../utils/formUtils.js"; import AbstractNonAuthentifiedView from "../AbstractNonAuthentified.js"; async function login() { - let username = document.getElementById("username").value; - let password = document.getElementById("password").value; + let username = document.getElementById("username-input").value; + let password = document.getElementById("password-input").value; let response_data = await client.login(username, password); @@ -14,17 +15,8 @@ async function login() return; } - ["username", "user", "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]; - }); + clear("innerHTML", ["username", "user", "password"]); + fill_errors(response_data, "innerHTML"); } export default class extends AbstractNonAuthentifiedView { @@ -34,7 +26,7 @@ export default class extends AbstractNonAuthentifiedView { async postInit() { - document.getElementById("button").onclick = login; + document.getElementById("login-button").onclick = login; } async getHtml() { @@ -42,12 +34,12 @@ export default class extends AbstractNonAuthentifiedView {
- - - - - - + + + + + + Register
`; diff --git a/frontend/static/js/views/accounts/RegisterView.js b/frontend/static/js/views/accounts/RegisterView.js index f4e212c..7f52a7d 100644 --- a/frontend/static/js/views/accounts/RegisterView.js +++ b/frontend/static/js/views/accounts/RegisterView.js @@ -14,17 +14,8 @@ async function register() return; } - ["username", "user", "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]; - }); + clear("innerHTML", ["username", "user", "password"]); + fill_errors(response_data, "innerHTML"); } export default class extends AbstractNonAuthentifiedView { @@ -34,7 +25,7 @@ export default class extends AbstractNonAuthentifiedView { async postInit() { - document.getElementById("button").onclick = register; + document.getElementById("register-button").onclick = register; } async getHtml() { @@ -42,12 +33,12 @@ export default class extends AbstractNonAuthentifiedView {
- - - - - - + + + + + + Login
`; diff --git a/profiles/viewsets.py b/profiles/viewsets.py index 74e9be8..ecf1cd9 100644 --- a/profiles/viewsets.py +++ b/profiles/viewsets.py @@ -3,6 +3,7 @@ from rest_framework.parsers import MultiPartParser, FormParser from rest_framework import permissions, status from rest_framework import viewsets from rest_framework.response import Response +from rest_framework.authentication import SessionAuthentication from django.http import HttpRequest from django.db.models import QuerySet @@ -33,6 +34,8 @@ class ProfileViewSet(viewsets.ModelViewSet): class MyProfileViewSet(viewsets.ModelViewSet): + permission_classes = (permissions.IsAuthenticated,) + authentication_classes = (SessionAuthentication,) serializer_class = ProfileSerializer queryset = ProfileModel.objects.all