diff --git a/chat/static/chat.js b/chat/static/chat.js
index 7a0ee09..b1b7997 100644
--- a/chat/static/chat.js
+++ b/chat/static/chat.js
@@ -150,6 +150,22 @@ function receiveFetchedMessages(data) {
     redrawMessages()
 }
 
+function startPrivateChat(data) {
+    let channel = data['channel']
+    if (!channel) {
+        console.error('Private chat not found:', data)
+        return
+    }
+
+    if (!channels[channel['id']]) {
+        channels[channel['id']] = channel
+        messages[channel['id']] = new Map()
+        setChannels(Object.values(channels))
+    }
+
+    selectChannel(channel['id'])
+}
+
 function redrawMessages() {
     let messageList = document.getElementById('message-list')
     messageList.innerHTML = ''
@@ -181,6 +197,26 @@ function redrawMessages() {
         authorSpan.innerText = message['author']
         authorDiv.appendChild(authorSpan)
 
+        authorSpan.addEventListener('contextmenu', (menu_event) => {
+            menu_event.preventDefault()
+            const popover = bootstrap.Popover.getOrCreateInstance(authorSpan, {
+                'title': message['author'],
+                'content': `<a id="send-private-message-link-${message['id']}" class="nav-link" href="#" tabindex="0">Envoyer un message privé</a>`,
+                'html': true,
+                'placement': "bottom",
+            })
+            popover.show()
+
+            document.getElementById('send-private-message-link-' + message['id']).addEventListener('click', event => {
+                event.preventDefault()
+                popover.hide()
+                socket.send(JSON.stringify({
+                    'type': 'start_private_chat',
+                    'user_id': message['author_id'],
+                }))
+            })
+        })
+
         let dateSpan = document.createElement('span')
         dateSpan.classList.add('text-muted', 'float-end')
         dateSpan.innerText = new Date(message['timestamp']).toLocaleString()
@@ -234,6 +270,9 @@ document.addEventListener('DOMContentLoaded', () => {
             case 'fetch_messages':
                 receiveFetchedMessages(data)
                 break
+            case 'start_private_chat':
+                startPrivateChat(data)
+                break
             default:
                 console.log(data)
                 console.error('Unknown message type:', data['type'])
@@ -246,6 +285,7 @@ document.addEventListener('DOMContentLoaded', () => {
         socket = new WebSocket(
             (document.location.protocol === 'https:' ? 'wss' : 'ws') + '://' + window.location.host + '/ws/chat/'
         )
+        let socketOpen = false
 
         // Listen on websockets and process messages from the server
         socket.addEventListener('message', e => {
@@ -258,10 +298,11 @@ document.addEventListener('DOMContentLoaded', () => {
         // Manage errors
         socket.addEventListener('close', e => {
             console.error('Chat socket closed unexpectedly, restarting…')
-            setTimeout(() => setupSocket(2 * nextDelay), nextDelay)
+            setTimeout(() => setupSocket(socketOpen ? 1000 : 2 * nextDelay), nextDelay)
         })
 
         socket.addEventListener('open', e => {
+            socketOpen = true
             socket.send(JSON.stringify({
                 'type': 'fetch_channels',
             }))