Send messages
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
This commit is contained in:
parent
f3a4a99b78
commit
4a78e80399
|
@ -3,9 +3,10 @@
|
|||
|
||||
from channels.generic.websocket import AsyncJsonWebsocketConsumer
|
||||
from django.contrib.auth.models import User
|
||||
from participation.models import Team, Pool, Tournament
|
||||
from registration.models import Registration
|
||||
|
||||
from .models import Channel
|
||||
from .models import Channel, Message
|
||||
|
||||
|
||||
class ChatConsumer(AsyncJsonWebsocketConsumer):
|
||||
|
@ -34,6 +35,10 @@ class ChatConsumer(AsyncJsonWebsocketConsumer):
|
|||
# Accept the connection
|
||||
await self.accept()
|
||||
|
||||
channels = await Channel.get_accessible_channels(user, 'read')
|
||||
async for channel in channels.all():
|
||||
await self.channel_layer.group_add(f"chat-{channel.id}", self.channel_name)
|
||||
|
||||
async def disconnect(self, close_code) -> None:
|
||||
"""
|
||||
Called when the websocket got disconnected, for any reason.
|
||||
|
@ -43,6 +48,10 @@ class ChatConsumer(AsyncJsonWebsocketConsumer):
|
|||
# User is not authenticated
|
||||
return
|
||||
|
||||
channels = await Channel.get_accessible_channels(self.scope['user'], 'read')
|
||||
async for channel in channels.all():
|
||||
await self.channel_layer.group_discard(f"chat-{channel.id}", self.channel_name)
|
||||
|
||||
async def receive_json(self, content, **kwargs):
|
||||
"""
|
||||
Called when the client sends us some data, parsed as JSON.
|
||||
|
@ -51,6 +60,8 @@ class ChatConsumer(AsyncJsonWebsocketConsumer):
|
|||
match content['type']:
|
||||
case 'fetch_channels':
|
||||
await self.fetch_channels()
|
||||
case 'send_message':
|
||||
await self.receive_message(content)
|
||||
case unknown:
|
||||
print("Unknown message type:", unknown)
|
||||
|
||||
|
@ -59,7 +70,6 @@ class ChatConsumer(AsyncJsonWebsocketConsumer):
|
|||
|
||||
read_channels = await Channel.get_accessible_channels(user, 'read')
|
||||
write_channels = await Channel.get_accessible_channels(user, 'write')
|
||||
print([channel async for channel in write_channels.all()])
|
||||
message = {
|
||||
'type': 'fetch_channels',
|
||||
'channels': [
|
||||
|
@ -73,3 +83,29 @@ class ChatConsumer(AsyncJsonWebsocketConsumer):
|
|||
]
|
||||
}
|
||||
await self.send_json(message)
|
||||
|
||||
async def receive_message(self, message: dict) -> None:
|
||||
user = self.scope['user']
|
||||
channel = await Channel.objects.prefetch_related('tournament__pools__juries', 'pool', 'team', 'invited') \
|
||||
.aget(id=message['channel_id'])
|
||||
write_channels = await Channel.get_accessible_channels(user, 'write')
|
||||
if not await write_channels.acontains(channel):
|
||||
return
|
||||
|
||||
message = await Message.objects.acreate(
|
||||
author=user,
|
||||
channel=channel,
|
||||
content=message['content'],
|
||||
)
|
||||
|
||||
await self.channel_layer.group_send(f'chat-{channel.id}', {
|
||||
'type': 'chat.send_message',
|
||||
'id': message.id,
|
||||
'timestamp': message.created_at.isoformat(),
|
||||
'author': await message.aget_author_name(),
|
||||
'content': message.content,
|
||||
})
|
||||
|
||||
async def chat_send_message(self, message) -> None:
|
||||
await self.send_json({'type': 'send_message', 'id': message['id'], 'timestamp': message['timestamp'],
|
||||
'author': message['author'], 'content': message['content']})
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
# Copyright (C) 2024 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from asgiref.sync import sync_to_async
|
||||
from django.contrib.auth.models import User
|
||||
from django.db import models
|
||||
from django.db.models import Q, QuerySet
|
||||
from django.utils.text import format_lazy
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from participation.models import Tournament
|
||||
from participation.models import Pool, Team, Tournament
|
||||
from registration.models import ParticipantRegistration, Registration, VolunteerRegistration
|
||||
from tfjm.permissions import PermissionType
|
||||
|
||||
|
@ -141,9 +142,6 @@ class Channel(models.Model):
|
|||
|
||||
qs |= Channel.objects.filter(invited=user)
|
||||
|
||||
print(user)
|
||||
print(qs.query)
|
||||
|
||||
return qs
|
||||
|
||||
class Meta:
|
||||
|
@ -182,6 +180,65 @@ class Message(models.Model):
|
|||
verbose_name=_("content"),
|
||||
)
|
||||
|
||||
def get_author_name(self):
|
||||
registration = self.author.registration
|
||||
|
||||
author_name = f"{self.author.first_name} {self.author.last_name}"
|
||||
if registration.is_volunteer:
|
||||
if registration.is_admin:
|
||||
author_name += " (CNO)"
|
||||
|
||||
if self.channel.pool:
|
||||
if registration == self.channel.pool.jury_president:
|
||||
author_name += " (P. jury)"
|
||||
elif registration in self.channel.pool.juries.all():
|
||||
author_name += " (Juré⋅e)"
|
||||
elif registration in self.channel.pool.tournament.organizers.all():
|
||||
author_name += " (CRO)"
|
||||
else:
|
||||
author_name += " (Bénévole)"
|
||||
elif self.channel.tournament:
|
||||
if registration in self.channel.tournament.organizers.all():
|
||||
author_name += " (CRO)"
|
||||
elif any([registration.id == pool.jury_president
|
||||
for pool in self.channel.tournament.pools.all()]):
|
||||
pools = ", ".join([pool.short_name
|
||||
for pool in self.channel.tournament.pools.all()
|
||||
if pool.jury_president == registration])
|
||||
author_name += f" (P. jury {pools})"
|
||||
elif any([pool.juries.contains(registration)
|
||||
for pool in self.channel.tournament.pools.all()]):
|
||||
pools = ", ".join([pool.short_name
|
||||
for pool in self.channel.tournament.pools.all()
|
||||
if pool.juries.acontains(registration)])
|
||||
author_name += f" (Juré⋅e {pools})"
|
||||
else:
|
||||
author_name += " (Bénévole)"
|
||||
else:
|
||||
if registration.organized_tournaments.exists():
|
||||
tournaments = ", ".join([tournament.name
|
||||
for tournament in registration.organized_tournaments.all()])
|
||||
author_name += f" (CRO {tournaments})"
|
||||
if Pool.objects.filter(jury_president=registration).exists():
|
||||
tournaments = Tournament.objects.filter(pools__jury_president=registration).distinct()
|
||||
tournaments = ", ".join([tournament.name for tournament in tournaments])
|
||||
author_name += f" (P. jury {tournaments})"
|
||||
elif registration.jury_in.exists():
|
||||
tournaments = Tournament.objects.filter(pools__juries=registration).distinct()
|
||||
tournaments = ", ".join([tournament.name for tournament in tournaments])
|
||||
author_name += f" (Juré⋅e {tournaments})"
|
||||
else:
|
||||
if registration.team_id:
|
||||
team = Team.objects.get(id=registration.team_id)
|
||||
author_name += f" ({team.trigram})"
|
||||
else:
|
||||
author_name += " (sans équipe)"
|
||||
|
||||
return author_name
|
||||
|
||||
async def aget_author_name(self):
|
||||
return await sync_to_async(self.get_author_name)()
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("message")
|
||||
verbose_name_plural = _("messages")
|
||||
|
|
|
@ -37,6 +37,22 @@ function selectChannel(channel_id) {
|
|||
messageInput.disabled = !channel['write_access']
|
||||
}
|
||||
|
||||
function sendMessage() {
|
||||
let messageInput = document.getElementById('input-message')
|
||||
let message = messageInput.value
|
||||
messageInput.value = ''
|
||||
|
||||
if (!message) {
|
||||
return
|
||||
}
|
||||
|
||||
socket.send(JSON.stringify({
|
||||
'type': 'send_message',
|
||||
'channel_id': selected_channel_id,
|
||||
'content': message,
|
||||
}))
|
||||
}
|
||||
|
||||
function setChannels(new_channels) {
|
||||
channels = {}
|
||||
for (let channel of new_channels) {
|
||||
|
@ -48,6 +64,23 @@ function setChannels(new_channels) {
|
|||
}
|
||||
}
|
||||
|
||||
function receiveMessage(message) {
|
||||
let messageList = document.getElementById('message-list')
|
||||
|
||||
let messageElement = document.createElement('li')
|
||||
messageElement.classList.add('list-group-item')
|
||||
messageList.appendChild(messageElement)
|
||||
|
||||
let authorDiv = document.createElement('div')
|
||||
authorDiv.classList.add('text-muted', 'fw-bold')
|
||||
authorDiv.innerText = message['author']
|
||||
messageElement.appendChild(authorDiv)
|
||||
|
||||
let contentDiv = document.createElement('div')
|
||||
contentDiv.innerText = message['content']
|
||||
messageElement.appendChild(contentDiv)
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
/**
|
||||
* Process the received data from the server.
|
||||
|
@ -59,6 +92,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
case 'fetch_channels':
|
||||
setChannels(data['channels'])
|
||||
break
|
||||
case 'send_message':
|
||||
receiveMessage(data)
|
||||
break
|
||||
default:
|
||||
console.log(data)
|
||||
console.error('Unknown message type:', data['type'])
|
||||
|
|
|
@ -35,31 +35,20 @@
|
|||
</h3>
|
||||
</div>
|
||||
<div class="card-body overflow-y-scroll mw-100 h-100 flex-grow-0" id="chat-messages">
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item">
|
||||
<div class="fw-bold">Emmy D'Anello (CNO)</div>
|
||||
Message 1
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<div class="fw-bold">Emmy D'Anello (CNO)</div>
|
||||
Message 2
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<div class="fw-bold">Emmy D'Anello (CNO)</div>
|
||||
Message 3
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="list-group list-group-flush" id="message-list"></ul>
|
||||
</div>
|
||||
<div class="card-footer mt-auto">
|
||||
<div class="input-group">
|
||||
<label for="input-message" class="input-group-text">
|
||||
<i class="fas fa-comment"></i>
|
||||
</label>
|
||||
<input type="text" class="form-control" id="input-message" placeholder="{% trans "Send message…" %}" autocomplete="off">
|
||||
<button class="input-group-text btn btn-success">
|
||||
<i class="fas fa-paper-plane"></i>
|
||||
</button>
|
||||
</div>
|
||||
<form onsubmit="event.preventDefault(); sendMessage()">
|
||||
<div class="input-group">
|
||||
<label for="input-message" class="input-group-text">
|
||||
<i class="fas fa-comment"></i>
|
||||
</label>
|
||||
<input type="text" class="form-control" id="input-message" placeholder="{% trans "Send message…" %}" autocomplete="off">
|
||||
<button class="input-group-text btn btn-success" type="submit">
|
||||
<i class="fas fa-paper-plane"></i>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
Loading…
Reference in New Issue