Setup chat UI

Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
This commit is contained in:
Emmy D'Anello 2024-04-27 12:08:10 +02:00
parent 46fc5f39c8
commit f3a4a99b78
Signed by: ynerant
GPG Key ID: 3A75C55819C8CF85
5 changed files with 161 additions and 45 deletions

View File

@ -4,6 +4,9 @@
await Notification.requestPermission() await Notification.requestPermission()
})() })()
let channels = {}
let selected_channel_id = null
/** /**
* Display a new notification with the given title and the given body. * Display a new notification with the given title and the given body.
* @param title The title of the notification * @param title The title of the notification
@ -18,6 +21,33 @@ function showNotification(title, body, timeout = 5000) {
return notif return notif
} }
function selectChannel(channel_id) {
let channel = channels[channel_id]
if (!channel) {
console.error('Channel not found:', channel_id)
return
}
selected_channel_id = channel_id
let channelTitle = document.getElementById('channel-title')
channelTitle.innerText = channel['name']
let messageInput = document.getElementById('input-message')
messageInput.disabled = !channel['write_access']
}
function setChannels(new_channels) {
channels = {}
for (let channel of new_channels) {
channels[channel['id']] = channel
}
if (new_channels && (!selected_channel_id || !channels[selected_channel_id])) {
selectChannel(Object.keys(channels)[0])
}
}
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
/** /**
* Process the received data from the server. * Process the received data from the server.
@ -25,7 +55,15 @@ document.addEventListener('DOMContentLoaded', () => {
*/ */
function processMessage(data) { function processMessage(data) {
// TODO Implement chat protocol // TODO Implement chat protocol
switch (data['type']) {
case 'fetch_channels':
setChannels(data['channels'])
break
default:
console.log(data) console.log(data)
console.error('Unknown message type:', data['type'])
break
}
} }
function setupSocket(nextDelay = 1000) { function setupSocket(nextDelay = 1000) {

View File

@ -4,6 +4,64 @@
{% load i18n %} {% load i18n %}
{% block content %} {% block content %}
<noscript>
{% trans "JavaScript must be enabled on your browser to access chat." %}
</noscript>
<div class="offcanvas offcanvas-start" tabindex="-1" id="channelSelector" aria-labelledby="offcanvasExampleLabel">
<div class="offcanvas-header">
<h4 class="offcanvas-title" id="offcanvasExampleLabel">{% trans "Chat channels" %}</h4>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<ul class="list-group list-group-flush" id="nav-channels-tab">
{% for channel in channels %}
<li class="list-group-item" id="tab-channel-{{ channel.id }}" data-bs-dismiss="offcanvas"
onclick="selectChannel({{ channel.id }})">
<button class="nav-link">{{ channel.name }}</button>
</li>
{% endfor %}
</ul>
</div>
</div>
<div class="card tab-content w-100 mh-100" style="height: 95vh" id="nav-channels-content">
<div class="card-header">
<h3>
<button class="navbar-toggler" type="button" data-bs-toggle="offcanvas" data-bs-target="#channelSelector"
aria-controls="channelSelector" aria-expanded="false" aria-label="Toggle channel selector">
<span class="navbar-toggler-icon"></span>
</button>
<span id="channel-title"></span>
</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>
</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>
</div>
</div>
{% endblock %} {% endblock %}
{% block extrajavascript %} {% block extrajavascript %}

View File

@ -4,6 +4,8 @@
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import TemplateView from django.views.generic import TemplateView
from chat.models import Channel
class ChatView(LoginRequiredMixin, TemplateView): class ChatView(LoginRequiredMixin, TemplateView):
""" """
@ -11,3 +13,9 @@ class ChatView(LoginRequiredMixin, TemplateView):
with Javascript and websockets. with Javascript and websockets.
""" """
template_name = "chat/chat.html" template_name = "chat/chat.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
from asgiref.sync import async_to_sync
context['channels'] = async_to_sync(Channel.get_accessible_channels)(self.request.user, 'read')
return context

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: TFJM\n" "Project-Id-Version: TFJM\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-27 08:46+0200\n" "POT-Creation-Date: 2024-04-27 11:02+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Emmy D'Anello <emmy.danello@animath.fr>\n" "Last-Translator: Emmy D'Anello <emmy.danello@animath.fr>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -21,20 +21,20 @@ msgstr ""
msgid "API" msgid "API"
msgstr "API" msgstr "API"
#: chat/models.py:13 participation/models.py:35 participation/models.py:263 #: chat/models.py:17 participation/models.py:35 participation/models.py:263
#: participation/tables.py:18 participation/tables.py:34 #: participation/tables.py:18 participation/tables.py:34
msgid "name" msgid "name"
msgstr "nom" msgstr "nom"
#: chat/models.py:17 #: chat/models.py:22
msgid "read permission" msgid "read permission"
msgstr "permission de lecture" msgstr "permission de lecture"
#: chat/models.py:22 #: chat/models.py:28
msgid "write permission" msgid "write permission"
msgstr "permission d'écriture" msgstr "permission d'écriture"
#: chat/models.py:32 draw/admin.py:53 draw/admin.py:71 draw/admin.py:88 #: chat/models.py:38 draw/admin.py:53 draw/admin.py:71 draw/admin.py:88
#: draw/models.py:26 participation/admin.py:79 participation/admin.py:140 #: draw/models.py:26 participation/admin.py:79 participation/admin.py:140
#: participation/admin.py:171 participation/models.py:693 #: participation/admin.py:171 participation/models.py:693
#: participation/models.py:717 participation/models.py:935 #: participation/models.py:717 participation/models.py:935
@ -43,7 +43,7 @@ msgstr "permission d'écriture"
msgid "tournament" msgid "tournament"
msgstr "tournoi" msgstr "tournoi"
#: chat/models.py:34 #: chat/models.py:40
msgid "" msgid ""
"For a permission that concerns a tournament, indicates what is the concerned " "For a permission that concerns a tournament, indicates what is the concerned "
"tournament." "tournament."
@ -51,21 +51,21 @@ msgstr ""
"Pour une permission qui concerne un tournoi, indique quel est le tournoi " "Pour une permission qui concerne un tournoi, indique quel est le tournoi "
"concerné." "concerné."
#: chat/models.py:43 draw/models.py:429 draw/models.py:456 #: chat/models.py:49 draw/models.py:429 draw/models.py:456
#: participation/admin.py:136 participation/admin.py:155 #: participation/admin.py:136 participation/admin.py:155
#: participation/models.py:1434 participation/models.py:1443 #: participation/models.py:1434 participation/models.py:1443
#: participation/tables.py:84 #: participation/tables.py:84
msgid "pool" msgid "pool"
msgstr "poule" msgstr "poule"
#: chat/models.py:45 #: chat/models.py:51
msgid "" msgid ""
"For a permission that concerns a pool, indicates what is the concerned pool." "For a permission that concerns a pool, indicates what is the concerned pool."
msgstr "" msgstr ""
"Pour une permission qui concerne une poule, indique quelle est la poule " "Pour une permission qui concerne une poule, indique quelle est la poule "
"concernée." "concernée."
#: chat/models.py:54 draw/templates/draw/tournament_content.html:277 #: chat/models.py:60 draw/templates/draw/tournament_content.html:277
#: participation/admin.py:167 participation/models.py:252 #: participation/admin.py:167 participation/models.py:252
#: participation/models.py:708 #: participation/models.py:708
#: participation/templates/participation/tournament_harmonize.html:15 #: participation/templates/participation/tournament_harmonize.html:15
@ -75,18 +75,18 @@ msgstr ""
msgid "team" msgid "team"
msgstr "équipe" msgstr "équipe"
#: chat/models.py:56 #: chat/models.py:62
msgid "" msgid ""
"For a permission that concerns a team, indicates what is the concerned team." "For a permission that concerns a team, indicates what is the concerned team."
msgstr "" msgstr ""
"Pour une permission qui concerne une équipe, indique quelle est l'équipe " "Pour une permission qui concerne une équipe, indique quelle est l'équipe "
"concernée." "concernée."
#: chat/models.py:60 #: chat/models.py:66
msgid "private" msgid "private"
msgstr "privé" msgstr "privé"
#: chat/models.py:62 #: chat/models.py:68
msgid "" msgid ""
"If checked, only users who have been explicitly added to the channel will be " "If checked, only users who have been explicitly added to the channel will be "
"able to access it." "able to access it."
@ -94,11 +94,11 @@ msgstr ""
"Si sélectionné, seul⋅es les utilisateur⋅rices qui ont été explicitement " "Si sélectionné, seul⋅es les utilisateur⋅rices qui ont été explicitement "
"ajouté⋅es au canal pourront y accéder." "ajouté⋅es au canal pourront y accéder."
#: chat/models.py:67 #: chat/models.py:73
msgid "invited users" msgid "invited users"
msgstr "Utilisateur⋅rices invité" msgstr "Utilisateur⋅rices invité"
#: chat/models.py:70 #: chat/models.py:76
msgid "" msgid ""
"Extra users who have been invited to the channel, in addition to the " "Extra users who have been invited to the channel, in addition to the "
"permitted group of the channel." "permitted group of the channel."
@ -106,43 +106,55 @@ msgstr ""
"Utilisateur⋅rices supplémentaires qui ont été invité⋅es au canal, en plus du " "Utilisateur⋅rices supplémentaires qui ont été invité⋅es au canal, en plus du "
"groupe autorisé du canal." "groupe autorisé du canal."
#: chat/models.py:75 #: chat/models.py:81
#, python-brace-format #, python-brace-format
msgid "Channel {name}" msgid "Channel {name}"
msgstr "Canal {name}" msgstr "Canal {name}"
#: chat/models.py:78 chat/models.py:87 #: chat/models.py:150 chat/models.py:159
msgid "channel" msgid "channel"
msgstr "canal" msgstr "canal"
#: chat/models.py:79 #: chat/models.py:151
msgid "channels" msgid "channels"
msgstr "canaux" msgstr "canaux"
#: chat/models.py:93 #: chat/models.py:165
msgid "author" msgid "author"
msgstr "auteur⋅rice" msgstr "auteur⋅rice"
#: chat/models.py:100 #: chat/models.py:172
msgid "created at" msgid "created at"
msgstr "créé le" msgstr "créé le"
#: chat/models.py:105 #: chat/models.py:177
msgid "updated at" msgid "updated at"
msgstr "modifié le" msgstr "modifié le"
#: chat/models.py:110 #: chat/models.py:182
msgid "content" msgid "content"
msgstr "contenu" msgstr "contenu"
#: chat/models.py:114 #: chat/models.py:186
msgid "message" msgid "message"
msgstr "message" msgstr "message"
#: chat/models.py:115 #: chat/models.py:187
msgid "messages" msgid "messages"
msgstr "messages" msgstr "messages"
#: chat/templates/chat/chat.html:8
msgid "JavaScript must be enabled on your browser to access chat."
msgstr ""
#: chat/templates/chat/chat.html:12
msgid "Chat channels"
msgstr "Canaux de chat"
#: chat/templates/chat/chat.html:43
msgid "Send message…"
msgstr "Envoyer un message…"
#: draw/admin.py:39 draw/admin.py:57 draw/admin.py:75 #: draw/admin.py:39 draw/admin.py:57 draw/admin.py:75
#: participation/admin.py:109 participation/models.py:253 #: participation/admin.py:109 participation/models.py:253
#: participation/tables.py:88 #: participation/tables.py:88
@ -158,68 +170,68 @@ msgstr "tour"
msgid "Draw" msgid "Draw"
msgstr "Tirage au sort" msgstr "Tirage au sort"
#: draw/consumers.py:30 #: draw/consumers.py:31
msgid "You are not an organizer." msgid "You are not an organizer."
msgstr "Vous n'êtes pas un⋅e organisateur⋅rice." msgstr "Vous n'êtes pas un⋅e organisateur⋅rice."
#: draw/consumers.py:162 #: draw/consumers.py:165
msgid "The draw is already started." msgid "The draw is already started."
msgstr "Le tirage a déjà commencé." msgstr "Le tirage a déjà commencé."
#: draw/consumers.py:168 #: draw/consumers.py:171
msgid "Invalid format" msgid "Invalid format"
msgstr "Format invalide" msgstr "Format invalide"
#: draw/consumers.py:173 #: draw/consumers.py:176
#, python-brace-format #, python-brace-format
msgid "The sum must be equal to the number of teams: expected {len}, got {sum}" msgid "The sum must be equal to the number of teams: expected {len}, got {sum}"
msgstr "" msgstr ""
"La somme doit être égale au nombre d'équipes : attendu {len}, obtenu {sum}" "La somme doit être égale au nombre d'équipes : attendu {len}, obtenu {sum}"
#: draw/consumers.py:178 #: draw/consumers.py:181
msgid "There can be at most one pool with 5 teams." msgid "There can be at most one pool with 5 teams."
msgstr "Il ne peut y avoir au plus qu'une seule poule de 5 équipes." msgstr "Il ne peut y avoir au plus qu'une seule poule de 5 équipes."
#: draw/consumers.py:218 #: draw/consumers.py:221
msgid "Draw started!" msgid "Draw started!"
msgstr "Le tirage a commencé !" msgstr "Le tirage a commencé !"
#: draw/consumers.py:240 #: draw/consumers.py:243
#, python-brace-format #, python-brace-format
msgid "The draw for the tournament {tournament} will start." msgid "The draw for the tournament {tournament} will start."
msgstr "Le tirage au sort du tournoi {tournament} va commencer." msgstr "Le tirage au sort du tournoi {tournament} va commencer."
#: draw/consumers.py:251 draw/consumers.py:277 draw/consumers.py:687 #: draw/consumers.py:254 draw/consumers.py:280 draw/consumers.py:690
#: draw/consumers.py:904 draw/consumers.py:993 draw/consumers.py:1015 #: draw/consumers.py:907 draw/consumers.py:996 draw/consumers.py:1018
#: draw/consumers.py:1106 draw/templates/draw/tournament_content.html:5 #: draw/consumers.py:1109 draw/templates/draw/tournament_content.html:5
msgid "The draw has not started yet." msgid "The draw has not started yet."
msgstr "Le tirage au sort n'a pas encore commencé." msgstr "Le tirage au sort n'a pas encore commencé."
#: draw/consumers.py:264 #: draw/consumers.py:267
#, python-brace-format #, python-brace-format
msgid "The draw for the tournament {tournament} is aborted." msgid "The draw for the tournament {tournament} is aborted."
msgstr "Le tirage au sort du tournoi {tournament} est annulé." msgstr "Le tirage au sort du tournoi {tournament} est annulé."
#: draw/consumers.py:304 draw/consumers.py:325 draw/consumers.py:621 #: draw/consumers.py:307 draw/consumers.py:328 draw/consumers.py:624
#: draw/consumers.py:692 draw/consumers.py:909 #: draw/consumers.py:695 draw/consumers.py:912
msgid "This is not the time for this." msgid "This is not the time for this."
msgstr "Ce n'est pas le moment pour cela." msgstr "Ce n'est pas le moment pour cela."
#: draw/consumers.py:317 draw/consumers.py:320 #: draw/consumers.py:320 draw/consumers.py:323
msgid "You've already launched the dice." msgid "You've already launched the dice."
msgstr "Vous avez déjà lancé le dé." msgstr "Vous avez déjà lancé le dé."
#: draw/consumers.py:323 #: draw/consumers.py:326
msgid "It is not your turn." msgid "It is not your turn."
msgstr "Ce n'est pas votre tour." msgstr "Ce n'est pas votre tour."
#: draw/consumers.py:410 #: draw/consumers.py:413
#, python-brace-format #, python-brace-format
msgid "Dices from teams {teams} are identical. Please relaunch your dices." msgid "Dices from teams {teams} are identical. Please relaunch your dices."
msgstr "" msgstr ""
"Les dés des équipes {teams} sont identiques. Merci de relancer vos dés." "Les dés des équipes {teams} sont identiques. Merci de relancer vos dés."
#: draw/consumers.py:1018 #: draw/consumers.py:1021
msgid "This is only available for the final tournament." msgid "This is only available for the final tournament."
msgstr "Cela n'est possible que pour la finale." msgstr "Cela n'est possible que pour la finale."

View File

@ -40,18 +40,18 @@
<body class="d-flex w-100 h-100 flex-column"> <body class="d-flex w-100 h-100 flex-column">
{% include "navbar.html" %} {% include "navbar.html" %}
<div id="body-wrapper" class="row w-100 my-3"> <div id="body-wrapper" class="row w-100 my-3 flex-grow-1">
<aside class="col-lg-2 px-2"> <aside class="col-lg-2 px-2">
{% include "sidebar.html" %} {% include "sidebar.html" %}
</aside> </aside>
<main class="col d-flex flex-column"> <main class="col d-flex flex-column flex-grow-1">
<div class="container"> <div class="container d-flex flex-column flex-grow-1">
{% block content-title %}<h1 id="content-title">{{ title }}</h1>{% endblock %} {% block content-title %}<h1 id="content-title">{{ title }}</h1>{% endblock %}
{% include "messages.html" %} {% include "messages.html" %}
<div id="content"> <div id="content" class="d-flex flex-column flex-grow-1">
{% block content %} {% block content %}
<p>Default content...</p> <p>Default content...</p>
{% endblock content %} {% endblock content %}