From ee48cf0dfd46a889ecf0a6dbf579912b9f95875d Mon Sep 17 00:00:00 2001 From: Mechiel Lukkien Date: Thu, 28 Nov 2024 17:22:01 +0100 Subject: [PATCH] webmail: fix using the compose window/popup after saving a draft message failed we kept the "save draft" promise, and would wait for it again for other operations (eg close, save again, send), which wouldn't make progress. can easily be reproduced by saving a message with a control character in an address or the subject. saving the draft will fail. for issue #256 by ally9335, thanks for reporting --- webmail/webmail.js | 17 +++++++++++------ webmail/webmail.ts | 16 ++++++++++------ 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/webmail/webmail.js b/webmail/webmail.js index 0c71cde..a7c00a5 100644 --- a/webmail/webmail.js +++ b/webmail/webmail.js @@ -2581,7 +2581,7 @@ const compose = (opts, listMailboxes) => { let draftSaveTimer = 0; let draftSavePromise = Promise.resolve(0); let draftLastText = opts.body; - const draftCancelSave = () => { + const draftCancelSaveTimer = () => { if (draftSaveTimer) { window.clearTimeout(draftSaveTimer); draftSaveTimer = 0; @@ -2598,7 +2598,7 @@ const compose = (opts, listMailboxes) => { }, 60 * 1000); }; const draftSave = async () => { - draftCancelSave(); + draftCancelSaveTimer(); let replyTo = ''; if (replytoViews && replytoViews.length === 1 && replytoViews[0].input.value) { replyTo = replytoViews[0].input.value; @@ -2619,7 +2619,12 @@ const compose = (opts, listMailboxes) => { throw new Error('no designated drafts mailbox'); } draftSavePromise = client.MessageCompose(cm, mbdrafts.ID); - draftMessageID = await draftSavePromise; + try { + draftMessageID = await draftSavePromise; + } + finally { + draftSavePromise = Promise.resolve(0); + } draftLastText = cm.TextBody; }; // todo future: on visibilitychange with visibilityState "hidden", use navigator.sendBeacon to save latest modified draft message? @@ -2630,7 +2635,7 @@ const compose = (opts, listMailboxes) => { // triggered. But we still have the beforeunload handler that checks for // unsavedChanges to protect the user in such cases. const cmdClose = async () => { - draftCancelSave(); + draftCancelSaveTimer(); await draftSavePromise; if (unsavedChanges()) { const action = await new Promise((resolve) => { @@ -2664,12 +2669,12 @@ const compose = (opts, listMailboxes) => { composeView = null; }; const cmdSave = async () => { - draftCancelSave(); + draftCancelSaveTimer(); await draftSavePromise; await withStatus('Saving draft', draftSave()); }; const submit = async (archive) => { - draftCancelSave(); + draftCancelSaveTimer(); await draftSavePromise; const files = await new Promise((resolve, reject) => { const l = []; diff --git a/webmail/webmail.ts b/webmail/webmail.ts index 4fefd5f..b15d4eb 100644 --- a/webmail/webmail.ts +++ b/webmail/webmail.ts @@ -1464,7 +1464,7 @@ const compose = (opts: ComposeOptions, listMailboxes: listMailboxes) => { let draftSavePromise = Promise.resolve(0) let draftLastText = opts.body - const draftCancelSave = () => { + const draftCancelSaveTimer = () => { if (draftSaveTimer) { window.clearTimeout(draftSaveTimer) draftSaveTimer = 0 @@ -1483,7 +1483,7 @@ const compose = (opts: ComposeOptions, listMailboxes: listMailboxes) => { } const draftSave = async () => { - draftCancelSave() + draftCancelSaveTimer() let replyTo = '' if (replytoViews && replytoViews.length === 1 && replytoViews[0].input.value) { replyTo = replytoViews[0].input.value @@ -1504,7 +1504,11 @@ const compose = (opts: ComposeOptions, listMailboxes: listMailboxes) => { throw new Error('no designated drafts mailbox') } draftSavePromise = client.MessageCompose(cm, mbdrafts.ID) - draftMessageID = await draftSavePromise + try { + draftMessageID = await draftSavePromise + } finally { + draftSavePromise = Promise.resolve(0) + } draftLastText = cm.TextBody } @@ -1518,7 +1522,7 @@ const compose = (opts: ComposeOptions, listMailboxes: listMailboxes) => { // triggered. But we still have the beforeunload handler that checks for // unsavedChanges to protect the user in such cases. const cmdClose = async () => { - draftCancelSave() + draftCancelSaveTimer() await draftSavePromise if (unsavedChanges()) { const action = await new Promise((resolve) => { @@ -1560,13 +1564,13 @@ const compose = (opts: ComposeOptions, listMailboxes: listMailboxes) => { } const cmdSave = async () => { - draftCancelSave() + draftCancelSaveTimer() await draftSavePromise await withStatus('Saving draft', draftSave()) } const submit = async (archive: boolean) => { - draftCancelSave() + draftCancelSaveTimer() await draftSavePromise const files = await new Promise((resolve, reject) => {