diff --git a/src/api/__tests__/list-properties.test.ts b/src/api/__tests__/list-properties.test.ts new file mode 100644 index 00000000..626138e8 --- /dev/null +++ b/src/api/__tests__/list-properties.test.ts @@ -0,0 +1,16 @@ +import { describe, it, expect, vi } from 'vitest'; + +vi.mock('../jmap-client', () => ({ jmapClient: { accountId: 'acc-1', request: vi.fn() } })); + +import { EMAIL_FULL_PROPERTIES, EMAIL_LIST_PROPERTIES, THREAD_HEADER_PROPERTIES } from '../email'; + +// Sort by "Date sent" compares sentAt on the device for a list merged from +// several accounts, but list rows never had it, so the order fell back to +// the received date (webmail #1219). +describe('list properties', () => { + it('include sentAt, once', () => { + for (const list of [EMAIL_LIST_PROPERTIES, EMAIL_FULL_PROPERTIES, THREAD_HEADER_PROPERTIES]) { + expect(list.filter((p) => p === 'sentAt')).toHaveLength(1); + } + }); +}); diff --git a/src/api/__tests__/live-stalwart-mail.test.ts b/src/api/__tests__/live-stalwart-mail.test.ts index 88f46e8a..cce22afb 100644 --- a/src/api/__tests__/live-stalwart-mail.test.ts +++ b/src/api/__tests__/live-stalwart-mail.test.ts @@ -445,6 +445,8 @@ live('live Stalwart: mail, contacts, search (0.16 and 1.0)', () => { const row = page.list.find((e) => e.id === id); expect(row).toBeDefined(); expect(senderWarningOf(row!)?.status).toBe('failed'); + // The Date header, for sorting by date sent in merged lists (bulwarkmail/webmail#1219). + expect(typeof row!.sentAt).toBe('string'); // The full fetch reads the same verdict from `headers`. const [full] = await getFullEmails([id]); expect(senderWarningOf(full)?.status).toBe('failed'); diff --git a/src/api/email.ts b/src/api/email.ts index 7ad3b9d2..c1f975c9 100644 --- a/src/api/email.ts +++ b/src/api/email.ts @@ -24,6 +24,9 @@ import { LIST_AUTH_RESULTS_PROPERTY } from '../lib/email-headers'; const EMAIL_SUMMARY_PROPERTIES = [ 'id', 'threadId', 'mailboxIds', 'keywords', 'size', 'receivedAt', 'from', 'to', 'cc', 'subject', 'preview', 'hasAttachment', + // Sorting by "Date sent" compares it on the device for a list merged from + // several accounts; without it the order fell back to receivedAt (#1219). + 'sentAt', ]; export const EMAIL_LIST_PROPERTIES = [ @@ -41,7 +44,7 @@ export const EMAIL_LIST_PROPERTIES = [ export const EMAIL_FULL_PROPERTIES = [ ...EMAIL_SUMMARY_PROPERTIES, 'bodyStructure', 'textBody', 'htmlBody', 'bodyValues', - 'attachments', 'blobId', 'bcc', 'replyTo', 'sentAt', + 'attachments', 'blobId', 'bcc', 'replyTo', 'messageId', 'inReplyTo', 'references', 'headers', ]; @@ -1024,7 +1027,7 @@ export async function getEmailFlags( } // What a collapsed conversation card shows; `sentAt` is the date it prints. -export const THREAD_HEADER_PROPERTIES = [...EMAIL_LIST_PROPERTIES, 'sentAt']; +export const THREAD_HEADER_PROPERTIES = EMAIL_LIST_PROPERTIES; /** A conversation's members, oldest first, without bodies. */ export interface ThreadHeaders {