24 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
609b045315 better css, add debug 2023-12-18 21:26:18 +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
3be50e747d bug issue, null users_id creation channel 2023-12-16 17:00:38 +01:00
1f41e62a86 core: refonte profile 2023-12-16 16:42:30 +01:00
42 changed files with 536 additions and 324 deletions

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

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

@ -1,7 +1,8 @@
body { body {
margin: 10; margin: 0.5em;
font-family: 'Quicksand', sans-serif; font-family: 'Quicksand', sans-serif;
font-size: 3vh; 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

@ -1,20 +1,22 @@
#app img #app img
{ {
max-height: 10vh; max-height: 3em;
max-width: 10vh; max-width: 3em;
} }
#app ul #app ul
{ {
font-size: 2vh; font-size: 0.75em;
margin: 5px 0 0 0; margin: 0.25em 0 0 0;
padding: 0 0 0 0; padding: 0 0 0 0;
list-style-type: none; list-style-type: none;
max-height: 80vh;
overflow: auto;
} }
#app li #app li
{ {
margin: 10px 10px 0 0; margin: 0.25em 0.25em 0 0;
} }
#app #chats { #app #chats {
@ -23,34 +25,46 @@
text-align: left; text-align: left;
} }
#app #users { #app #users {
margin-right: 5vh; margin: 0em 1.0em 0em 0.05em;
} }
#app #chat { #app #chat {
position: relative; position: relative;
max-height: 80vh; max-height: 100vh;
width: 60%; width: 100vh;
/*border: 2px solid green;*/ /*border: 2px solid green;*/
overflow: hidden; overflow: hidden;
} }
#app #members {
font-size: 1em;
}
#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: 50vh; max-height: 60vh;
overflow: scroll; overflow: scroll;
overflow-y: scroll; overflow-y: scroll;
overflow-x: hidden; overflow-x: hidden;
font-size: 2vh; font-size: 0.75em;
} }
#app #input_user{ #app #input_user{
color: green; color: green;
width: 20vh; width: 8.5em;
height: 3vh; height: 1.1em;
font-size: 2vh; font-size: 0.65em;
border: none;
outline: none;
border-bottom: 0.15em solid green;
} }
#app #input_chat{ #app #input_chat{
@ -60,18 +74,18 @@
width: 100%; width: 100%;
border: none; border: none;
outline: none; outline: none;
border-bottom: 0.5vh solid green; border-bottom: 0.2em solid green;
caret-color: green; caret-color: green;
color: green; color: green;
font-size: 2vh; font-size: 16px;
} }
#app #you { #app #you {
text-align: left; text-align: left;
position: relative; position: relative;
max-width: 48%; max-width: 48%;
left: 10px; left: 0.5em;
margin: 5px 0 0 0; margin: 0.5em 0 0 0;
color: green; color: green;
word-wrap: break-word; word-wrap: break-word;
@ -81,8 +95,8 @@
text-align: right; text-align: right;
position: relative; position: relative;
max-width: 48%; max-width: 48%;
margin: 5px 0 0 auto; margin: 0.5em 0 0 auto;
right: 10px; right: 0.5em;
color: red; color: red;
/* permet le retour à la ligne à la place de dépasser*/ /* permet le retour à la ligne à la place de dépasser*/

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

@ -7,6 +7,15 @@ class Channels {
} }
async createChannel(users_id, reload) { async createChannel(users_id, reload) {
let null_id = false;
users_id.forEach(user_id => {
if (user_id == null)
null_id = true;
});
if (null_id)
return console.log(users_id, "createChannel error, null id;");
let response = await this.client._post("/api/chat/", { let response = await this.client._post("/api/chat/", {
users_id:users_id users_id:users_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,22 +22,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 = 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;
});
} }
} }

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) {

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) {

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)
{ {

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_account()
}
async save()
{ {
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 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"> <input type="button" value="Save Credentials" id="save-account-button">
<span id="error_save"></span> <span id="save-account"></span>
<input type="button" value="Delete Account" id="delete-account-button"> <input type="button" value="Delete Account" id="delete-account-button">
<span id="error_delete"></span> <span id="delete-account"></span>
</div> </div>
<div class="profile"> <div class="profile">
<h3>Profile</h3> <h3>Profile</h3>
<input type="file" id="avatar" accept="image/png, image/jpeg"> <input type="file" id="avatar-input" accept="image/png, image/jpeg">
<input type="button" value="Save profile" id="save-profile-button"> <input type="button" value="Save profile" id="save-profile-button">
<span id="error_save"></span> <span id="save-profile"></span>
</div> </div>
<a href="/logout" class="nav__link" data-link>Logout</a> <a href="/logout" class="nav__link" data-link>Logout</a>
</div>
`; `;
} }
} }

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"
@ -10,11 +10,12 @@ export default class extends AbstractView {
async postInit() { async postInit() {
let search = document.getElementById("input_user"); let search = document.getElementById("input_user");
search.addEventListener("input", this.users) search.oninput = this.users;
let chat_input = document.getElementById("input_chat"); let chat_input = document.getElementById("input_chat");
//chat_input.addEventListener("keydown", this.chat_manager) //chat_input.addEventListener("keydown", this.chat_manager)
this.last_add_chat = undefined;
this.users(); this.users();
this.chat(); this.chat();
@ -31,6 +32,12 @@ export default class extends AbstractView {
list_users.innerHTML = ""; list_users.innerHTML = "";
users.filter(user => user.username.toLowerCase().startsWith(search) == true).forEach((user) => { users.filter(user => user.username.toLowerCase().startsWith(search) == true).forEach((user) => {
if (user.user_id == null) {
console.log("list User one with id null;");
return;
}
var new_user = document.createElement("li"); var new_user = document.createElement("li");
// username // username
@ -46,36 +53,34 @@ export default class extends AbstractView {
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.id = "add_chat_off"; add_chat.id = "add_chat_off";
add_chat.addEventListener("click", async () => { add_chat.onclick = async () => {
add_chat.id = "add_chat_off";
if (client.channel != undefined) { if (client.channel != undefined) {
client.channel.members_id.forEach((member_id) => { client.channel.members_id.forEach((member_id) => {
if (member_id == user.user_id) if (member_id == user.user_id)
client.channel = undefined; client.channel = undefined;
}); });
if (client.channel == undefined) if (client.channel == undefined) {
add_chat.id = "add_chat_off";
this.last_add_chat = undefined;
return this.hideChat(); return this.hideChat();
}
client.channel.disconnect(); client.channel.disconnect();
} }
client.channel = await client.channels.createChannel([client.me.user_id , user.user_id], this.chat); client.channel = await client.channels.createChannel([client.me.user_id , user.user_id], this.chat);
this.chat(); this.chat();
if (this.last_add_chat != undefined)
this.last_add_chat.id = "add_chat_off";
this.last_add_chat = add_chat;
add_chat.id = "add_chat_on"; add_chat.id = "add_chat_on";
}); };
add_chat.appendChild(document.createTextNode("Chat")); add_chat.appendChild(document.createTextNode("Chat"));
new_user.appendChild(add_chat); new_user.appendChild(add_chat);
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
@ -95,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();
@ -115,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");
if (messages == null) {
messages = document.createElement("div");
messages.id = "messages"; messages.id = "messages";
if (document.getElementById("input_chat") == null) if (document.getElementById("input_chat") == null)
chat.appendChild(messages); chat.appendChild(messages);
else else
document.getElementById("input_chat").before(messages); 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) {
@ -133,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 = "";
@ -161,17 +191,9 @@ 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
}); messages.scrollTop = messages.scrollHeight;
} }
@ -200,7 +222,6 @@ export default class extends AbstractView {
</ul> </ul>
</div> </div>
</div> </div>
`; `;
} }
} }

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,7 +26,7 @@ export default class extends AbstractNonAuthentifiedView {
async postInit() async postInit()
{ {
document.getElementById("button").onclick = login; document.getElementById("login-button").onclick = login;
} }
async getHtml() { async getHtml() {
@ -42,12 +34,12 @@ export default class extends AbstractNonAuthentifiedView {
<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
{ {

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,7 +26,7 @@ export default class extends AbstractNonAuthentifiedView {
async postInit() async postInit()
{ {
document.getElementById("button").onclick = register; document.getElementById("register-button").onclick = register;
} }
async getHtml() { async getHtml() {
@ -42,12 +34,12 @@ export default class extends AbstractNonAuthentifiedView {
<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]}"
@ -18,3 +19,10 @@ 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"),
path("block/<int:pk>", views.BlockView.as_view(), name="block_page"),
] + static("/static/avatars/", document_root="./avatars") ] + 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,9 +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):
if (not ProfileModel.objects.filter(pk=pk).exists()): if (not self.queryset().filter(pk=pk).exists()):
return Response({"detail": "Profile not found."}, status=status.HTTP_404_NOT_FOUND) return Response({"detail": "Profile not found."}, status=status.HTTP_404_NOT_FOUND)
instance = ProfileModel.objects.get(pk=pk) 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)
@ -33,13 +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):
if (not ProfileModel.objects.filter(pk=pk).exists()):
return Response({"detail": "Profile not found."}, status=status.HTTP_404_NOT_FOUND) permission_classes = (permissions.IsAuthenticated,)
profile: ProfileModel = ProfileModel.objects.get(pk=self.request.user.pk) authentication_classes = (SessionAuthentication,)
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)