feat(compose): inline images #77

Closed
opened 2026-07-01 21:22:49 +00:00 by JMR-dev · 0 comments
JMR-dev commented 2026-07-01 21:22:49 +00:00 (Migrated from github.com)

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).
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: JMR-dev/LibreMail#77