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
This commit is contained in:
Mechiel Lukkien 2024-11-28 17:22:01 +01:00
parent bd693805fd
commit ee48cf0dfd
No known key found for this signature in database
2 changed files with 21 additions and 12 deletions

View file

@ -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 = [];

View file

@ -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<string>((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<api.File[]>((resolve, reject) => {