feat: add per-email notes stored on IMAP/JMAP server #443

Merged
guettlibot merged 5 commits from issue-436-notes-on-emails into main 2026-06-05 17:31:37 +00:00
guettlibot commented 2026-06-05 14:52:24 +00:00 (Migrated from codeberg.org)

Closes #436

What changed

Notes can now be added and deleted on any email. Each note is stored as a message in a dedicated Notes folder on the IMAP or JMAP server, so:

  • Multi-user safe — all clients connected to the same account see the same notes
  • Move-stable — notes are keyed by the RFC 2822 Message-ID header (via X-SharedInbox-Note-For custom header), not the IMAP UID, so they survive folder moves
  • No local-only storage — the server is the source of truth; the local Drift cache is only a read-through cache

Implementation

  • Schema v39: new EmailNotes table (id, accountId, messageId, noteText, serverId, createdAt)
  • NoteRepository + NoteRepositoryImpl: IMAP uses APPEND to Notes folder with X-SharedInbox-Note-For/X-SharedInbox-Note-Id custom headers, SEARCH HEADER to sync, UID STORE \Deleted + EXPUNGE to delete. JMAP uses Email/set create/destroy in a Notes mailbox, Email/get with header:...:asText to sync.
  • di.dart: noteRepositoryProvider + notesProvider stream family
  • email_detail_screen.dart: Notes section below the email body — list with delete buttons, "Add" dialog. Notes synced from server once when the screen opens.
  • migration_test.dart: updated for schema v39

Verification

  • flutter analyze lib/ → no issues
  • flutter test test/unit/ → 335 tests, all pass
Closes #436 ## What changed Notes can now be added and deleted on any email. Each note is stored as a message in a dedicated **Notes** folder on the IMAP or JMAP server, so: - **Multi-user safe** — all clients connected to the same account see the same notes - **Move-stable** — notes are keyed by the RFC 2822 `Message-ID` header (via `X-SharedInbox-Note-For` custom header), not the IMAP UID, so they survive folder moves - **No local-only storage** — the server is the source of truth; the local Drift cache is only a read-through cache ## Implementation - **Schema v39**: new `EmailNotes` table (`id`, `accountId`, `messageId`, `noteText`, `serverId`, `createdAt`) - **`NoteRepository`** + **`NoteRepositoryImpl`**: IMAP uses `APPEND` to `Notes` folder with `X-SharedInbox-Note-For`/`X-SharedInbox-Note-Id` custom headers, `SEARCH HEADER` to sync, `UID STORE \Deleted` + `EXPUNGE` to delete. JMAP uses `Email/set` create/destroy in a `Notes` mailbox, `Email/get` with `header:...:asText` to sync. - **`di.dart`**: `noteRepositoryProvider` + `notesProvider` stream family - **`email_detail_screen.dart`**: Notes section below the email body — list with delete buttons, "Add" dialog. Notes synced from server once when the screen opens. - **`migration_test.dart`**: updated for schema v39 ## Verification - `flutter analyze lib/` → no issues - `flutter test test/unit/` → 335 tests, all pass
Sign in to join this conversation.