mirror of
https://github.com/MarekWo/mc-webui.git
synced 2026-07-13 21:30:59 +02:00
fix(chat): prevent poll-triggered reload after send by using server timestamp
The 60s checkForUpdates poll was detecting has_updates due to clock skew between client and server timestamps. Now the send API returns the server timestamp, and the frontend uses it for markChannelAsRead — ensuring the poll sees no updates for own sent messages. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1252,7 +1252,7 @@ class DeviceManager:
|
|||||||
'id': msg_id,
|
'id': msg_id,
|
||||||
}, namespace='/chat')
|
}, namespace='/chat')
|
||||||
|
|
||||||
return {'success': True, 'message': 'Message sent', 'id': msg_id}
|
return {'success': True, 'message': 'Message sent', 'id': msg_id, 'timestamp': ts}
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to send channel message: {e}")
|
logger.error(f"Failed to send channel message: {e}")
|
||||||
|
|||||||
+4
-7
@@ -60,20 +60,17 @@ def recv_messages() -> Tuple[bool, str]:
|
|||||||
return True, "Messages are received automatically via events"
|
return True, "Messages are received automatically via events"
|
||||||
|
|
||||||
|
|
||||||
def send_message(text: str, reply_to: Optional[str] = None, channel_index: int = 0) -> Tuple[bool, str, Optional[int]]:
|
def send_message(text: str, reply_to: Optional[str] = None, channel_index: int = 0) -> Dict:
|
||||||
"""Send a message to a channel. Returns (success, message, msg_id)."""
|
"""Send a message to a channel. Returns result dict with id and timestamp."""
|
||||||
if reply_to:
|
if reply_to:
|
||||||
text = f"@[{reply_to}] {text}"
|
text = f"@[{reply_to}] {text}"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
dm = _get_dm()
|
dm = _get_dm()
|
||||||
result = dm.send_channel_message(channel_index, text)
|
return dm.send_channel_message(channel_index, text)
|
||||||
if result['success']:
|
|
||||||
return True, result.get('message', 'Message sent'), result.get('id')
|
|
||||||
return False, result.get('error', 'Failed to send message'), None
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"send_message error: {e}")
|
logger.error(f"send_message error: {e}")
|
||||||
return False, str(e), None
|
return {'success': False, 'error': str(e)}
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|||||||
+5
-4
@@ -601,21 +601,22 @@ def send_message():
|
|||||||
channel_idx = data.get('channel_idx', 0)
|
channel_idx = data.get('channel_idx', 0)
|
||||||
|
|
||||||
# Send message via meshcli
|
# Send message via meshcli
|
||||||
success, message, msg_id = cli.send_message(text, reply_to=reply_to, channel_index=channel_idx)
|
result = cli.send_message(text, reply_to=reply_to, channel_index=channel_idx)
|
||||||
|
|
||||||
if success:
|
if result['success']:
|
||||||
# v2: Echo tracking is handled automatically by DeviceManager events
|
# v2: Echo tracking is handled automatically by DeviceManager events
|
||||||
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'success': True,
|
'success': True,
|
||||||
'message': 'Message sent successfully',
|
'message': 'Message sent successfully',
|
||||||
'channel_idx': channel_idx,
|
'channel_idx': channel_idx,
|
||||||
'id': msg_id,
|
'id': result.get('id'),
|
||||||
|
'timestamp': result.get('timestamp'),
|
||||||
}), 200
|
}), 200
|
||||||
else:
|
else:
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'success': False,
|
'success': False,
|
||||||
'error': message
|
'error': result.get('error', 'Failed to send message')
|
||||||
}), 500
|
}), 500
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@@ -1325,6 +1325,10 @@ async function sendMessage() {
|
|||||||
const wrapper = document.querySelector(`.message-wrapper[data-msg-id="${optimisticId}"]`);
|
const wrapper = document.querySelector(`.message-wrapper[data-msg-id="${optimisticId}"]`);
|
||||||
if (wrapper) wrapper.dataset.msgId = data.id;
|
if (wrapper) wrapper.dataset.msgId = data.id;
|
||||||
}
|
}
|
||||||
|
// Use server timestamp to prevent poll-triggered reload due to clock skew
|
||||||
|
if (data.timestamp) {
|
||||||
|
markChannelAsRead(currentChannelIdx, data.timestamp);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
showNotification('Failed to send: ' + data.error, 'danger');
|
showNotification('Failed to send: ' + data.error, 'danger');
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user