Files
webmail/lib/offline/outbox-hooks.ts
T
Linus Rath db5ac82f53 feat: keep recent mail on this device and send mail written offline #523
Settings > Backup & reset gets "Keep mail on this device", off until the
user turns it on in this browser. While it is on:

- the JMAP client hands the server's answers to an encrypted cache in
  IndexedDB (AES-GCM, non-extractable key) and answers from it when the
  network is gone: the folder list, the last 30 days of the Inbox and of
  the folders the user opened, bodies and attachments, at most 250 MB,
  oldest out first; a background sync keeps it current
- read, flag, tags and moves made offline are queued and sent in order
  when the server answers again; deleting for good and emptying a folder
  are refused offline
- the composer, quick reply and the S/MIME and PGP plugins' sends that
  complete the composer's go to an outbox once every step up to the
  transport ran (checks, label, signature, plugin send checks); only the
  finished send waits, with files attached offline
- the service worker keeps this build's app shell, so the app also
  starts offline from the kept session and the last policy
- a full sign-out, the idle sign-out and signing one account out delete
  it, a sign-out the user starts asks first when mail is unsent, and the
  login page names what an automatic sign-out deleted; a policy fetch
  that forbids it (devices.noOffline) wipes that account

Not in Lite.
2026-11-01 11:46:11 +01:00

38 lines
1.6 KiB
TypeScript

/**
* How a send reaches the outbox (#523) without the email store importing
* offline mode (which imports the stores). Registered by lib/offline/offline-mode.ts;
* null while offline mode isn't running, and then every send goes out as before.
*/
import type { SendEmailResult } from '@/lib/jmap/types';
import type { OfflineClientRef } from '@/lib/jmap/offline-hooks';
export interface RawSendOptions {
identityId: string;
delayedUntil?: string;
envelopeRecipients?: string[];
submitOnly?: boolean;
}
export interface OutboxHooks {
/** Whether a send from this client goes to the outbox now (offline, with offline mode on for its account). */
shouldQueue(client: OfflineClientRef): boolean;
/** Keeps a finished send (useEmailStore.sendEmail's arguments after the client). */
queueEmail(client: OfflineClientRef, args: unknown[]): Promise<SendEmailResult>;
/** Keeps a finished raw message (a plugin's signed or encrypted send). */
queueRaw(client: OfflineClientRef, raw: Blob, options: RawSendOptions): Promise<SendEmailResult>;
/** Uploads the files of a message that were attached offline, now that the server is back. */
resolveHeldAttachments<T extends { blobId: string }>(client: OfflineClientRef, attachments: T[]): Promise<T[]>;
}
const KEY = '__bulwarkOutboxHooks';
type HookGlobal = typeof globalThis & { [KEY]?: OutboxHooks | null };
export function setOutboxHooks(hooks: OutboxHooks | null): void {
(globalThis as HookGlobal)[KEY] = hooks;
}
export function getOutboxHooks(): OutboxHooks | null {
return (globalThis as HookGlobal)[KEY] ?? null;
}