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:
@@ -2884,7 +2884,14 @@ export const useEmailStore = create<EmailStore>((set, get) => ({
|
||||
const merged: Email[] = [...refreshedEmails];
|
||||
const mergedIds = new Set(refreshedEmails.map((e: Email) => e.id));
|
||||
const insertedCount = Math.max((result.total || 0) - previousTotal, 0);
|
||||
const appendFromIndex = Math.max(refreshedEmails.length - insertedCount, 0);
|
||||
// Derive the cutoff from the page size, not from the refreshed list's
|
||||
// length: when the folder shrank (a deletion - e.g. the draft of a just
|
||||
// sent mail), the fresh page is shorter than the stale list and a
|
||||
// length-based cutoff re-appends the deleted rows from stale local
|
||||
// state. That ghost row is how "sent mail still shows as draft"
|
||||
// reports happen (#592) - and re-sending the ghost delivers the mail
|
||||
// again.
|
||||
const appendFromIndex = Math.max(emailsPerPage - insertedCount, 0);
|
||||
|
||||
for (const email of currentEmails.slice(appendFromIndex)) {
|
||||
if (!mergedIds.has(email.id)) {
|
||||
|
||||
Reference in New Issue
Block a user