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:
MarekWo
2026-03-31 10:28:54 +02:00
parent 6eb2250d88
commit 29e5e6982d
4 changed files with 14 additions and 12 deletions

View File

@@ -1252,7 +1252,7 @@ class DeviceManager:
'id': msg_id,
}, 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:
logger.error(f"Failed to send channel message: {e}")

View File

@@ -60,20 +60,17 @@ def recv_messages() -> Tuple[bool, str]:
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]]:
"""Send a message to a channel. Returns (success, message, msg_id)."""
def send_message(text: str, reply_to: Optional[str] = None, channel_index: int = 0) -> Dict:
"""Send a message to a channel. Returns result dict with id and timestamp."""
if reply_to:
text = f"@[{reply_to}] {text}"
try:
dm = _get_dm()
result = 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
return dm.send_channel_message(channel_index, text)
except Exception as e:
logger.error(f"send_message error: {e}")
return False, str(e), None
return {'success': False, 'error': str(e)}
# =============================================================================

View File

@@ -601,21 +601,22 @@ def send_message():
channel_idx = data.get('channel_idx', 0)
# 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
return jsonify({
'success': True,
'message': 'Message sent successfully',
'channel_idx': channel_idx,
'id': msg_id,
'id': result.get('id'),
'timestamp': result.get('timestamp'),
}), 200
else:
return jsonify({
'success': False,
'error': message
'error': result.get('error', 'Failed to send message')
}), 500
except Exception as e:

View File

@@ -1325,6 +1325,10 @@ async function sendMessage() {
const wrapper = document.querySelector(`.message-wrapper[data-msg-id="${optimisticId}"]`);
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 {
showNotification('Failed to send: ' + data.error, 'danger');
}