feat(messaging-ui): attachments in inbox mail — reply + compose upload

InboxAdapter gains uploadAttachment + attachment params on mailReply/
composeInternal/composeExternal. MailReader reply and ComposeModal grow an
attach affordance (paperclip + pending chips); mail history renders sent
attachments. MockInboxAdapter implements upload. 67 tests green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-22 01:05:14 +05:30
parent 2f24a95ef4
commit 2aa2893973
8 changed files with 214 additions and 34 deletions
@@ -1,4 +1,4 @@
import type { InboxItem, InboxState, MailMessage, MailPerson } from './types';
import type { MailAttachment, InboxItem, InboxState, MailMessage, MailPerson } from './types';
/**
* The inbox seam. A host implements this; the SDK renders it. Mirrors MessagingAdapter's philosophy:
@@ -15,14 +15,18 @@ export interface InboxAdapter {
/** Messages of one mail thread (HTML + text parts) for the reader. */
mailHistory(threadId: string): Promise<MailMessage[]>;
/** Reply into an existing mail thread. */
mailReply(threadId: string, content: string): Promise<void>;
/** Reply into an existing mail thread, optionally with one attachment. */
mailReply(threadId: string, content: string, attachment?: MailAttachment): Promise<void>;
// ── Attachments (optional) — absent hides the attach affordance ──
/** Upload a file to storage, returning a reference to send with a reply/compose. */
uploadAttachment?(file: File): Promise<MailAttachment>;
// ── Compose (optional) — absent hides the "New message" affordance ──
/** People you can compose an in-app message to. */
directory?(): Promise<MailPerson[]>;
/** App-to-app mail (no SMTP) to a registered user's in-app inbox. */
composeInternal?(recipientUserId: string, subject: string, text: string): Promise<void>;
composeInternal?(recipientUserId: string, subject: string, text: string, attachments?: MailAttachment[]): Promise<void>;
/** External email (SMTP) to an address. */
composeExternal?(target: string, subject: string, text: string): Promise<void>;
composeExternal?(target: string, subject: string, text: string, attachments?: MailAttachment[]): Promise<void>;
}