diff --git a/chat/models.py b/chat/models.py index 3045bd0..0392179 100644 --- a/chat/models.py +++ b/chat/models.py @@ -1,40 +1,41 @@ -from django.db import models -from django.db.models import IntegerField +from django.db.models import Model, IntegerField, ForeignKey, CharField, CASCADE +from django.db.models.signals import post_delete +from django.dispatch import receiver from django.contrib.auth.models import User -from django.contrib import admin -# Create your models here. -class ChatChannelModel(models.Model): - - def create(self, users_id: [int]): + +class ChatChannelModel(Model): + def create(self, members: [User]): self.save() - for user_id in users_id: - ChatMemberModel(channel_id = self.pk, member_id = user_id).save() - return self.pk + for member in members: + ChatMemberModel(channel=self, member=member).save() + return self - def get_members_id(self): - return [member_channel.member_id for member_channel in ChatMemberModel.objects.filter(channel_id = self.pk)] + def get_members(self): + return [member_channel.member for member_channel in ChatMemberModel.objects.filter(channel=self)] -class ChatMemberModel(models.Model): - member_id = IntegerField(primary_key=False) - channel_id = IntegerField(primary_key=False) - def __str__(self): - return "member_id: " + str(self.member_id) + ", channel_id: " + str(self.channel_id) +class ChatMemberModel(Model): + member = ForeignKey(User, on_delete=CASCADE) + channel = ForeignKey(ChatChannelModel, on_delete=CASCADE) -class ChatMessageModel(models.Model): - channel_id = IntegerField(primary_key=False) - author_id = IntegerField(primary_key=False) - content = models.CharField(max_length=255) + +@receiver(post_delete, sender=ChatMemberModel) +def delete_channel_when_member_deleted(sender, instance, **kwargs): + print(sender, instance) + + +class ChatMessageModel(Model): + channel = ForeignKey(ChatChannelModel, on_delete=CASCADE) + author = ForeignKey(User, on_delete=CASCADE) + content = CharField(max_length=255) time = IntegerField(primary_key=False) - def __str__(self): - return "author_id: " + str(self.author_id) + ", channel_id: " + str(self.channel_id) + ", content: " + self.content -class AskModel(models.Model): - asker_id = IntegerField(primary_key=False) - asked_id = IntegerField(primary_key=False) +class AskModel(Model): + asker_id = IntegerField(primary_key=False) + asked_id = IntegerField(primary_key=False) - # return if the asker ask the asked to play a game - def is_asked(self, asker_id, asked_id): - return AskModel.objects.get(asker_id=asker_id, asked_id=asked_id) != None + # return if the asker ask the asked to play a game + def is_asked(self, asker_id, asked_id): + return AskModel.objects.get(asker_id=asker_id, asked_id=asked_id) is not None diff --git a/chat/serializers/chat.py b/chat/serializers/chat.py index 689547b..f2cc1b8 100644 --- a/chat/serializers/chat.py +++ b/chat/serializers/chat.py @@ -1,17 +1,19 @@ from rest_framework import serializers from django.utils.translation import gettext as _ +from django.contrib.auth.models import User -from profiles.models import ProfileModel from ..models import ChatChannelModel, ChatMessageModel + class ChatChannelSerializer(serializers.ModelSerializer): - members_id = serializers.ListField(child=serializers.IntegerField()) + members_id = serializers.ListField(child=serializers.IntegerField(), required=True, write_only=True) + messages = serializers.SerializerMethodField() class Meta: model = ChatChannelModel - fields = ["members_id", "pk"] + fields = ["members_id", "id", 'messages'] def validate_members_id(self, value): members_id: [int] = value @@ -19,14 +21,20 @@ class ChatChannelSerializer(serializers.ModelSerializer): raise serializers.ValidationError(_('There is not enough members to create the channel.')) if len(set(members_id)) != len(members_id): raise serializers.ValidationError(_('Duplicate in members list.')) + if self.context.get('user').pk not in members_id: + raise serializers.ValidationError(_('You are trying to create a group chat without you.')) for member_id in members_id: - if not ProfileModel.objects.filter(pk=member_id).exists(): + if not User.objects.filter(pk=member_id).exists(): raise serializers.ValidationError(_(f"The profile {member_id} doesn't exist.")) return members_id + def get_messages(self, obj: ChatChannelModel): + messages = ChatMessageModel.objects.filter(channel=obj).order_by('time') + return ChatMessageSerializer(messages, many=True).data -class ChatMessageSerializer(serializers.Serializer): + +class ChatMessageSerializer(serializers.ModelSerializer): class Meta: model = ChatMessageModel - fields = ["channel_id", "author_id", "content", "time"] + fields = ["channel", "author", "content", "time"] diff --git a/chat/urls.py b/chat/urls.py index bf1b38d..326fd95 100644 --- a/chat/urls.py +++ b/chat/urls.py @@ -1,6 +1,4 @@ from django.urls import path -from django.conf import settings -from django.conf.urls.static import static from .views import chat from .views import ask diff --git a/chat/views/chat.py b/chat/views/chat.py index e9b49b2..d3ca70a 100644 --- a/chat/views/chat.py +++ b/chat/views/chat.py @@ -1,48 +1,31 @@ from rest_framework.views import APIView from rest_framework.response import Response -from rest_framework import permissions, status +from rest_framework import permissions from rest_framework.authentication import SessionAuthentication -from django.http import HttpRequest -from django.contrib.auth import login -from django.db.models import QuerySet -from django.core import serializers +from django.contrib.auth.models import User +from ..models import ChatChannelModel, ChatMemberModel +from ..serializers import ChatChannelSerializer -from ..models import ChatChannelModel, ChatMemberModel, ChatMessageModel -from ..serializers.chat import ChatChannelSerializer, ChatMessageSerializer class ChannelView(APIView): - - queryset = ChatChannelModel.objects + serializer_class = ChatChannelSerializer permission_classes = (permissions.IsAuthenticated,) - authentication_classes = (SessionAuthentication,) + authentication_classes = (SessionAuthentication,) def post(self, request): - - serializer = self.serializer_class(data = request.data) - + serializer = self.serializer_class(data=request.data, context={'user': request.user}) serializer.is_valid(raise_exception=True) - data: dict = serializer.validated_data + members_id = serializer.validated_data.get('members_id') + member_list = [User.objects.get(pk=member_id) for member_id in members_id] - members_id = data.get("members_id") - if members_id == None: - return Response({"detail": "members_id is None."}, status = status.HTTP_400_BAD_REQUEST) - - if self.request.user.pk not in members_id: - return Response({"detail": "You are trying to create a chat group without you."}, status = status.HTTP_400_BAD_REQUEST) - - for member_channel in ChatMemberModel.objects.filter(member_id = members_id[0]): - channel_id: int = member_channel.channel_id - if not ChatChannelModel.objects.filter(pk = channel_id).exists(): - continue - channel: ChatChannelModel = ChatChannelModel.objects.get(pk = channel_id) - if set(channel.get_members_id()) == set(members_id): - messages = ChatMessageModel.objects.filter(channel_id = channel_id).order_by("time") - messages = serializers.serialize("json", messages) - return Response({'channel_id': channel_id, 'messages': messages}, status=status.HTTP_200_OK) - - new_channel_id = ChatChannelModel().create(members_id) - return Response({'channel_id': new_channel_id}, status=status.HTTP_201_CREATED) + for member_channel in ChatMemberModel.objects.filter(member=member_list[0]): + channel: ChatChannelModel = member_channel.channel + if set(channel.get_members()) == set(member_list): + break + else: + channel = ChatChannelModel().create(member_list) + return Response(self.serializer_class(channel).data) diff --git a/frontend/static/js/api/Client.js b/frontend/static/js/api/Client.js index 4166ec4..61eaef3 100644 --- a/frontend/static/js/api/Client.js +++ b/frontend/static/js/api/Client.js @@ -1,9 +1,15 @@ import { Account } from "./Account.js"; import { MatchMaking } from "./Matchmaking.js"; import { Profiles } from "./Profiles.js"; -import { Channels } from './chat/Channels.js'; import { MyProfile } from "./MyProfile.js"; +<<<<<<< Updated upstream import { Channel } from "./chat/Channel.js"; +||||||| Stash base +import { Tourmanents } from "./tournament/Tournaments.js"; +import { Channel } from "./chat/Channel.js"; +======= +import { Tourmanents } from "./tournament/Tournaments.js"; +>>>>>>> Stashed changes import Notice from "./Notice.js"; import LanguageManager from './LanguageManager.js'; @@ -50,16 +56,6 @@ class Client */ this._logged = undefined; - /** - * @type {Channels} - */ - this.channels = new Channels(this); - - /** - * @type {Channel} - */ - this.channel = undefined; - /** * @type {Notice} */ diff --git a/frontend/static/js/api/chat/Channel.js b/frontend/static/js/api/chat/Channel.js index c9869d4..aff7f93 100644 --- a/frontend/static/js/api/chat/Channel.js +++ b/frontend/static/js/api/chat/Channel.js @@ -1,10 +1,10 @@ import {Message} from "./Message.js"; class Channel { - constructor(client, channel_id, members_id, messages, reload) { + constructor(client, channel, members, messages, reload) { this.client = client; - this.channel_id = channel_id; - this.members_id = members_id; + this.channel = channel; + this.members = members; this.messages = []; if (messages != undefined) this.updateMessages(messages); @@ -13,16 +13,18 @@ class Channel { } // reload = function to use when we receive a message - async connect(reload) { - let url = `${window.location.protocol[4] === 's' ? 'wss' : 'ws'}://${window.location.host}/ws/chat/${this.channel_id}`; + connect(reload) { + const url = location.origin.replace('http', 'ws') + + '/ws/chat/' + + this.channel; this.chatSocket = new WebSocket(url); this.chatSocket.onmessage = (event) =>{ let data = JSON.parse(event.data); this.messages.push(new Message( - this.channel_id, - data.author_id, + this.channel, + data.author, data.content, data.time, )); @@ -31,28 +33,22 @@ class Channel { }; } - async disconnect() { + disconnect() { this.chatSocket.close(); } updateMessages(messages) { - messages = JSON.parse(messages); - let new_messages = []; + this.messages = []; messages.forEach((message) => { - message = message.fields; - new_messages.push(new Message( - message.channel_id, - message.author_id, + this.messages.push(new Message( + message.channel, + message.author, message.content, message.time, )); }); - - //console.log(new_messages); - this.messages = new_messages; - return new_messages; } async sendMessageChannel(message, receivers_id) { @@ -65,19 +61,6 @@ class Channel { 'receivers_id':receivers_id, })); } - - async deleteChannel() { - let response = await this.client._delete("/api/chat/" + this.channel_id, { - }); - - let data = await response.json(); - return data; - } - - async sendInvite() { - - } - } export {Channel}; diff --git a/frontend/static/js/api/chat/Channels.js b/frontend/static/js/api/chat/Channels.js index ac2356e..1ffde7d 100644 --- a/frontend/static/js/api/chat/Channels.js +++ b/frontend/static/js/api/chat/Channels.js @@ -1,6 +1,6 @@ import {Channel} from "./Channel.js"; -class Channels { +export default class Channels { constructor(client) { this.client = client; this.channel = undefined; @@ -8,32 +8,16 @@ class Channels { async createChannel(members_id, reload) { - let response = await this.client._post("/api/chat/", { + const response = await this.client._post("/api/chat/", { members_id:members_id }); if (response.status >= 300) return undefined; - let data = await response.json(); + const data = await response.json(); + console.log(data) - let messages; - if (response.status == 200) - messages = data.messages; - - this.channel = new Channel(this.client, data.channel_id, members_id, messages, reload); - return this.channel; + this.channel = new Channel(this.client, data.id, members_id, data.messages, reload); } - - async deleteChannel(members_id) { - let response = await this.client._delete("/api/chat/", { - members_id:members_id - }); - - let data = await response.json(); - return data; - } - } - -export {Channels}; diff --git a/frontend/static/js/api/chat/Message.js b/frontend/static/js/api/chat/Message.js index c9532b7..4753a19 100644 --- a/frontend/static/js/api/chat/Message.js +++ b/frontend/static/js/api/chat/Message.js @@ -1,7 +1,7 @@ class Message { - constructor(channel_id, author_id, content, time) { - this.channel_id = channel_id; - this.author_id = author_id; + constructor(channel, author, content, time) { + this.channel = channel; + this.author = author; this.content = content; this.time = time; } diff --git a/frontend/static/js/views/Search.js b/frontend/static/js/views/Search.js index ebb9092..0f9ea6e 100644 --- a/frontend/static/js/views/Search.js +++ b/frontend/static/js/views/Search.js @@ -1,35 +1,37 @@ import AbstractView from "./abstracts/AbstractView.js"; import { client, lang } from "../index.js"; -import {Message} from "../api/chat/Message.js"; +import Channels from '../api/chat/Channels.js' export default class extends AbstractView { - constructor(params) { - super(params, 'SearchWindowTitle'); - } + constructor(params) { + super(params, 'SearchWindowTitle'); + this.channelManager = new Channels(client); + } async postInit() { + this.profiles = await client.profiles.all(); + this.logged = await client.isAuthenticated(); this.logged = await client.isAuthenticated(); this.profiles = await client.profiles.all(); + document.getElementById('username-input').oninput = () => this.display_users(this.logged, this.profiles); + let search = document.getElementById("input_user"); if (search != undefined) search.oninput = () => this.display_users(); - let chat_input = document.getElementById("input_chat"); - this.last_add_chat = undefined; this.display_users(); this.display_chat(); - } async display_users() { - let search = document.getElementById("input_user").value.toLowerCase(); + const search = document.getElementById("username-input").value.toLowerCase(); - let list_users = document.getElementById('list_users'); + let list_users = document.getElementById('user-list'); list_users.innerHTML = ""; this.profiles.filter(user => user.username.toLowerCase().startsWith(search) == true).forEach(async (user) => { @@ -69,23 +71,23 @@ export default class extends AbstractView { let add_chat = document.createElement("a"); add_chat.id = "add_chat_off"; add_chat.onclick = async () => { - if (client.channels.channel != undefined) { + if (this.channelManager.channel != undefined) { // Permet de savoir si c'est le même channel // Check to know if it's the same channel - client.channels.channel.members_id.forEach((member_id) => { + this.channelManager.channel.members_id.forEach((member_id) => { if (member_id == user.id) - client.channels.channel = undefined; + this.channelManager.channel = undefined; }); - if (client.channels.channel == undefined) { + if (this.channelManager.channel == undefined) { add_chat.id = "add_chat_off"; this.last_add_chat = undefined; return await this.hide_chat(); } - await client.channels.channel.disconnect(); + await this.channelManager.channel.disconnect(); } - client.channels.channel = await client.channels.createChannel([client.me.id , user.id], () => this.reload_display_messages()); + await this.channelManager.createChannel([client.me.id , user.id], () => this.reload_display_messages()); this.display_chat(); if (this.last_add_chat != undefined) this.last_add_chat.id = "add_chat_off"; @@ -141,7 +143,7 @@ export default class extends AbstractView { document.getElementById(reload).remove(); }); - if (client.channels.channel === undefined || this.logged === false) + if (this.channelManager.channel === undefined || this.logged === false) return ; let chats = document.getElementById("chats"); @@ -159,19 +161,19 @@ export default class extends AbstractView { let messages = await this.display_messages(chat); // Input pour rentrer un message - let chat_input = document.getElementById("input_chat") || document.createElement("input"); - chat_input.id="input_chat"; + let chat_input = document.getElementById("chat-input") || document.createElement("input"); + chat_input.id="chat_input"; chat_input.type="text"; chat_input.name="message"; chat_input.placeholder="message bozo"; chat_input.maxLength=255; chat.appendChild(chat_input); - let members_id = client.channels.channel.members_id; + let members_id = this.channelManager.channel.members_id; chat_input.onkeydown = async () => { - if (event.keyCode == 13 && client.channels.channel != undefined) { - //let chat_input = document.getElementById("input_chat"); + if (event.keyCode == 13 && this.channelManager.channel != undefined) { + //let chat_input = document.getElementById("chat-input"); let chat_text = chat_input.value; let receivers_id = []; @@ -179,7 +181,7 @@ export default class extends AbstractView { if (member_id != client.me.id) receivers_id.push(this.profiles.filter(user => user.id == member_id)[0].id); }); - await client.channels.channel.sendMessageChannel(chat_text, receivers_id); + await this.channelManager.channel.sendMessageChannel(chat_text, receivers_id); // Reset chat_input.value = ""; } @@ -190,7 +192,7 @@ export default class extends AbstractView { // Scroll to the bottom of messages messages.scrollTop = messages.scrollHeight; - this.display_invite(); + // this.display_invite(); } @@ -202,12 +204,13 @@ export default class extends AbstractView { chat.appendChild(messages); // les messages, réecriture seulement du dernier - client.channels.channel.messages.forEach((message) => { + this.channelManager.channel.messages.forEach((message) => { let text = document.createElement("p"); let date = new Date(message.time); text.title = date.toLocaleString("fr-FR"); text.appendChild(document.createTextNode(message.content)); - text.id = message.author_id === client.me.id ? "you" : "other"; + console.log(message.author, client.me.id) + text.id = message.author === client.me.id ? "you" : "other"; messages.appendChild(text); }); @@ -219,13 +222,13 @@ export default class extends AbstractView { let messages = document.getElementById("messages"); let i = 0; - client.channels.channel.messages.forEach((message) => { + this.channelManager.channel.messages.forEach((message) => { if (messages.children[i] == null || message.content != messages.children[i].innerText) { let text = document.createElement("p"); let date = new Date(message.time); text.title = date.toLocaleString("fr-FR"); text.appendChild(document.createTextNode(message.content)); - text.id = message.author_id === client.me.id ? "you" : "other"; + text.id = message.author === client.me.id ? "you" : "other"; messages.appendChild(text); } @@ -237,7 +240,7 @@ export default class extends AbstractView { async display_members(chat) { - let members_id = client.channels.channel.members_id; + let members_id = this.channelManager.channel.members_id; let members = document.createElement("h2"); members.id = "members"; @@ -263,7 +266,7 @@ export default class extends AbstractView { if (chat == undefined) return ; - let members_id = client.channels.channel.members_id; + let members_id = this.channelManager.channel.members_id; let others = members_id.filter(id => id !== client.me.id); let invite = document.getElementById("invite") || document.createElement("button"); @@ -325,9 +328,9 @@ export default class extends AbstractView { } async leavePage() { - if (client.channels.channel != undefined) - client.channels.channel.disconnect(); - client.channels.channel = undefined; + if (this.channelManager.channel != undefined) + this.channelManager.channel.disconnect(); + this.channelManager.channel = undefined; } @@ -337,8 +340,8 @@ export default class extends AbstractView {