27 Commits

Author SHA1 Message Date
4fd6616786 add block option 2023-12-20 23:48:52 +01:00
57ed6165ea bug issue; rewrite all messages 2023-12-19 12:42:46 +01:00
acba77e228 Réarrangement du code; correction css; écriture uniquement du nouveau message 2023-12-19 11:27:18 +01:00
fb0f9be103 merge with Chatte; patch css 2023-12-18 22:37:05 +01:00
41a7a6dfb8 css patch 2023-12-18 22:35:42 +01:00
a10fdefd23 css patch 2023-12-18 22:29:40 +01:00
kdx
b139652fc5 fix css 2023-12-18 22:29:38 +01:00
c6f6e216f2 css patch 2023-12-18 22:27:17 +01:00
kdx
7479088254 fix css 2023-12-18 22:27:02 +01:00
651fbbd67f css patch 2023-12-18 22:18:49 +01:00
778d10d22f fix: client.me 2 2023-12-18 21:41:00 +01:00
1961047703 merge with Chatte 2023-12-18 21:29:35 +01:00
929c1fdeb3 merge with Chatte 2023-12-18 21:27:52 +01:00
980a66fb47 fix: client.me 2023-12-18 21:27:10 +01:00
ceed7c2c4a fix: profile page 2 2023-12-17 20:29:04 +01:00
6a537a9b68 fix: profile work 2023-12-17 20:27:13 +01:00
26a9152756 fix: register view 2023-12-17 20:17:52 +01:00
f32d38287a tentative de merge 2023-12-16 18:02:07 +01:00
51354d9922 tentative de merge 2023-12-16 18:00:38 +01:00
b5d73e59fd core: simplified login and register, fix: me 2023-12-16 17:41:31 +01:00
86e2528d04 merge with Chatte 2023-12-16 17:01:03 +01:00
1f41e62a86 core: refonte profile 2023-12-16 16:42:30 +01:00
51059e6bf3 merge with Chatte 2023-12-16 16:15:13 +01:00
115ae9357a fix: profile not found GET return error 2023-12-16 11:06:24 +01:00
1b44d5c94f Merge remote-tracking branch 'refs/remotes/origin/server' into server 2023-12-16 10:58:11 +01:00
468a3917a9 fix css file to /login /register 2023-12-16 10:39:07 +01:00
9be65a2bab Update README.md 2023-12-16 10:35:52 +01:00
41 changed files with 487 additions and 306 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 runserver makemigrations chat python manage.py makemigrations chat
python manage.py migrate python manage.py migrate
``` ```
- Start the developpement server - Start the developpement server

View File

@ -2,6 +2,7 @@ import json
from channels.generic.websocket import WebsocketConsumer from channels.generic.websocket import WebsocketConsumer
from asgiref.sync import async_to_sync from asgiref.sync import async_to_sync
from .models import MemberModel, MessageModel from .models import MemberModel, MessageModel
from profiles.models import BlockModel
import time import time
class ChatConsumer(WebsocketConsumer): class ChatConsumer(WebsocketConsumer):
@ -34,6 +35,9 @@ class ChatConsumer(WebsocketConsumer):
text_data_json = json.loads(text_data) text_data_json = json.loads(text_data)
message = text_data_json['message'] message = text_data_json['message']
receivers_id = text_data_json['receivers_id']
print(text_data)
channel_id : int = int(self.scope['path'].split('/')[3]) channel_id : int = int(self.scope['path'].split('/')[3])
user = self.scope["user"] user = self.scope["user"]
@ -46,9 +50,14 @@ class ChatConsumer(WebsocketConsumer):
if (self.channel_layer == None): if (self.channel_layer == None):
return return
print(message)
message_time : int = int(time.time() * 1000) message_time : int = int(time.time() * 1000)
if (len(receivers_id) == 1 and
BlockModel.objects.filter(blocker=user.pk, blocked=receivers_id[0]) or
BlockModel.objects.filter(blocker=receivers_id[0], blocked=user.pk)
):
return
async_to_sync(self.channel_layer.group_send)( async_to_sync(self.channel_layer.group_send)(
self.room_group_name, self.room_group_name,
{ {

View File

@ -20,5 +20,5 @@ class MessageModel(models.Model):
content = models.CharField(max_length=255) content = models.CharField(max_length=255)
time = IntegerField(primary_key=False) time = IntegerField(primary_key=False)
def __str__(self): def __str__(self):
return "author_id: " + str(self.author_id) + ", channel_id: " + str(self.channel_id) + ", content: " + self.content return "author_id: " + str(self.author_id) + ", channel_id: " + str(self.channel_id) + ", content: " + self.content

View File

@ -11,9 +11,9 @@ class ChatView(APIView):
def get(self, request, pk): def get(self, request, pk):
if (ChannelModel.objects.filter(pk=pk)): if (ChannelModel.objects.filter(pk=pk)):
return Response({'channel_id': pk}) return Response({'channel_id': pk}, status=status.HTTP_200_OK)
else: else:
return Response("Channel doesn't exist") return Response("Channel doesn't exist", status=status.HTTP_404_NOT_FOUND)
def delete(self, request, pk): def delete(self, request, pk):
@ -21,28 +21,7 @@ class ChatView(APIView):
MessageModel.objects.filter(pk=pk).delete() MessageModel.objects.filter(pk=pk).delete()
MemberModel.objects.filter(pk=pk).delete() MemberModel.objects.filter(pk=pk).delete()
return Response({'channel_id': pk}) return Response({'channel_id': pk}, status=status.HTTP_200_OK)
"""
def post(self, request, pk):
channel = ChannelModel.objects.filter(pk=pk)
message = request.data.get("message", [])
print(message)
if (message == []):
return Response('No message', status=status.HTTP_400_BAD_REQUEST)
new_message = MessageModel()
new_message.channel_id = message["channel_id"]
new_message.author_id = message["author_id"]
new_message.content = message["content"]
new_message.time = message["time"]
new_message.save()
messages = MessageModel.objects.filter(channel_id=pk)
messages = serializers.serialize("json", messages)
return Response({'messages':messages}, status=status.HTTP_200_OK)
"""
class ChatsView(APIView): class ChatsView(APIView):
def post(self, request): def post(self, request):

View File

@ -9,4 +9,4 @@
margin-right: auto; margin-right: auto;
margin-top: 90px; margin-top: 90px;
border: 15px black solid; border: 15px black solid;
} }

View File

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

View File

@ -1,19 +1,18 @@
#app .account #app #main .account
{ {
background-color: red; background-color: red;
} }
#app .account, #app .profile #app #main
{ {
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 .profile #app #main .profile
{ {
background-color: green; background-color: green;
} }

View File

@ -0,0 +1,20 @@
#app #avatar
{
height: 100px;
width: 100px;
}
#app #username
{
font-size: 0.8em;
}
#app #block {
cursor: pointer;
font-size: 0.7em;
text-decoration: underline;
}
#app {
margin-top: 20px;
}

View File

@ -1,11 +0,0 @@
#app #avatar
{
height: 100px;
width: 100px;
}
#app #username
{
height: 100px;
width: 100px;
}

View File

@ -1,11 +0,0 @@
#app .item img
{
height: 100px;
width: 100px;
}
#app .item a
{
height: 100px;
width: 100px;
}

View File

@ -30,7 +30,7 @@
#app #chat { #app #chat {
position: relative; position: relative;
max-height: 86vh; max-height: 100vh;
width: 100vh; width: 100vh;
/*border: 2px solid green;*/ /*border: 2px solid green;*/
overflow: hidden; overflow: hidden;
@ -42,21 +42,26 @@
#app #add_chat_off { #app #add_chat_off {
text-decoration: underline; text-decoration: underline;
cursor: pointer;
}
#app #add_chat_on {
cursor: pointer;
} }
#app #messages { #app #messages {
max-height: 50em; max-height: 60vh;
overflow: scroll; overflow: scroll;
overflow-y: scroll; overflow-y: scroll;
overflow-x: hidden; overflow-x: hidden;
font-size: 0.6em; font-size: 0.75em;
} }
#app #input_user{ #app #input_user{
color: green; color: green;
width: 8.5em; width: 8.5em;
height: 1.1em; height: 1.1em;
font-size: 0.75em; font-size: 0.65em;
border: none; border: none;
outline: none; outline: none;
border-bottom: 0.15em solid green; border-bottom: 0.15em solid green;
@ -72,7 +77,7 @@
border-bottom: 0.2em solid green; border-bottom: 0.2em solid green;
caret-color: green; caret-color: green;
color: green; color: green;
font-size: 0.65em; font-size: 16px;
} }
#app #you { #app #you {

View File

@ -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}

View File

@ -1,7 +1,15 @@
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;
} }
@ -28,7 +36,6 @@ 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;
@ -55,7 +62,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._logged = false; this.client._;
return null; return null;
} }
return response_data; return response_data;

View File

@ -54,13 +54,14 @@ class Channel {
return new_messages; return new_messages;
} }
async sendMessageChannel(message) { async sendMessageChannel(message, receivers_id) {
if (this.chatSocket == undefined) if (this.chatSocket == undefined)
return; return;
this.chatSocket.send(JSON.stringify({ this.chatSocket.send(JSON.stringify({
'message':message 'message':message,
'receivers_id':receivers_id,
})); }));
} }

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)
{ {
@ -35,10 +35,11 @@ class Client
return this.logged; return this.logged;
} }
async _get(uri) async _get(uri, data)
{ {
let response = await fetch(this._url + uri, { let response = await fetch(this._url + uri, {
method: "GET", method: "GET",
body: JSON.stringify(data),
}); });
return response; return response;
} }
@ -94,15 +95,23 @@ 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)
{ {
this.me = new Profile(this) await this._update_logged(true);
await this.me.init(data.id)
this.logged = true;
return null; return null;
} }
return data; return data;
@ -111,7 +120,7 @@ class Client
async logout() async logout()
{ {
await this._get("/api/accounts/logout"); await this._get("/api/accounts/logout");
this.logged = false; await this._update_logged(false);
} }
async _test_logged() async _test_logged()
@ -120,10 +129,7 @@ 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, navigateTo } from "../index.js" import { Client } from "./client.js";
class MatchMaking class MatchMaking
{ {
/** /**
* @param {client} client * @param {Client} client
*/ */
constructor(client) constructor(client)
{ {

View File

@ -1,11 +1,20 @@
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;
this.user_id = user_id this.user_id = user_id;
this.isBlocked = false;
} }
async init(user_id) async init(user_id)
@ -13,23 +22,24 @@ 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 = user_id; this.user_id = response_data.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 block_response = await this.client._get("/api/profiles/block");
{
let response = await this.client._patch_file(`/api/profiles/${this.user_id}`, form_data);
let response_data = await response.json()
return response_data; if (block_response.status == 404)
} return
async setData (data) let block_data = await block_response.json();
{ let block_list = JSON.parse(block_data);
block_list.forEach(block => {
let blocker = block.fields.blocker;
let blocked = block.fields.blocked;
if (blocker == this.client.me.user_id && blocked == user_id)
return this.isBlocked = true;
});
} }
} }
export {Profile} export {Profile}

View File

@ -2,8 +2,14 @@ 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
} }
@ -25,6 +31,34 @@ class Profiles
await profile.init(user_id); await profile.init(user_id);
return profile; return profile;
} }
async block(user_id) {
// blocker & blocked
let response = await this.client._post("/api/profiles/block", {
users_id:[this.client.me.user_id, user_id],
});
let data = await response.json();
console.log(response.status);
console.log(data);
return data;
}
async deblock(user_id) {
// blocker & blocked
let response = await this.client._delete("/api/profiles/block", {
users_id:[this.client.me.user_id, user_id],
});
let data = await response.json();
console.log(response.status);
console.log(data);
return data;
}
} }
export {Profiles} export {Profiles}

View File

@ -10,9 +10,9 @@ import GameView from "./views/Game.js"
import PageNotFoundView from './views/PageNotFoundView.js' import PageNotFoundView from './views/PageNotFoundView.js'
import AbstractRedirectView from "./views/AbstractRedirectView.js"; import AbstractRedirectView from "./views/abstracts/AbstractRedirectView.js";
import MeView from "./views/MeView.js"; import MeView from "./views/MeView.js";
import ProfilePageView from "./views/profiles/ProfilePageView.js"; import ProfilePageView from "./views/ProfilePageView.js";
import MatchMakingView from "./views/MatchMakingView.js"; import MatchMakingView from "./views/MatchMakingView.js";
let client = new Client(location.protocol + "//" + location.host) let client = new Client(location.protocol + "//" + location.host)
@ -79,6 +79,7 @@ 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

@ -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}

View File

@ -1,4 +1,4 @@
import AbstractView from "./AbstractView.js"; import AbstractView from "./abstracts/AbstractView.js";
export default class extends AbstractView { export default class extends AbstractView {
constructor(params) { constructor(params) {
@ -16,4 +16,4 @@ export default class extends AbstractView {
</p> </p>
`; `;
} }
} }

View File

@ -1,4 +1,4 @@
import AbstractView from './AbstractView.js' import AbstractView from "./abstracts/AbstractView.js";
export default class extends AbstractView { export default class extends AbstractView {
constructor(params) { constructor(params) {

View File

@ -1,4 +1,4 @@
import AbstractAuthentificateView from "./AbstractAuthentifiedView.js"; import AbstractAuthentificateView from "./abstracts/AbstractAuthentifiedView.js";
export default class extends AbstractAuthentificateView { export default class extends AbstractAuthentificateView {
constructor(params) { constructor(params) {
@ -15,4 +15,4 @@ export default class extends AbstractAuthentificateView {
<a href="/logout" class="nav__link" data-link>Logout</a> <a href="/logout" class="nav__link" data-link>Logout</a>
`; `;
} }
} }

View File

@ -1,5 +1,5 @@
import { client, navigateTo } from "../index.js"; import { client, navigateTo } from "../index.js";
import AbstractView from "./AbstractView.js"; import AbstractView from "./abstracts/AbstractView.js";
function game_found(game_id) function game_found(game_id)
{ {
@ -26,4 +26,4 @@ export default class extends AbstractView {
{ {
await client.matchmaking.stop(); await client.matchmaking.stop();
} }
} }

View File

@ -1,5 +1,6 @@
import { client, navigateTo } from "../index.js"; import { client, navigateTo } from "../index.js";
import AbstractAuthentificateView from "./AbstractAuthentifiedView.js"; import { clear, fill_errors } from "../utils/formUtils.js";
import AbstractAuthentificateView from "./abstracts/AbstractAuthentifiedView.js";
export default class extends AbstractAuthentificateView export default class extends AbstractAuthentificateView
{ {
@ -10,63 +11,39 @@ export default class extends AbstractAuthentificateView
async postInit() async postInit()
{ {
if (this.fill() === null) document.getElementById("save-account-button").onclick = this.save_account;
return; document.getElementById("delete-account-button").onclick = this.delete_account;
document.getElementById("save-account-button").onclick = this.acccount_save; document.getElementById("save-profile-button").onclick = this.save_profile;
document.getElementById("delete-account-button").onclick = this.account_delete_accounts;
} }
async fill() async delete_account()
{ {
let data = await client.account.get(); let current_password = document.getElementById("current_password-input").value;
if (data === null)
{
navigateTo("/login")
return;
}
document.getElementById("username").value = data.username;
}
async delete_accounts()
{
let current_password = document.getElementById("current_password").value;
let response_data = await client.account.delete(current_password); 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"); navigateTo("/login");
return; return;
} }
clear("innerHTML", ["current_password-input"])
["delete", "current_password"].forEach(error_field => { fill_errors({"current_password-input": response_data["password"]}, "innerHTML")
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"]
} }
async save() async save_account()
{ {
let username = document.getElementById("username").value; let username = document.getElementById("username-input").value;
let new_password = document.getElementById("new_password").value; let new_password = document.getElementById("new_password-input").value;
let current_password = document.getElementById("current_password").value; let current_password = document.getElementById("current_password-input").value;
let data = {}; let data = {};
data.username = username; data.username = username;
if (new_password.length != 0) if (new_password.length != 0)
data.new_password = new_password; data.new_password = new_password;
let response_data = await client.account.update(data, current_password); let response_data = await client.account.update(data, current_password);
if (response_data === null) if (response_data === null)
@ -74,58 +51,55 @@ export default class extends AbstractAuthentificateView
navigateTo("/login"); navigateTo("/login");
return; return;
} }
else if (response_data === "data has been alterate")
{ if (response_data === "data has been alterate")
navigateTo("/me"); response_data = {"save-account": "saved"}
return;
} clear("innerHTML", ["username", "new_password", "current_password", "save-account", "delete-account"])
fill_errors(response_data, "innerHTML")
}
["username", "new_password", "current_password"].forEach(error_field => { async save_profile()
let error_display = document.getElementById(`error_${error_field}`); {
if (error_display != null) let avatar = document.getElementById("avatar-input");
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 class="account"> <div id="main">
<h3>Account</h3> <div class="account">
<input type="text" placeholder="username" id="username"> <h3>Account</h3>
<span id="error_username"></span> <input type="text" placeholder="username" id="username-input" text=${client.me.username}>
<input type=password placeholder="new password" id="new_password"> <span id="username"></span>
<span id="error_new_password"></span> <input type=password placeholder="new_password" id="new_password-input">
<input type=password placeholder="current password" id="current_password"> <span id="new_password"></span>
<span id="error_current_password"></span> <input type=password placeholder="current_password" id="current_password-input">
<input type="button" value="Save Credentials" id="save-account-button"> <span id="current_password"></span>
<span id="error_save"></span>
<input type="button" value="Delete Account" id="delete-account-button"> <input type="button" value="Save Credentials" id="save-account-button">
<span id="error_delete"></span> <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/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

@ -1,4 +1,4 @@
import AbstractView from "./AbstractView.js"; import AbstractView from "./abstracts/AbstractView.js";
export default class extends AbstractView { export default class extends AbstractView {
constructor(params) { constructor(params) {

View File

@ -0,0 +1,62 @@
import AbstractView from "./abstracts/AbstractView.js";
import { client } from "../index.js"
export default class extends AbstractView {
constructor(params) {
super(params, "Profile ");
this.user_id = params.id;
}
async postInit()
{
this.profile = await client.profiles.getProfile(this.user_id);
this.info = document.getElementById("info");
// Username
let username = document.createElement("a");
username.id = "username";
username.appendChild(document.createTextNode(this.profile.username));
this.info.appendChild(username);
this.info.appendChild(document.createElement("br"));
// Avatar
let avatar = document.createElement("img");
avatar.id = "avatar";
avatar.src = this.profile.avatar_url;
this.info.appendChild(avatar);
await this.blockButton();
}
async blockButton() {
// Block option
if (client.me.user_id != this.user_id) {
let block = document.getElementById("block") || document.createElement("a");
block.id = "block";
block.innerText = "";
block.onclick = async () => {
if (!this.profile.isBlocked)
await client.profiles.block(this.user_id);
else
await client.profiles.deblock(this.user_id);
this.profile = await client.profiles.getProfile(this.user_id);
this.blockButton();
};
if (this.profile.isBlocked)
block.appendChild(document.createTextNode("Deblock"));
else
block.appendChild(document.createTextNode("Block"));
this.info.appendChild(block);
}
}
async getHtml() {
return `
<link rel="stylesheet" href="/static/css/profile.css">
<div id="info">
</div>
`;
}
}

View File

@ -1,4 +1,4 @@
import AbstractView from "./AbstractView.js"; import AbstractView from "./abstracts/AbstractView.js";
import {client} from "../index.js"; import {client} from "../index.js";
import {Message} from "../api/chat/message.js" import {Message} from "../api/chat/message.js"
@ -81,13 +81,6 @@ export default class extends AbstractView {
new_user.appendChild(document.createTextNode(" ")); new_user.appendChild(document.createTextNode(" "));
let block = document.createElement("a");
block.addEventListener("click", async () => {
if (client.me.user_id != user.user_id) {
}
});
block.appendChild(document.createTextNode("Block"));
new_user.appendChild(block);
} }
// break line // break line
@ -107,12 +100,13 @@ export default class extends AbstractView {
async chat() { async chat() {
let users = await client.profiles.all();
let logged = await client.isAuthentificate(); let logged = await client.isAuthentificate();
let reload = document.getElementById("messages"); /*let reload = document.getElementById("messages");
if (reload != null) if (reload != null)
reload.remove(); reload.remove();*/
reload = document.getElementById("members"); let reload = document.getElementById("members");
if (reload != null) if (reload != null)
reload.remove(); reload.remove();
@ -127,13 +121,33 @@ export default class extends AbstractView {
chats.appendChild(chat); chats.appendChild(chat);
} }
// div des messages // div des messages
let messages = document.createElement("div"); let messages = document.getElementById("messages");
messages.id = "messages"; if (messages == null) {
if (document.getElementById("input_chat") == null) messages = document.createElement("div");
chat.appendChild(messages); messages.id = "messages";
else if (document.getElementById("input_chat") == null)
document.getElementById("input_chat").before(messages); chat.appendChild(messages);
else
document.getElementById("input_chat").before(messages);
}
// les messages, réecriture seulement du dernier
let i = 0;
client.channel.messages.forEach((message) => {
if (messages.children[i] == null || message.content != messages.children[i].innerText) {
let text = document.createElement("p");
text.appendChild(document.createTextNode(message.content));
if (message.author_id == client.me.user_id)
text.id = "you";
else
text.id = "other";
messages.appendChild(text);
}
i++;
});
// Input pour rentrer un message // Input pour rentrer un message
if (document.getElementById("input_chat") == null) { if (document.getElementById("input_chat") == null) {
@ -145,21 +159,25 @@ export default class extends AbstractView {
chat_input.maxLength=255; chat_input.maxLength=255;
chat.appendChild(chat_input); chat.appendChild(chat_input);
chat_input.addEventListener("keydown", async () => { chat_input.onkeydown = async () => {
if (event.keyCode == 13 && client.channel != undefined) { if (event.keyCode == 13 && client.channel != undefined) {
//let chat_input = document.getElementById("input_chat"); //let chat_input = document.getElementById("input_chat");
let chat_text = chat_input.value; let chat_text = chat_input.value;
await client.channel.sendMessageChannel(chat_text) let receivers_id = [];
client.channel.members_id.forEach((member_id) => {
if (member_id != client.me.user_id)
receivers_id.push(users.filter(user => user.user_id == member_id)[0].user_id);
});
await client.channel.sendMessageChannel(chat_text, receivers_id)
// Reset // Reset
chat_input.value = ""; chat_input.value = "";
} }
}); };
} }
// nom des membres du chat // nom des membres du chat
let users = await client.profiles.all();
let members = document.createElement("h2"); let members = document.createElement("h2");
members.id = "members"; members.id = "members";
let usernames = ""; let usernames = "";
@ -173,17 +191,6 @@ export default class extends AbstractView {
members.appendChild(document.createTextNode(usernames)); members.appendChild(document.createTextNode(usernames));
messages.before(members); messages.before(members);
// les messages
client.channel.messages.forEach((message) => {
let text = document.createElement("p");
text.appendChild(document.createTextNode(message.content));
if (message.author_id == client.me.user_id)
text.id = "you";
else
text.id = "other";
messages.appendChild(text);
});
// Scroll to the bottom of messages // Scroll to the bottom of messages
messages.scrollTop = messages.scrollHeight; messages.scrollTop = messages.scrollHeight;

View File

@ -1,4 +1,4 @@
import { client, navigateTo } from "../index.js"; import { client, navigateTo } from "../../index.js";
import AbstractRedirectView from "./AbstractRedirectView.js"; import AbstractRedirectView from "./AbstractRedirectView.js";
export default class extends AbstractRedirectView{ export default class extends AbstractRedirectView{

View File

@ -1,4 +1,4 @@
import { client, navigateTo } from "../index.js"; import { client, navigateTo } from "../../index.js";
import AbstractRedirectView from "./AbstractRedirectView.js"; import AbstractRedirectView from "./AbstractRedirectView.js";
export default class extends AbstractRedirectView{ export default class extends AbstractRedirectView{

View File

@ -1,4 +1,4 @@
import { navigateTo } from "../index.js"; import { navigateTo } from "../../index.js";
import AbstractView from "./AbstractView.js"; import AbstractView from "./AbstractView.js";
export default class extends AbstractView{ export default class extends AbstractView{

View File

@ -1,10 +1,11 @@
import { client, navigateTo } from "../../index.js"; import { client, navigateTo } from "../../index.js";
import AbstractNonAuthentifiedView from "../AbstractNonAuthentified.js"; import { clear, fill_errors } from "../../utils/formUtils.js";
import AbstractNonAuthentifiedView from "../abstracts/AbstractNonAuthentified.js";
async function login() async function login()
{ {
let username = document.getElementById("username").value; let username = document.getElementById("username-input").value;
let password = document.getElementById("password").value; let password = document.getElementById("password-input").value;
let response_data = await client.login(username, password); let response_data = await client.login(username, password);
@ -14,17 +15,8 @@ async function login()
return; return;
} }
["username", "user", "password"].forEach(error_field => { clear("innerHTML", ["username", "user", "password"]);
let error_display = document.getElementById(`error_${error_field}`); fill_errors(response_data, "innerHTML");
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 {
@ -34,22 +26,22 @@ export default class extends AbstractNonAuthentifiedView {
async postInit() async postInit()
{ {
document.getElementById("button").onclick = login; document.getElementById("login-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" placeholder="username"> <input type="text" id="username-input" placeholder="username">
<span id="error_username"></span> <span id="username"></span>
<input type="password" id="password" placeholder="password"> <input type="password" id="password-input" placeholder="password">
<span id="error_password"></span> <span id="password"></span>
<input type="button" value="login" id="button"> <input type="button" value="Login" id="login-button">
<span id="error_user"></span> <span id="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,5 +1,5 @@
import { client, navigateTo } from "../../index.js"; import { client, navigateTo } from "../../index.js";
import AbstractAuthentifiedView from "../AbstractAuthentifiedView.js"; import AbstractAuthentifiedView from "../abstracts/AbstractAuthentifiedView.js";
export default class extends AbstractAuthentifiedView export default class extends AbstractAuthentifiedView
{ {
@ -8,4 +8,4 @@ export default class extends AbstractAuthentifiedView
client.logout(); client.logout();
navigateTo("/login") navigateTo("/login")
} }
} }

View File

@ -1,10 +1,11 @@
import { client, navigateTo } from "../../index.js"; import { client, navigateTo } from "../../index.js";
import AbstractNonAuthentifiedView from "../AbstractNonAuthentified.js"; import { clear, fill_errors } from "../../utils/formUtils.js";
import AbstractNonAuthentifiedView from "../abstracts/AbstractNonAuthentified.js";
async function register() async function register()
{ {
let username = document.getElementById("username").value; let username = document.getElementById("username-input").value;
let password = document.getElementById("password").value; let password = document.getElementById("password-input").value;
let response_data = await client.account.create(username, password); let response_data = await client.account.create(username, password);
@ -14,17 +15,8 @@ async function register()
return; return;
} }
["username", "user", "password"].forEach(error_field => { clear("innerHTML", ["username", "user", "password"]);
let error_display = document.getElementById(`error_${error_field}`); fill_errors(response_data, "innerHTML");
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 {
@ -34,20 +26,20 @@ export default class extends AbstractNonAuthentifiedView {
async postInit() async postInit()
{ {
document.getElementById("button").onclick = register; document.getElementById("register-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" placeholder="username"> <input type="text" id="username-input" placeholder="username">
<span id="error_username"></span> <span id="username"></span>
<input type="password" id="password" placeholder="password"> <input type="password" id="password-input" placeholder="password">
<span id="error_password"></span> <span id="password"></span>
<input type="button" value="register" id="button"> <input type="button" value="Register" id="register-button">
<span id="error_user"></span> <span id="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

@ -1,29 +0,0 @@
import AbstractView from "../AbstractView.js";
import { client } from "../../index.js"
export default class extends AbstractView {
constructor(params) {
super(params, "Profile ");
this.user_id = params.id;
}
async postInit()
{
let profile = await client.profiles.getProfile(this.user_id);
let username_element = document.getElementById("username");
username_element.href = `/profiles/${this.user_id}`;
username_element.appendChild(document.createTextNode(profile.username));
let avatar_element = document.getElementById("avatar");
avatar_element.src = profile.avatar_url;
}
async getHtml() {
return `
<link rel="stylesheet" href="/static/css/profiles/profile.css">
<img id="avatar"/>
<a id="username"></a>
`;
}
}

View File

@ -1,6 +1,7 @@
from django.contrib import admin from django.contrib import admin
from .models import ProfileModel from .models import ProfileModel, BlockModel
# Register your models here. # Register your models here.
admin.site.register(ProfileModel) admin.site.register(ProfileModel)
admin.site.register(BlockModel)

View File

@ -4,6 +4,7 @@ from django.contrib.auth.models import User
from django.db.models.signals import post_save from django.db.models.signals import post_save
from django.dispatch import receiver from django.dispatch import receiver
from django.conf import settings from django.conf import settings
from django.db.models import IntegerField
def upload_to(instance, filename: str): def upload_to(instance, filename: str):
return f"./profiles/static/avatars/{instance.pk}.{filename.split('.')[1]}" return f"./profiles/static/avatars/{instance.pk}.{filename.split('.')[1]}"
@ -17,4 +18,11 @@ class ProfileModel(models.Model):
def on_user_created(sender, instance, created, **kwargs): def on_user_created(sender, instance, created, **kwargs):
if created: if created:
profile: ProfileModel = ProfileModel.objects.create(pk = instance.pk, user = instance) profile: ProfileModel = ProfileModel.objects.create(pk = instance.pk, user = instance)
profile.save() profile.save()
class BlockModel(models.Model):
blocker = IntegerField(primary_key=False)
blocked = IntegerField(primary_key=False)
def __str__(self):
return "blocker_id: " + str(self.blocker) + ", blocked_id: " + str(self.blocked)

View File

@ -3,9 +3,13 @@ from django.conf import settings
from django.conf.urls.static import static from django.conf.urls.static import static
from . import viewsets from . import viewsets
from . import views
urlpatterns = [ urlpatterns = [
path("<int:pk>", 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("<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"), path("block", views.BlocksView.as_view(), name="block_page"),
] + static("/static/avatars/", document_root="./avatars") path("block/<int:pk>", views.BlockView.as_view(), name="block_page"),
] + static("/static/avatars/", document_root="./avatars")

66
profiles/views.py Normal file
View File

@ -0,0 +1,66 @@
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import authentication, permissions, status
from rest_framework.authentication import SessionAuthentication
from rest_framework.renderers import JSONRenderer
from django.core import serializers
from .models import BlockModel
class BlockView(APIView):
permission_classes = (permissions.IsAuthenticated,)
authentication_classes = (SessionAuthentication,)
def get(self, request, pk):
block = BlockModel.objects.filter(pk=pk)
if (block):
return Response(serializers.serialize("json", block), status=status.HTTP_200_OK)
else:
return Response("Not Found", status=status.HTTP_404_NOT_FOUND)
class BlocksView(APIView):
permission_classes = (permissions.IsAuthenticated,)
authentication_classes = (SessionAuthentication,)
def get(self, request):
blocks = BlockModel.objects.filter(blocker=request.user.pk)
if (blocks):
return Response(serializers.serialize("json", BlockModel.objects.filter(blocker=request.user.pk)), status=status.HTTP_200_OK)
else:
return Response({}, status=status.HTTP_404_NOT_FOUND)
def post(self, request):
data: dict = request.data
users_id = request.data.get("users_id", None)
if (users_id == None):
return Response({"Error"}, status=status.HTTP_400_BAD_REQUEST)
if (BlockModel.objects.filter(blocker=users_id[0], blocked=users_id[1])):
return Response({"Already Exist"}, status=status.HTTP_409_CONFLICT)
new_block = BlockModel()
new_block.blocker = users_id[0]
new_block.blocked = users_id[1]
new_block.save()
return Response({"block_id": new_block.pk}, status=status.HTTP_201_CREATED)
def delete(self, request):
data: dict = request.data
users_id = request.data.get("users_id", None)
if (users_id == None):
return Response({"Error"}, status=status.HTTP_400_BAD_REQUEST)
block = BlockModel.objects.filter(blocker=users_id[0], blocked=users_id[1])
print(list(block))
if (block.count() > 1):
return Response("Not normal >:[", status=status.HTTP_500_INTERNAL_SERVER_ERROR)
if (not block):
return Response("Don't exist", status=status.HTTP_404_NOT_FOUND)
block.delete()
return Response("Deleted", status=status.HTTP_200_OK)

View File

@ -3,6 +3,7 @@ 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
@ -17,7 +18,9 @@ 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):
instance = ProfileModel.objects.get(pk=pk) if (not self.queryset().filter(pk=pk).exists()):
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)
@ -31,14 +34,28 @@ 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)
def perform_update(self, serializer): class MyProfileViewSet(viewsets.ModelViewSet):
query: QuerySet = ProfileModel.objects.filter(pk=self.request.user.pk)
if (not query.exists()): permission_classes = (permissions.IsAuthenticated,)
return Response("profile not found", status=status.HTTP_400_BAD_REQUEST) authentication_classes = (SessionAuthentication,)
profile: ProfileModel = ProfileModel.objects.get(pk=self.request.user.pk) 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) 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)