Fix: stop resurrecting deleted rows in the mailbox refresh merge

Fixes #592.

refreshCurrentMailbox merges the refreshed first page with the already
loaded list, appending existing entries beyond a cutoff. That cutoff
was derived from the refreshed list's length - so whenever a folder
shrank, the fresh page was shorter than the stale list and the loop
re-appended the deleted rows from stale local state, despite the
comment right above promising the opposite.

The visible result is the reported bug: after sending a draft, the
Drafts view keeps showing a ghost row for the already-destroyed draft.
The send actually succeeded - resending the ghost delivers the mail
again, which we reproduced with a live JMAP trace: four successful
submissions, an empty server-side Drafts folder, a notFound ghost id,
and five delivered copies. Deriving the cutoff from the page size
fixes the shrink case while preserving the merge's intent for
arrivals and loaded deeper pages; regression tests cover all three
shapes.

Also surface post-send filing failures instead of dropping them, as
flagged in 4dc76bbb's follow-up note: a rejected onSuccessUpdateEmail
patch or old-draft destroy now logs the server's error details and
returns a filingError on SendEmailResult, and the UI shows a warning
toast (all 23 locales) so a stale draft row is never again mistaken
for a failed send. A plugin veto of the send leaves a debug trace.
This commit is contained in:
dealerweb
2026-07-23 13:26:05 +02:00
parent e442c55931
commit af2b00dd35
29 changed files with 197 additions and 27 deletions
+2 -1
View File
@@ -685,7 +685,8 @@
"recipient_email_placeholder": "عنوان البريد الإلكتروني",
"recipient_name_placeholder": "الاسم المعروض",
"autocomplete_search_server": "البحث في الخادم",
"autocomplete_searching": "جارٍ البحث..."
"autocomplete_searching": "جارٍ البحث...",
"send_filing_warning": "تم الإرسال - لكن التنظيف بعد الإرسال فشل، وقد تبقى مسودة قديمة."
},
"confirm_dialog": {
"confirm": "تأكيد",
+2 -1
View File
@@ -685,7 +685,8 @@
"recipient_email_placeholder": "E-mailová adresa",
"recipient_name_placeholder": "Zobrazované jméno",
"autocomplete_search_server": "Hledat na serveru",
"autocomplete_searching": "Hledání..."
"autocomplete_searching": "Hledání...",
"send_filing_warning": "Odesláno - ale následný úklid selhal, může zůstat zastaralý koncept."
},
"confirm_dialog": {
"confirm": "Potvrdit",
+2 -1
View File
@@ -685,7 +685,8 @@
"recipient_email_placeholder": "E-mailadresse",
"recipient_name_placeholder": "Visningsnavn",
"autocomplete_search_server": "Søg på serveren",
"autocomplete_searching": "Søger..."
"autocomplete_searching": "Søger...",
"send_filing_warning": "Sendt - men oprydningen bagefter mislykkedes, en forældet kladde kan blive stående."
},
"confirm_dialog": {
"confirm": "Bekræft",
+2 -1
View File
@@ -685,7 +685,8 @@
"recipient_email_placeholder": "E-Mail-Adresse",
"recipient_name_placeholder": "Anzeigename",
"autocomplete_search_server": "Auf dem Server suchen",
"autocomplete_searching": "Suche läuft..."
"autocomplete_searching": "Suche läuft...",
"send_filing_warning": "Gesendet - aber das Aufräumen danach schlug fehl, evtl. bleibt ein alter Entwurf sichtbar."
},
"confirm_dialog": {
"confirm": "Bestätigen",
+2 -1
View File
@@ -685,7 +685,8 @@
"recipient_email_placeholder": "Email address",
"recipient_name_placeholder": "Display name",
"autocomplete_search_server": "Search the server",
"autocomplete_searching": "Searching..."
"autocomplete_searching": "Searching...",
"send_filing_warning": "Sent - but the post-send cleanup failed, a stale draft may remain."
},
"confirm_dialog": {
"confirm": "Confirm",
+2 -1
View File
@@ -685,7 +685,8 @@
"recipient_email_placeholder": "Dirección de correo",
"recipient_name_placeholder": "Nombre para mostrar",
"autocomplete_search_server": "Buscar en el servidor",
"autocomplete_searching": "Buscando..."
"autocomplete_searching": "Buscando...",
"send_filing_warning": "Enviado - pero la limpieza posterior falló, puede quedar un borrador obsoleto."
},
"confirm_dialog": {
"confirm": "Confirmar",
+2 -1
View File
@@ -685,7 +685,8 @@
"recipient_email_placeholder": "آدرس ایمیل",
"recipient_name_placeholder": "نام نمایشی",
"autocomplete_search_server": "جستجو در سرور",
"autocomplete_searching": "در حال جستجو..."
"autocomplete_searching": "در حال جستجو...",
"send_filing_warning": "ارسال شد - اما پاک‌سازی پس از ارسال ناموفق بود، ممکن است پیش‌نویس قدیمی باقی بماند."
},
"confirm_dialog": {
"confirm": "تأیید",
+2 -1
View File
@@ -685,7 +685,8 @@
"recipient_email_placeholder": "Adresse e-mail",
"recipient_name_placeholder": "Nom d'affichage",
"autocomplete_search_server": "Rechercher sur le serveur",
"autocomplete_searching": "Recherche en cours..."
"autocomplete_searching": "Recherche en cours...",
"send_filing_warning": "Envoyé - mais le nettoyage après envoi a échoué, un ancien brouillon peut subsister."
},
"confirm_dialog": {
"confirm": "Confirmer",
+2 -1
View File
@@ -650,7 +650,8 @@
"recipient_email_placeholder": "כתובת דוא״ל",
"recipient_name_placeholder": "שם תצוגה",
"autocomplete_search_server": "חיפוש בשרת",
"autocomplete_searching": "מחפש..."
"autocomplete_searching": "מחפש...",
"send_filing_warning": "נשלח - אך הניקוי שלאחר השליחה נכשל, ייתכן שתישאר טיוטה ישנה."
},
"confirm_dialog": {
"confirm": "אשר",
+2 -1
View File
@@ -685,7 +685,8 @@
"recipient_email_placeholder": "E-mail-cím",
"recipient_name_placeholder": "Megjelenített név",
"autocomplete_search_server": "Keresés a kiszolgálón",
"autocomplete_searching": "Keresés..."
"autocomplete_searching": "Keresés...",
"send_filing_warning": "Elküldve - de az utólagos rendrakás nem sikerült, egy elavult piszkozat megmaradhat."
},
"confirm_dialog": {
"confirm": "Megerősítés",
+2 -1
View File
@@ -685,7 +685,8 @@
"recipient_email_placeholder": "Indirizzo email",
"recipient_name_placeholder": "Nome visualizzato",
"autocomplete_search_server": "Cerca nel server",
"autocomplete_searching": "Ricerca in corso..."
"autocomplete_searching": "Ricerca in corso...",
"send_filing_warning": "Inviato - ma la pulizia successiva non è riuscita, potrebbe restare una bozza obsoleta."
},
"confirm_dialog": {
"confirm": "Conferma",
+2 -1
View File
@@ -685,7 +685,8 @@
"recipient_email_placeholder": "メールアドレス",
"recipient_name_placeholder": "表示名",
"autocomplete_search_server": "サーバーを検索",
"autocomplete_searching": "検索中..."
"autocomplete_searching": "検索中...",
"send_filing_warning": "送信されましたが、送信後の整理に失敗しました。古い下書きが残る場合があります。"
},
"confirm_dialog": {
"confirm": "確認",
+2 -1
View File
@@ -685,7 +685,8 @@
"recipient_email_placeholder": "이메일 주소",
"recipient_name_placeholder": "표시 이름",
"autocomplete_search_server": "서버에서 검색",
"autocomplete_searching": "검색 중..."
"autocomplete_searching": "검색 중...",
"send_filing_warning": "보냈지만 전송 후 정리에 실패했습니다. 오래된 임시 보관 메일이 남아 있을 수 있습니다."
},
"confirm_dialog": {
"confirm": "확인",
+2 -1
View File
@@ -685,7 +685,8 @@
"recipient_email_placeholder": "E-pasta adrese",
"recipient_name_placeholder": "Parādāmais vārds",
"autocomplete_search_server": "Meklēt serverī",
"autocomplete_searching": "Meklē..."
"autocomplete_searching": "Meklē...",
"send_filing_warning": "Nosūtīts - bet pēcapstrāde neizdevās, var palikt novecojis melnraksts."
},
"confirm_dialog": {
"confirm": "Apstiprināt",
+2 -1
View File
@@ -685,7 +685,8 @@
"recipient_email_placeholder": "E-mailadres",
"recipient_name_placeholder": "Weergavenaam",
"autocomplete_search_server": "Op de server zoeken",
"autocomplete_searching": "Bezig met zoeken..."
"autocomplete_searching": "Bezig met zoeken...",
"send_filing_warning": "Verzonden - maar het opruimen daarna is mislukt, mogelijk blijft een oud concept staan."
},
"confirm_dialog": {
"confirm": "Bevestigen",
+2 -1
View File
@@ -685,7 +685,8 @@
"recipient_email_placeholder": "Adres e-mail",
"recipient_name_placeholder": "Wyświetlana nazwa",
"autocomplete_search_server": "Szukaj na serwerze",
"autocomplete_searching": "Wyszukiwanie..."
"autocomplete_searching": "Wyszukiwanie...",
"send_filing_warning": "Wysłano - ale późniejsze porządkowanie nie powiodło się, może pozostać nieaktualna wersja robocza."
},
"confirm_dialog": {
"confirm": "Potwierdź",
+2 -1
View File
@@ -685,7 +685,8 @@
"recipient_email_placeholder": "Endereço de e-mail",
"recipient_name_placeholder": "Nome de exibição",
"autocomplete_search_server": "Pesquisar no servidor",
"autocomplete_searching": "Pesquisando..."
"autocomplete_searching": "Pesquisando...",
"send_filing_warning": "Enviado - mas a limpeza posterior falhou, um rascunho antigo pode permanecer."
},
"confirm_dialog": {
"confirm": "Confirmar",
+2 -1
View File
@@ -685,7 +685,8 @@
"recipient_email_placeholder": "Adresă de e-mail",
"recipient_name_placeholder": "Numele afișat",
"autocomplete_search_server": "Caută pe server",
"autocomplete_searching": "Se caută..."
"autocomplete_searching": "Se caută...",
"send_filing_warning": "Trimis - dar curățarea ulterioară a eșuat, poate rămâne o ciornă veche."
},
"confirm_dialog": {
"confirm": "Confirmare",
+2 -1
View File
@@ -685,7 +685,8 @@
"recipient_email_placeholder": "Адрес эл. почты",
"recipient_name_placeholder": "Отображаемое имя",
"autocomplete_search_server": "Искать на сервере",
"autocomplete_searching": "Поиск..."
"autocomplete_searching": "Поиск...",
"send_filing_warning": "Отправлено - но последующая очистка не удалась, может остаться устаревший черновик."
},
"confirm_dialog": {
"confirm": "Подтвердить",
+2 -1
View File
@@ -685,7 +685,8 @@
"recipient_email_placeholder": "E-mailová adresa",
"recipient_name_placeholder": "Zobrazené meno",
"autocomplete_search_server": "Hľadať na serveri",
"autocomplete_searching": "Hľadanie..."
"autocomplete_searching": "Hľadanie...",
"send_filing_warning": "Odoslané - ale následné upratovanie zlyhalo, môže zostať zastaraný koncept."
},
"confirm_dialog": {
"confirm": "Potvrdiť",
+2 -1
View File
@@ -685,7 +685,8 @@
"recipient_email_placeholder": "E-posta adresi",
"recipient_name_placeholder": "Görünen ad",
"autocomplete_search_server": "Sunucuda ara",
"autocomplete_searching": "Aranıyor..."
"autocomplete_searching": "Aranıyor...",
"send_filing_warning": "Gönderildi - ancak sonrasındaki temizleme başarısız oldu, eski bir taslak kalabilir."
},
"confirm_dialog": {
"confirm": "Onayla",
+2 -1
View File
@@ -685,7 +685,8 @@
"recipient_email_placeholder": "Адреса ел. пошти",
"recipient_name_placeholder": "Відображуване ім'я",
"autocomplete_search_server": "Шукати на сервері",
"autocomplete_searching": "Пошук..."
"autocomplete_searching": "Пошук...",
"send_filing_warning": "Надіслано - але подальше очищення не вдалося, може залишитися застаріла чернетка."
},
"confirm_dialog": {
"confirm": "Підтвердити",
+2 -1
View File
@@ -685,7 +685,8 @@
"recipient_email_placeholder": "电子邮件地址",
"recipient_name_placeholder": "显示名称",
"autocomplete_search_server": "在服务器上搜索",
"autocomplete_searching": "搜索中..."
"autocomplete_searching": "搜索中...",
"send_filing_warning": "已发送,但发送后的清理失败,可能会残留旧草稿。"
},
"confirm_dialog": {
"confirm": "确认",