fix(messages): capture content in keydown, clear editor instantly, send async
Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions

Root cause: when typing fast + Enter, previous send hadn't finished
clearing the editor, so new text accumulated. Fix: keydown handler
captures content snapshot and clears Quill SYNCHRONOUSLY, then sends
via sendContent(). No setTimeout, no race condition.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Maciej Pienczyn 2026-03-28 17:24:36 +01:00
parent bd591a2aed
commit 762bad0db1

View File

@ -1225,12 +1225,18 @@
}, },
}); });
// Enter = send (tiny delay lets Quill commit pending keystrokes) // Enter = send: capture content immediately, clear editor, send async
state.quill.root.addEventListener('keydown', function (e) { state.quill.root.addEventListener('keydown', function (e) {
if (e.key === 'Enter' && !e.shiftKey) { if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault(); e.preventDefault();
e.stopImmediatePropagation(); e.stopImmediatePropagation();
setTimeout(function() { Composer.send(); }, 10); // Snapshot content NOW before Quill processes further keystrokes
var html = state.quill.root.innerHTML;
var text = state.quill.getText().trim();
if (text) {
state.quill.setText(''); // Clear immediately
Composer.sendContent(html, text); // Send with captured content
}
} }
}, true); }, true);
@ -1271,23 +1277,36 @@
} }
}, },
send: async function () { // sendContent: called with pre-captured content (from Enter keydown)
if (!state.currentConversationId || !state.quill) return; sendContent: async function (html, text) {
if (state._isSending) return; if (!state.currentConversationId) return;
state._isSending = true; var convId = state.currentConversationId;
var savedReplyTo = state.replyToMessage;
var html = state.quill.root.innerHTML; var savedFiles = state.attachedFiles.slice();
var text = state.quill.getText().trim();
// Clear state (editor already cleared by keydown handler)
if (!text && !state.attachedFiles.length) { state.attachedFiles = [];
state._isSending = false; state.replyToMessage = null;
return; var replyPreview = document.getElementById('replyPreview');
} if (replyPreview) replyPreview.style.display = 'none';
Composer.renderAttachments();
return Composer._doSend(convId, html, text, savedReplyTo, savedFiles);
},
// send: called from button click — reads from Quill
send: async function () {
if (!state.currentConversationId || !state.quill) return;
var html = state.quill.root.innerHTML;
var text = state.quill.getText().trim();
if (!text && !state.attachedFiles.length) return;
var convId = state.currentConversationId; var convId = state.currentConversationId;
var savedReplyTo = state.replyToMessage; var savedReplyTo = state.replyToMessage;
var savedFiles = state.attachedFiles.slice(); var savedFiles = state.attachedFiles.slice();
// OPTIMISTIC UI: clear editor and show message IMMEDIATELY
state.quill.setText(''); state.quill.setText('');
state.attachedFiles = []; state.attachedFiles = [];
state.replyToMessage = null; state.replyToMessage = null;
@ -1295,6 +1314,11 @@
if (replyPreview) replyPreview.style.display = 'none'; if (replyPreview) replyPreview.style.display = 'none';
Composer.renderAttachments(); Composer.renderAttachments();
return Composer._doSend(convId, html, text, savedReplyTo, savedFiles);
},
_doSend: async function (convId, html, text, savedReplyTo, savedFiles) {
// Show optimistic message in DOM instantly // Show optimistic message in DOM instantly
var tempId = 'temp-' + Date.now(); var tempId = 'temp-' + Date.now();
var optimisticMsg = { var optimisticMsg = {