Part of the compose rich-formatting epic — stacked on the rich-text foundation ticket (branch feat-richtext-foundation), which provides RichImage + <img src="cid:…"> serialization. Largest ticket — the full pipeline.
Scope
Compose UI: image picker (image/*, persistable URI grant — mirror the attachment picker in ComposeScreen.kt); toolbar button inserts a [image: name] token + RichImage(contentId) at the cursor; deleting the token drops the image; inline images tracked alongside regular attachments in ComposeUiState.
Domain: OutgoingAttachment gains contentId: String? / isInline: Boolean (update the draft JSON mapper in data/local/Mappers.kt; drafts need no Room migration).
Outbox: staging (MailRepositoryImpl.copyAttachments → cacheDir/outbox/{id}/{index}/… → SendWorker.orderedAttachments) must preserve the cid↔file pairing → add attachment metadata to OutboxEntity, Room 12→13: migration in Migrations.kt, wired in DatabaseModule.kt, exported schema committed under app/schemas, migration test (template: MIGRATION_10_11, which added bodyHtml).
SMTP (mail/SmtpSender.kt): multipart/related wrapping the alternative part; inline parts get Content-ID + inline disposition; regular attachments keep today's multipart/mixed shape.
Graph (mail/GraphSender.kt): isInline: true + contentId on the fileAttachment payload.
Acceptance
GreenMail test asserts multipart/related with a Content-ID matching the HTML's cid: reference; GraphSenderTest asserts the payload; draft save/reopen keeps token + image pairing. Reader-side cid: rendering is explicitly OUT of scope (follow-up).
Part of the compose rich-formatting epic — stacked on the rich-text foundation ticket (branch `feat-richtext-foundation`), which provides `RichImage` + `<img src="cid:…">` serialization. Largest ticket — the full pipeline.
**Scope**
- **Compose UI**: image picker (`image/*`, persistable URI grant — mirror the attachment picker in `ComposeScreen.kt`); toolbar button inserts a `[image: name]` token + `RichImage(contentId)` at the cursor; deleting the token drops the image; inline images tracked alongside regular attachments in `ComposeUiState`.
- **Domain**: `OutgoingAttachment` gains `contentId: String?` / `isInline: Boolean` (update the draft JSON mapper in `data/local/Mappers.kt`; drafts need no Room migration).
- **Outbox**: staging (`MailRepositoryImpl.copyAttachments` → `cacheDir/outbox/{id}/{index}/…` → `SendWorker.orderedAttachments`) must preserve the cid↔file pairing → add attachment metadata to `OutboxEntity`, **Room 12→13**: migration in `Migrations.kt`, wired in `DatabaseModule.kt`, exported schema committed under `app/schemas`, migration test (template: `MIGRATION_10_11`, which added `bodyHtml`).
- **SMTP** (`mail/SmtpSender.kt`): `multipart/related` wrapping the alternative part; inline parts get `Content-ID` + inline disposition; regular attachments keep today's `multipart/mixed` shape.
- **Graph** (`mail/GraphSender.kt`): `isInline: true` + `contentId` on the fileAttachment payload.
**Acceptance**
- GreenMail test asserts `multipart/related` with a `Content-ID` matching the HTML's `cid:` reference; `GraphSenderTest` asserts the payload; draft save/reopen keeps token + image pairing. Reader-side `cid:` rendering is explicitly OUT of scope (follow-up).
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Part of the compose rich-formatting epic — stacked on the rich-text foundation ticket (branch
feat-richtext-foundation), which providesRichImage+<img src="cid:…">serialization. Largest ticket — the full pipeline.Scope
image/*, persistable URI grant — mirror the attachment picker inComposeScreen.kt); toolbar button inserts a[image: name]token +RichImage(contentId)at the cursor; deleting the token drops the image; inline images tracked alongside regular attachments inComposeUiState.OutgoingAttachmentgainscontentId: String?/isInline: Boolean(update the draft JSON mapper indata/local/Mappers.kt; drafts need no Room migration).MailRepositoryImpl.copyAttachments→cacheDir/outbox/{id}/{index}/…→SendWorker.orderedAttachments) must preserve the cid↔file pairing → add attachment metadata toOutboxEntity, Room 12→13: migration inMigrations.kt, wired inDatabaseModule.kt, exported schema committed underapp/schemas, migration test (template:MIGRATION_10_11, which addedbodyHtml).mail/SmtpSender.kt):multipart/relatedwrapping the alternative part; inline parts getContent-ID+ inline disposition; regular attachments keep today'smultipart/mixedshape.mail/GraphSender.kt):isInline: true+contentIdon the fileAttachment payload.Acceptance
multipart/relatedwith aContent-IDmatching the HTML'scid:reference;GraphSenderTestasserts the payload; draft save/reopen keeps token + image pairing. Reader-sidecid:rendering is explicitly OUT of scope (follow-up).