fix(socketio): register /chat namespace handler to fix real-time message delivery

The /chat namespace had no server-side connect handler registered. With
python-socketio 5.x (always_connect=False), client connections to
unregistered namespaces are silently rejected. This caused all SocketIO
events (new_message, ack, echo) to never reach the frontend — messages
only appeared via the 60s polling fallback.

Fixes:
- Add @socketio.on('connect', namespace='/chat') handler in main.py
- Add optimistic message append: sent messages appear instantly before
  API round-trip (eliminates 3-4s serial command delay)
- Skip own-message SocketIO events to prevent duplicates
- Add connect_error handler for frontend debugging
- Bump SW cache to v6

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
MarekWo
2026-03-12 08:15:43 +01:00
parent c6a2444249
commit e4a1e75cc0
4 changed files with 37 additions and 5 deletions
+1
View File
@@ -388,6 +388,7 @@ class DeviceManager:
'timestamp': ts,
'id': msg_id,
}, namespace='/chat')
logger.debug(f"SocketIO emitted new_message for ch{channel_idx} msg #{msg_id}")
except Exception as e:
logger.error(f"Error handling channel message: {e}")
+15
View File
@@ -121,6 +121,21 @@ def create_app():
return app
# ============================================================
# WebSocket handlers for Chat (real-time message push)
# ============================================================
@socketio.on('connect', namespace='/chat')
def handle_chat_connect():
"""Handle chat WebSocket connection — required for /chat namespace to accept clients."""
logger.info("Chat WebSocket client connected")
@socketio.on('disconnect', namespace='/chat')
def handle_chat_disconnect():
logger.debug("Chat WebSocket client disconnected")
# ============================================================
# WebSocket handlers for Console
# ============================================================
+20 -4
View File
@@ -397,6 +397,10 @@ function connectChatSocket() {
console.log('SocketIO connected to /chat');
});
chatSocket.on('connect_error', (err) => {
console.error('SocketIO /chat connect error:', err.message);
});
// Real-time new channel message
chatSocket.on('new_message', (data) => {
// Filter blocked contacts in real-time
@@ -410,6 +414,8 @@ function connectChatSocket() {
updateUnreadBadges();
checkAndNotify();
} else if (!currentArchiveDate) {
// Skip own messages — already appended optimistically on send
if (data.is_own) return;
// Current channel and live view — append message directly (no full reload)
appendMessageFromSocket(data);
}
@@ -1063,6 +1069,19 @@ async function sendMessage() {
const sendBtn = document.getElementById('sendBtn');
sendBtn.disabled = true;
// Optimistic append: show sent message immediately before API round-trip
input.value = '';
updateCharCounter();
const optimisticId = '_pending_' + Date.now();
appendMessageFromSocket({
id: optimisticId,
sender: window.MC_CONFIG?.deviceName || 'Me',
content: text,
timestamp: Math.floor(Date.now() / 1000),
is_own: true,
channel_idx: currentChannelIdx,
});
try {
const response = await fetch('/api/messages', {
method: 'POST',
@@ -1078,12 +1097,9 @@ async function sendMessage() {
const data = await response.json();
if (data.success) {
input.value = '';
updateCharCounter();
showNotification('Message sent', 'success');
// Message will appear via SocketIO 'new_message' event (no reload needed).
// Schedule one deferred reload to pick up echo data (echoes arrive within 5-30s).
// Schedule deferred reload to pick up echo data + replace optimistic msg with real one
setTimeout(() => loadMessages(), 15000);
} else {
showNotification('Failed to send: ' + data.error, 'danger');
+1 -1
View File
@@ -1,4 +1,4 @@
const CACHE_NAME = 'mc-webui-v5';
const CACHE_NAME = 'mc-webui-v6';
const ASSETS_TO_CACHE = [
'/',
'/static/css/style.css',