diff --git a/lib/__tests__/unified-mailbox-cross.test.ts b/lib/__tests__/unified-mailbox-cross.test.ts index 9c261a546..28589e426 100644 --- a/lib/__tests__/unified-mailbox-cross.test.ts +++ b/lib/__tests__/unified-mailbox-cross.test.ts @@ -45,6 +45,14 @@ describe('getCrossIncludedMailboxes', () => { expect(ids).toEqual(['inbox', 'projects']); }); + it('excludes an RFC 6154 All mailbox, which would pull in sent/archive mail (#1241)', () => { + const account = makeAccount({ + accountId: 'a', + mailboxes: [mb('inbox', 'inbox'), mb('all', 'all'), mb('sent', 'sent')], + }); + expect(getCrossIncludedMailboxes(account).map((m) => m.id)).toEqual(['inbox']); + }); + it('honors an explicit crossIncludedMailboxIds selection (folder picker)', () => { const account = makeAccount({ accountId: 'a', @@ -109,6 +117,14 @@ describe('getCrossUnreadTotal', () => { expect(getCrossUnreadTotal([a, b])).toBe(10); }); + it('does not count inbox unread twice through an all mailbox (#1241)', () => { + const a = makeAccount({ + accountId: 'a', + mailboxes: [mb('inbox', 'inbox', 1), mb('all', 'all', 1), mb('sent', 'sent', 0)], + }); + expect(getCrossUnreadTotal([a])).toBe(1); + }); + it('counts only the selected folders when crossIncludedMailboxIds is set; shared accounts stay unrestricted', () => { // personal account narrowed to inbox only (projects excluded by the picker) const personal = makeAccount({ diff --git a/lib/jmap/types.ts b/lib/jmap/types.ts index befd90f9f..04b906186 100644 --- a/lib/jmap/types.ts +++ b/lib/jmap/types.ts @@ -1077,8 +1077,10 @@ export function isCrossViewId(id: string): boolean { /** * Mailbox roles excluded from the cross-account views. Everything else (inbox - * and custom/no-role folders) is included. + * and custom/no-role folders) is included. `all` (RFC 6154 \All) holds every + * message, so including it would bring back sent/archive mail and count inbox + * unread twice. */ export const CROSS_EXCLUDED_ROLES: ReadonlySet = new Set([ - 'junk', 'sent', 'archive', 'trash', 'drafts', + 'junk', 'sent', 'archive', 'trash', 'drafts', 'all', ]); diff --git a/lib/unified-mailbox.ts b/lib/unified-mailbox.ts index 8fac05d5c..4e8815db1 100644 --- a/lib/unified-mailbox.ts +++ b/lib/unified-mailbox.ts @@ -370,7 +370,7 @@ export function fetchUnifiedMailboxCounts( // ─── Cross-account views (unread / starred / all) ───────────────────────────── // // These merge messages across EVERY account (including shared) and across all -// folders except the CROSS_EXCLUDED_ROLES (junk, sent, archive, trash, drafts), +// folders except the CROSS_EXCLUDED_ROLES (junk, sent, archive, trash, drafts, all), // i.e. inbox + custom folders, into one date-sorted list. Unlike the per-role // unified fan-out above, the query spans many mailboxes per account, so the // filter is built from each account's included-mailbox ids.