mirror of
				https://gitlab.com/animath/si/plateforme.git
				synced 2025-11-04 08:22:10 +01:00 
			
		
		
		
	Properly sort messages and add fetch previous messages ability
Signed-off-by: Emmy D'Anello <emmy.danello@animath.fr>
This commit is contained in:
		@@ -63,7 +63,7 @@ class ChatConsumer(AsyncJsonWebsocketConsumer):
 | 
				
			|||||||
            case 'send_message':
 | 
					            case 'send_message':
 | 
				
			||||||
                await self.receive_message(content)
 | 
					                await self.receive_message(content)
 | 
				
			||||||
            case 'fetch_messages':
 | 
					            case 'fetch_messages':
 | 
				
			||||||
                await self.fetch_messages(content['channel_id'])
 | 
					                await self.fetch_messages(**content)
 | 
				
			||||||
            case unknown:
 | 
					            case unknown:
 | 
				
			||||||
                print("Unknown message type:", unknown)
 | 
					                print("Unknown message type:", unknown)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -109,17 +109,19 @@ class ChatConsumer(AsyncJsonWebsocketConsumer):
 | 
				
			|||||||
            'content': message.content,
 | 
					            'content': message.content,
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async def fetch_messages(self, channel_id: int, offset: int = 0, limit: int = 50) -> None:
 | 
					    async def fetch_messages(self, channel_id: int, offset: int = 0, limit: int = 50, **_kwargs) -> None:
 | 
				
			||||||
        channel = await Channel.objects.aget(id=channel_id)
 | 
					        channel = await Channel.objects.aget(id=channel_id)
 | 
				
			||||||
        read_channels = await Channel.get_accessible_channels(self.scope['user'], 'read')
 | 
					        read_channels = await Channel.get_accessible_channels(self.scope['user'], 'read')
 | 
				
			||||||
        if not await read_channels.acontains(channel):
 | 
					        if not await read_channels.acontains(channel):
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        messages = Message.objects.filter(channel=channel).order_by('created_at')[offset:offset + limit].all()
 | 
					        limit = min(limit, 200)  # Fetch only maximum 200 messages at the time
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        messages = Message.objects.filter(channel=channel).order_by('-created_at')[offset:offset + limit].all()
 | 
				
			||||||
        await self.send_json({
 | 
					        await self.send_json({
 | 
				
			||||||
            'type': 'fetch_messages',
 | 
					            'type': 'fetch_messages',
 | 
				
			||||||
            'channel_id': channel_id,
 | 
					            'channel_id': channel_id,
 | 
				
			||||||
            'messages': [
 | 
					            'messages': list(reversed([
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    'id': message.id,
 | 
					                    'id': message.id,
 | 
				
			||||||
                    'timestamp': message.created_at.isoformat(),
 | 
					                    'timestamp': message.created_at.isoformat(),
 | 
				
			||||||
@@ -127,7 +129,7 @@ class ChatConsumer(AsyncJsonWebsocketConsumer):
 | 
				
			|||||||
                    'content': message.content,
 | 
					                    'content': message.content,
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                async for message in messages
 | 
					                async for message in messages
 | 
				
			||||||
            ]
 | 
					            ]))
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async def chat_send_message(self, message) -> None:
 | 
					    async def chat_send_message(self, message) -> None:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,8 @@
 | 
				
			|||||||
    await Notification.requestPermission()
 | 
					    await Notification.requestPermission()
 | 
				
			||||||
})()
 | 
					})()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const MAX_MESSAGES = 50
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let channels = {}
 | 
					let channels = {}
 | 
				
			||||||
let messages = {}
 | 
					let messages = {}
 | 
				
			||||||
let selected_channel_id = null
 | 
					let selected_channel_id = null
 | 
				
			||||||
@@ -61,12 +63,9 @@ function setChannels(new_channels) {
 | 
				
			|||||||
    for (let channel of new_channels) {
 | 
					    for (let channel of new_channels) {
 | 
				
			||||||
        channels[channel['id']] = channel
 | 
					        channels[channel['id']] = channel
 | 
				
			||||||
        if (!messages[channel['id']])
 | 
					        if (!messages[channel['id']])
 | 
				
			||||||
            messages[channel['id']] = []
 | 
					            messages[channel['id']] = new Map()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        socket.send(JSON.stringify({
 | 
					        fetchMessages(channel['id'])
 | 
				
			||||||
            'type': 'fetch_messages',
 | 
					 | 
				
			||||||
            'channel_id': channel['id'],
 | 
					 | 
				
			||||||
        }))
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (new_channels && (!selected_channel_id || !channels[selected_channel_id]))
 | 
					    if (new_channels && (!selected_channel_id || !channels[selected_channel_id]))
 | 
				
			||||||
@@ -78,16 +77,35 @@ function receiveMessage(message) {
 | 
				
			|||||||
    redrawMessages()
 | 
					    redrawMessages()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function fetchMessages(data) {
 | 
					function fetchMessages(channel_id, offset = 0, limit = MAX_MESSAGES) {
 | 
				
			||||||
 | 
					    socket.send(JSON.stringify({
 | 
				
			||||||
 | 
					        'type': 'fetch_messages',
 | 
				
			||||||
 | 
					        'channel_id': channel_id,
 | 
				
			||||||
 | 
					        'offset': offset,
 | 
				
			||||||
 | 
					        'limit': limit,
 | 
				
			||||||
 | 
					    }))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function fetchPreviousMessages() {
 | 
				
			||||||
 | 
					    let channel_id = selected_channel_id
 | 
				
			||||||
 | 
					    let offset = messages[channel_id].size
 | 
				
			||||||
 | 
					    fetchMessages(channel_id, offset, MAX_MESSAGES)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function receiveFetchedMessages(data) {
 | 
				
			||||||
    let channel_id = data['channel_id']
 | 
					    let channel_id = data['channel_id']
 | 
				
			||||||
    let new_messages = data['messages']
 | 
					    let new_messages = data['messages']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!messages[channel_id])
 | 
					    if (!messages[channel_id])
 | 
				
			||||||
        messages[channel_id] = []
 | 
					        messages[channel_id] = new Map()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (let message of new_messages) {
 | 
					    for (let message of new_messages)
 | 
				
			||||||
        messages[channel_id].push(message)
 | 
					        messages[channel_id].set(message['id'], message)
 | 
				
			||||||
    }
 | 
					
 | 
				
			||||||
 | 
					    // Sort messages by timestamp
 | 
				
			||||||
 | 
					    messages[channel_id] = new Map([...messages[channel_id].values()]
 | 
				
			||||||
 | 
					      .sort((a, b) => new Date(a['timestamp']) - new Date(b['timestamp']))
 | 
				
			||||||
 | 
					      .map(message => [message['id'], message]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    redrawMessages()
 | 
					    redrawMessages()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -99,7 +117,7 @@ function redrawMessages() {
 | 
				
			|||||||
    let lastMessage = null
 | 
					    let lastMessage = null
 | 
				
			||||||
    let lastContentDiv = null
 | 
					    let lastContentDiv = null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (let message of messages[selected_channel_id]) {
 | 
					    for (let message of messages[selected_channel_id].values()) {
 | 
				
			||||||
        if (lastMessage && lastMessage['author'] === message['author']) {
 | 
					        if (lastMessage && lastMessage['author'] === message['author']) {
 | 
				
			||||||
            let lastTimestamp = new Date(lastMessage['timestamp'])
 | 
					            let lastTimestamp = new Date(lastMessage['timestamp'])
 | 
				
			||||||
            let newTimestamp = new Date(message['timestamp'])
 | 
					            let newTimestamp = new Date(message['timestamp'])
 | 
				
			||||||
@@ -138,6 +156,12 @@ function redrawMessages() {
 | 
				
			|||||||
        lastMessage = message
 | 
					        lastMessage = message
 | 
				
			||||||
        lastContentDiv = contentDiv
 | 
					        lastContentDiv = contentDiv
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let fetchMoreButton = document.getElementById('fetch-previous-messages')
 | 
				
			||||||
 | 
					    if (!messages[selected_channel_id] || messages[selected_channel_id].size % MAX_MESSAGES !== 0)
 | 
				
			||||||
 | 
					        fetchMoreButton.classList.add('d-none')
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        fetchMoreButton.classList.remove('d-none')
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
document.addEventListener('DOMContentLoaded', () => {
 | 
					document.addEventListener('DOMContentLoaded', () => {
 | 
				
			||||||
@@ -154,7 +178,7 @@ document.addEventListener('DOMContentLoaded', () => {
 | 
				
			|||||||
                receiveMessage(data)
 | 
					                receiveMessage(data)
 | 
				
			||||||
                break
 | 
					                break
 | 
				
			||||||
            case 'fetch_messages':
 | 
					            case 'fetch_messages':
 | 
				
			||||||
                fetchMessages(data)
 | 
					                receiveFetchedMessages(data)
 | 
				
			||||||
                break
 | 
					                break
 | 
				
			||||||
            default:
 | 
					            default:
 | 
				
			||||||
                console.log(data)
 | 
					                console.log(data)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,6 +35,12 @@
 | 
				
			|||||||
            </h3>
 | 
					            </h3>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <div class="card-body overflow-y-scroll mw-100 h-100 flex-grow-0" id="chat-messages">
 | 
					        <div class="card-body overflow-y-scroll mw-100 h-100 flex-grow-0" id="chat-messages">
 | 
				
			||||||
 | 
					            <div class="text-center d-none" id="fetch-previous-messages">
 | 
				
			||||||
 | 
					                <a href="#" class="nav-link" onclick="event.preventDefault(); fetchPreviousMessages()">
 | 
				
			||||||
 | 
					                    {% trans "Fetch previous messages…" %}
 | 
				
			||||||
 | 
					                </a>
 | 
				
			||||||
 | 
					                <hr>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
            <ul class="list-group list-group-flush" id="message-list"></ul>
 | 
					            <ul class="list-group list-group-flush" id="message-list"></ul>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <div class="card-footer mt-auto">
 | 
					        <div class="card-footer mt-auto">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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 11:02+0200\n"
 | 
					"POT-Creation-Date: 2024-04-27 14:10+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:17 participation/models.py:35 participation/models.py:263
 | 
					#: chat/models.py:18 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:22
 | 
					#: chat/models.py:23
 | 
				
			||||||
msgid "read permission"
 | 
					msgid "read permission"
 | 
				
			||||||
msgstr "permission de lecture"
 | 
					msgstr "permission de lecture"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: chat/models.py:28
 | 
					#: chat/models.py:29
 | 
				
			||||||
msgid "write permission"
 | 
					msgid "write permission"
 | 
				
			||||||
msgstr "permission d'écriture"
 | 
					msgstr "permission d'écriture"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: chat/models.py:38 draw/admin.py:53 draw/admin.py:71 draw/admin.py:88
 | 
					#: chat/models.py:39 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:40
 | 
					#: chat/models.py:41
 | 
				
			||||||
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:49 draw/models.py:429 draw/models.py:456
 | 
					#: chat/models.py:50 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:51
 | 
					#: chat/models.py:52
 | 
				
			||||||
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:60 draw/templates/draw/tournament_content.html:277
 | 
					#: chat/models.py:61 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:62
 | 
					#: chat/models.py:63
 | 
				
			||||||
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:66
 | 
					#: chat/models.py:67
 | 
				
			||||||
msgid "private"
 | 
					msgid "private"
 | 
				
			||||||
msgstr "privé"
 | 
					msgstr "privé"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: chat/models.py:68
 | 
					#: chat/models.py:69
 | 
				
			||||||
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:73
 | 
					#: chat/models.py:74
 | 
				
			||||||
msgid "invited users"
 | 
					msgid "invited users"
 | 
				
			||||||
msgstr "Utilisateur⋅rices invité"
 | 
					msgstr "Utilisateur⋅rices invité"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: chat/models.py:76
 | 
					#: chat/models.py:77
 | 
				
			||||||
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,52 +106,56 @@ 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:81
 | 
					#: chat/models.py:82
 | 
				
			||||||
#, python-brace-format
 | 
					#, python-brace-format
 | 
				
			||||||
msgid "Channel {name}"
 | 
					msgid "Channel {name}"
 | 
				
			||||||
msgstr "Canal {name}"
 | 
					msgstr "Canal {name}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: chat/models.py:150 chat/models.py:159
 | 
					#: chat/models.py:148 chat/models.py:157
 | 
				
			||||||
msgid "channel"
 | 
					msgid "channel"
 | 
				
			||||||
msgstr "canal"
 | 
					msgstr "canal"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: chat/models.py:151
 | 
					#: chat/models.py:149
 | 
				
			||||||
msgid "channels"
 | 
					msgid "channels"
 | 
				
			||||||
msgstr "canaux"
 | 
					msgstr "canaux"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: chat/models.py:165
 | 
					#: chat/models.py:163
 | 
				
			||||||
msgid "author"
 | 
					msgid "author"
 | 
				
			||||||
msgstr "auteur⋅rice"
 | 
					msgstr "auteur⋅rice"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: chat/models.py:172
 | 
					#: chat/models.py:170
 | 
				
			||||||
msgid "created at"
 | 
					msgid "created at"
 | 
				
			||||||
msgstr "créé le"
 | 
					msgstr "créé le"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: chat/models.py:177
 | 
					#: chat/models.py:175
 | 
				
			||||||
msgid "updated at"
 | 
					msgid "updated at"
 | 
				
			||||||
msgstr "modifié le"
 | 
					msgstr "modifié le"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: chat/models.py:182
 | 
					#: chat/models.py:180
 | 
				
			||||||
msgid "content"
 | 
					msgid "content"
 | 
				
			||||||
msgstr "contenu"
 | 
					msgstr "contenu"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: chat/models.py:186
 | 
					#: chat/models.py:243
 | 
				
			||||||
msgid "message"
 | 
					msgid "message"
 | 
				
			||||||
msgstr "message"
 | 
					msgstr "message"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: chat/models.py:187
 | 
					#: chat/models.py:244
 | 
				
			||||||
msgid "messages"
 | 
					msgid "messages"
 | 
				
			||||||
msgstr "messages"
 | 
					msgstr "messages"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: chat/templates/chat/chat.html:8
 | 
					#: chat/templates/chat/chat.html:8
 | 
				
			||||||
msgid "JavaScript must be enabled on your browser to access chat."
 | 
					msgid "JavaScript must be enabled on your browser to access chat."
 | 
				
			||||||
msgstr ""
 | 
					msgstr "JavaScript doit être activé sur votre navigateur pour accéder au chat."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: chat/templates/chat/chat.html:12
 | 
					#: chat/templates/chat/chat.html:12
 | 
				
			||||||
msgid "Chat channels"
 | 
					msgid "Chat channels"
 | 
				
			||||||
msgstr "Canaux de chat"
 | 
					msgstr "Canaux de chat"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#: chat/templates/chat/chat.html:43
 | 
					#: chat/templates/chat/chat.html:40
 | 
				
			||||||
 | 
					msgid "Fetch previous messages…"
 | 
				
			||||||
 | 
					msgstr "Récupérer les messages précédents…"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#: chat/templates/chat/chat.html:52
 | 
				
			||||||
msgid "Send message…"
 | 
					msgid "Send message…"
 | 
				
			||||||
msgstr "Envoyer un message…"
 | 
					msgstr "Envoyer un message…"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user