From c8463d18e33fa97282dbee2d2a35d374a8ca148e Mon Sep 17 00:00:00 2001 From: Abhinav Raut Date: Thu, 22 Oct 2026 01:12:14 +0530 Subject: [PATCH] fix reply editor focus after draft loading --- .../main/src/features/conversation/ReplyBox.vue | 10 ---------- .../features/conversation/ReplyBoxContent.vue | 16 +++++++++++----- .../cypress/e2e/ui/conversationLifecycle.cy.js | 4 ++++ 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/frontend/apps/main/src/features/conversation/ReplyBox.vue b/frontend/apps/main/src/features/conversation/ReplyBox.vue index 9f2373a4..2ef0c75d 100644 --- a/frontend/apps/main/src/features/conversation/ReplyBox.vue +++ b/frontend/apps/main/src/features/conversation/ReplyBox.vue @@ -787,14 +787,4 @@ watch( }, { deep: true, immediate: true } ) - -// Media files and macro state are restored per draft by the draft manager; resetting here would race ahead of the save and drop them. -watch( - () => conversationStore.current?.uuid, - () => { - setTimeout(() => { - activeContentRef()?.focus() - }, 100) - } -) diff --git a/frontend/apps/main/src/features/conversation/ReplyBoxContent.vue b/frontend/apps/main/src/features/conversation/ReplyBoxContent.vue index 2f251372..d6571a7a 100644 --- a/frontend/apps/main/src/features/conversation/ReplyBoxContent.vue +++ b/frontend/apps/main/src/features/conversation/ReplyBoxContent.vue @@ -165,7 +165,7 @@ :message-type="messageType" :placeholder="isCramped ? t('globals.terms.typeMessage') : t('editor.hint.full')" :insertContent="insertContent" - :autoFocus="true" + :autoFocus="false" :disabled="isDraftLoading" :enableMentions="messageType === 'private_note'" :enableConversationReferences="messageType === 'private_note'" @@ -502,14 +502,20 @@ watch( } else if (newType === 'private_note') { macroStore.setCurrentView('adding_private_note') } - // Focus editor on tab change - setTimeout(() => { - editorRef.value?.focus() - }, 50) }, { immediate: true } ) +watch( + [messageType, () => props.isDraftLoading, () => conversationStore.current?.uuid], + async () => { + await nextTick() + if (props.isDraftLoading || document.activeElement?.matches('input, textarea')) return + editorRef.value?.focus() + }, + { immediate: true, flush: 'post' } +) + // Expose focus method for parent components const focus = () => { editorRef.value?.focus() diff --git a/frontend/cypress/e2e/ui/conversationLifecycle.cy.js b/frontend/cypress/e2e/ui/conversationLifecycle.cy.js index 4e0e39da..5ce7a770 100644 --- a/frontend/cypress/e2e/ui/conversationLifecycle.cy.js +++ b/frontend/cypress/e2e/ui/conversationLifecycle.cy.js @@ -351,7 +351,11 @@ describe('Conversation lifecycle', () => { for (const field of ['CC', 'BCC']) { it(`keeps ${field} open and focused while replacing an address`, () => { + cy.intercept('GET', '**/api/v1/drafts', (req) => { + req.continue((res) => res.setDelay(1000)) + }).as('loadDrafts') openConversation(ccConversationUuid) + cy.wait('@loadDrafts') cy.get('.tiptap.ProseMirror[contenteditable="true"]').should('be.focused') if (field === 'BCC') { cy.contains('button', /^BCC$/).click()