mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-31 16:13:15 +00:00
44 lines
1.8 KiB
TypeScript
44 lines
1.8 KiB
TypeScript
/**
|
|
* Which timestamp to show for a message.
|
|
*
|
|
* JMAP `receivedAt` is the server's internal date - the moment the message
|
|
* landed in the store. That is fine for freshly delivered mail, but it is not
|
|
* the mail's own date: an import, migration or backup restore that does not
|
|
* preserve internal dates stamps every message with the import time, so a whole
|
|
* mailbox suddenly "arrived" today (#891). `sentAt` is the RFC 5322 `Date`
|
|
* header, which survives such moves and is what Thunderbird, Outlook and Apple
|
|
* Mail display.
|
|
*
|
|
* Prefer `sentAt`; fall back to `receivedAt` when the header is missing,
|
|
* unparsable, or implausibly far in the future relative to the receive time
|
|
* (spam forges future dates to float to the top of date-sorted lists; genuine
|
|
* clock skew is minutes, not days).
|
|
*
|
|
* List *ordering* is unaffected - queries still sort by `receivedAt`, which is
|
|
* monotonic and cannot be forged by the sender.
|
|
*/
|
|
|
|
/** How far ahead of `receivedAt` a `Date` header may be before it is ignored. */
|
|
export const MAX_FUTURE_SENT_AT_MS = 24 * 60 * 60 * 1000;
|
|
|
|
interface DatedEmail {
|
|
sentAt?: string | null;
|
|
receivedAt?: string | null;
|
|
}
|
|
|
|
/** `string` when the input is guaranteed a `receivedAt` (a full `Email`), else possibly undefined. */
|
|
type DisplayDate<T extends DatedEmail> = T extends { receivedAt: string } ? string : string | undefined;
|
|
|
|
export function emailDisplayDate<T extends DatedEmail>(email: T): DisplayDate<T> {
|
|
const { sentAt, receivedAt } = email;
|
|
const fallback = (receivedAt ?? undefined) as DisplayDate<T>;
|
|
if (!sentAt) return fallback;
|
|
const sent = Date.parse(sentAt);
|
|
if (Number.isNaN(sent)) return fallback;
|
|
if (receivedAt) {
|
|
const received = Date.parse(receivedAt);
|
|
if (!Number.isNaN(received) && sent - received > MAX_FUTURE_SENT_AT_MS) return fallback;
|
|
}
|
|
return sentAt as DisplayDate<T>;
|
|
}
|