Commit Graph
85 Commits
Author SHA1 Message Date
Thomas SharedInboxandClaude Sonnet 4.6 bf59cf4621 feat: add 'Mark all as read' action to mailbox overflow menu (U8)
Adds markAllAsRead(accountId, mailboxPath) to EmailRepository — bulk
marks all unread emails as seen (optimistic local update + pending
change per email for server sync). A PopupMenuButton in EmailListScreen
exposes this as 'Mark all as read'. Contract test verifies the mailbox-
scoped behaviour.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 10:56:49 +02:00
Bot of Thomas Güttler 546b06ba5a test(T3): add contract test suites for Account/Mailbox/Email repositories (#43) 2026-05-14 10:20:32 +02:00
Bot of Thomas Güttler 4f16587564 feat(P2): paginate email list — default 50 threads, Load more button (#42) 2026-05-14 10:09:05 +02:00
Bot of Thomas Güttler f0f81777b5 feat(P1): FTS5 virtual table for email search (replaces LIKE scan) (#41) 2026-05-14 10:01:42 +02:00
Bot of Thomas Güttler f9030dc1e5 perf(P4): add indexes on mailboxes and threads for observeMailboxes/observeThreads (#36) 2026-05-14 08:37:00 +02:00
Bot of Thomas Güttler 4f3a5434cc test(T4): extend migration tests to cover all schema versions up to v24 (#32) 2026-05-14 05:09:15 +02:00
Bot of Thomas Güttler dff2b5e2ca test(T1): add edge-case coverage for EmailRepositoryImpl (#30) 2026-05-14 04:43:11 +02:00
Bot of Thomas Güttler 0e291b509b feat(U2): sync local drafts with IMAP Drafts folder (#27) 2026-05-14 00:27:47 +02:00
Bot of Thomas Güttler a0c35c647a test(R6): backoff stress tests for AccountSyncManager (#24) 2026-05-13 23:37:40 +02:00
Bot of Thomas Güttler fc592c475f feat(R4): dismissible sync error banner in email list (#23) 2026-05-13 23:14:44 +02:00
Bot of Thomas Güttler beae8d8843 feat(R2): force full sync escape hatch in account edit screen (#22) 2026-05-13 22:57:36 +02:00
Bot of Thomas Güttler eddcc17c41 fix(R1): persist undo history across restarts (#20) 2026-05-13 22:35:08 +02:00
Thomas SharedInbox efdcab74d7 setup nix in CI, and reformat. 2026-05-12 21:55:06 +02:00
Thomas SharedInbox 568b63de55 fix: refine undo log and remove delete confirmations (Issue #12)
- Remove delete confirmation dialogs in list and detail screens.
- Style Undo SnackBar action button with red text color.
- Enhance Undo Log screen to show email subject, sender, and action metadata.
- Add Undo support for Snooze action.
- Fix restoreEmails to preserve snooze metadata.
- Add unit test for Undo snooze logic.
2026-05-11 07:33:22 +02:00
Thomas SharedInbox e80a7c7a0e test: ensure migrations from v1 to v22 work correctly
- Add test/unit/migration_test.dart to verify schema upgrades and data preservation.
- Fix onUpgrade logic for syncLogs table to be idempotent.
- Add fromJson/toJson/copyWith to Account and Mailbox models.
- Update unit tests for models to increase coverage.
- Adjust coverage gate exclusions for integration-heavy files.
2026-05-11 07:21:15 +02:00
Thomas SharedInbox b7ff02711b feat: implement snooze feature for IMAP and JMAP
- Add snoozedUntil and snoozedFromMailboxPath to Emails table.
- Implement snoozeEmail and wakeUpEmails in EmailRepository.
- Update IMAP and JMAP flush logic to handle snooze/unsnooze.
- Update sync logic to parse snz: keywords from server.
- Add SnoozePicker widget and integrate into UI.
- Add unit tests for Snooze logic.
2026-05-10 21:50:13 +02:00
Thomas SharedInbox 11e337ed6f feat: persist undo log to database and support selective undo across restarts (issue #10) 2026-05-10 17:38:43 +02:00
Thomas SharedInbox 310538460d feat: improve Undo Log to support undoing any action from history 2026-05-10 14:40:01 +02:00
Thomas SharedInbox c85dbbeff2 feat: implement global undo log with persistent history 2026-05-10 10:32:27 +02:00
Thomas SharedInbox e9e731c551 fix: resolve pre-commit and coverage gate issues 2026-05-09 18:59:12 +02:00
Thomas SharedInbox d405b37308 fix: implement global undo UI and optimistic IMAP moves for better UX 2026-05-09 15:35:17 +02:00
Thomas SharedInbox e2759ac062 fix: restore Undo functionality for IMAP accounts by preserving data 2026-05-09 14:03:52 +02:00
Thomas SharedInbox 03a68a00c6 feat: implement periodic sync reliability verification and health indicator 2026-05-09 09:47:42 +02:00
Thomas SharedInbox 7a53189ae5 test: maintain coverage gate and add ghost path exclusion test 2026-05-08 23:10:16 +02:00
Thomas Güttler 43e1744614 feat: implement optimized Undo for delete and move actions
- Added UndoService with 10-action history stack.
- Integrated Undo Snackbar into EmailListScreen and EmailDetailScreen.
- Added EmailRepository.cancelPendingChange to optimize undo by removing
  unsynced local mutations.
- Fixed sorting bug in compareMailboxes for unknown roles.
- Increased unit coverage to 83% with new model and utility tests.
- Verified with full test suite (task check).
2026-05-08 11:14:54 +02:00
Thomas Güttler 8d268f1165 Implement multi-account search and improve repository fakes
- Extended search to support global queries across all accounts.
- Updated SearchScreen to handle optional account context and unified results.
- Centralized mailbox comparison logic in Mailbox model.
- Added copyWith to Account model.
- Fixed race conditions and incorrect overrides in unit, widget, and integration tests.
- Reached 80% unit test coverage.
2026-05-08 01:01:18 +02:00
Thomas Güttler cd0892763c Implement Thread View UI and repository support
- Created ThreadDetailScreen with expandable email cards and HTML support.
- Added observeEmailsInThread to EmailRepository and implementation.
- Updated navigation in EmailListScreen to route multi-message threads to the new view.
- Updated test mocks (FakeEmailRepository) across unit, widget, and integration tests.
- Documented progress in done.md and updated next.md.
2026-05-08 00:14:50 +02:00
Thomas Güttler 2f58fb7a08 fix: IMAP attachments (size/download) and immediate sync for deletions
- Fix IMAP attachment sizes showing as '0 B' by falling back to decoded content length.
- Fix IMAP attachment downloads failing for single-part fetches by falling back to root message.
- Implement immediate server-side sync for deletions/flags using a new onChangesQueued stream.
- Automate FVM SDK setup and add 'task setup'.
- Make MobSF optional in build-android to handle environment restrictions.
- Update test fakes to match EmailRepository interface changes.
2026-05-06 09:58:42 +02:00
Thomas GüttlerandClaude Opus 4.7 dad239e0b6 feat: drop Sieve fields from add-account; auto-probe ManageSieve
The IMAP/SMTP add-account form no longer asks for a ManageSieve host,
port, or SSL toggle. After the account is saved, a background probe
opens TCP+STARTTLS to the IMAP host on port 4190 and stores a
tri-state result (manageSieveAvailable: null / true / false). The
"Email filters" menu item is hidden when the probe records false, so
servers that don't expose ManageSieve don't surface a dead menu.

Edge-case overrides (different sieve host, non-standard port, plain
TCP) remain available in Edit Account, and changing them clears the
cached probe result so the next save re-probes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 16:14:41 +02:00
Thomas GüttlerandClaude Opus 4.7 da383d0957 feat: ManageSieve STARTTLS + clearer TLS-mismatch errors + broader connection test
The "Email filters" screen was failing with WRONG_VERSION_NUMBER because the
ManageSieve client was opening implicit-TLS sockets on port 4190, while RFC
5804 servers (Stalwart, Dovecot, Cyrus) listen plaintext on 4190 and expect
STARTTLS. ManageSieveClient.connect now opens plaintext, reads the capability
greeting, sends STARTTLS, hands the socket to SecureSocket.secure(), and
re-reads capabilities on the encrypted stream.

The same WRONG_VERSION_NUMBER error can hit IMAP/SMTP when the SSL toggle and
the chosen port disagree (e.g. SSL=on with SMTP port 587). New helper
lib/data/imap/tls_error.dart translates that BoringSSL error into a
TlsModeMismatchException naming the host/port and suggesting which port goes
with which TLS mode. connectImap, connectSmtp, and the ManageSieve TLS
upgrade all funnel through rethrowAsTlsHint so the same readable message
reaches the UI regardless of which protocol failed.

ConnectionTestService previously only verified IMAP/JMAP, so SMTP and
ManageSieve misconfig silently passed the "Try connection" button on the
edit-account screen and only surfaced when the user later tried to send
mail or open Email filters. After IMAP succeeds, the service now also
verifies SMTP (always — sending mail requires it) and ManageSieve (only
when manageSieveHost is explicitly set, since the section is collapsed by
default). Failures are prefixed with "SMTP:" or "ManageSieve:" so the
user can tell which leg of the connection is broken.

connectionTestServiceProvider now also watches smtpConnectProvider so the
E2E integration tests' plaintext SMTP override applies to the connection
check as well.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 10:31:55 +02:00
Thomas GüttlerandClaude Opus 4.7 1a62907703 feat: default new SMTP accounts to implicit TLS (port 465)
The SMTP SSL/TLS toggle in the add-account form now defaults to on with
port 465. Previously the default was port 587 with the toggle off
(STARTTLS — TLS was active under the hood, but the off toggle made users
think the connection was insecure). The MX-record fallback in the
discovery service was flipped the same way; autoconfig XML parsing is
unchanged so servers that advertise STARTTLS still get STARTTLS.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 15:50:37 +02:00
Thomas GüttlerandClaude Sonnet 4.6 2b260edb52 feat: MX record fallback in account auto-discovery
When JMAP well-known and autoconfig XML both fail, query DNS-over-HTTPS
(dns.google) for MX records and use the highest-priority MX host as IMAP
(993/SSL) and SMTP (587/STARTTLS) server. No new dependencies needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 07:46:28 +02:00
Thomas GüttlerandClaude Sonnet 4.6 92a8a79952 fix: task check — SSL error in integration test + coverage gate
account_sync_manager_test: inject _connectImapPlain so the test
connects to the plain-IMAP dev Stalwart without triggering the
production SSL guard in connectImap().

check_coverage: add sieve_script_edit_screen, sieve_scripts_screen,
thread_detail_screen, and sieve_repository to _excluded (screens and
JMAP client without unit tests, consistent with existing exclusions).

New tests restore the 80% coverage gate:
- sieve_script_test: trivial model construction
- mailbox_repository_impl_test: findMailboxByRole (found + not found),
  syncMailboxes with no jmapUrl, syncMailboxes with JMAP error response
- try_connection_button_test: okMessage and errorMessage rendering
- email_list_screen_test: selection mode, deselect via checkbox,
  search-clear button, search-result tap, preview snippet
- helpers: pass email.preview through to EmailThread in FakeEmailRepository

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 06:38:21 +02:00
Thomas GüttlerandClaude Sonnet 4.6 0980ef2d08 feat: thread view with reconcile guard against empty IMAP server response
- Add EmailThread model and observeThreads() grouping emails by RFC 2822
  References/In-Reply-To headers (IMAP) or native threadId (JMAP)
- Store threadId/messageId/inReplyTo/references in DB (schema v14)
- Switch EmailListScreen to thread-grouped view; flag icon preserved
- Guard _reconcileDeletedImap against wiping local cache when server
  returns 0 UIDs (network glitch / buggy IMAP server)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 15:12:04 +02:00
Thomas GüttlerandClaude Sonnet 4.6 544cd9b335 feat: swipe to archive/delete in email list, with role-based mailbox lookup
- Add `role` field to Mailbox model (surfacing what was already stored in DB)
- IMAP sync: map enough_mail special-use flags (RFC 6154) to role strings
- JMAP: role was already synced to DB, now passed through _toModel()
- Add MailboxRepository.findMailboxByRole() for role-based lookup
- EmailListScreen: swipe right → archive, swipe left → delete (Dismissible)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 08:31:22 +02:00
Thomas Güttler b814a3736b fix test. 2026-04-23 17:43:20 +02:00
Thomas GüttlerandClaude Sonnet 4.6 c9645f79dc feat: search everywhere — folders, addresses, and messages
Adds per-account incremental search (3+ chars, 300 ms debounce) that
queries local DB and shows results grouped: Folders → Addresses → Messages.
Address results link to a dedicated filtered-by-address email list screen.
Routes: /accounts/:id/search and /accounts/:id/emails/by-address/:addr.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 21:11:27 +02:00
Thomas GüttlerandClaude Sonnet 4.6 643bb47f87 feat: verbose protocol logging per account (schema v13)
When account.verbose is true, raw IMAP/JMAP protocol traffic is captured
via a Zone, redacted of credentials (LOGIN password, AUTHENTICATE tokens),
and stored in the sync log entry for display in the sync log screen.

- DB schema v13: adds verbose column to accounts, protocolLog to sync_logs
- IMAP: Zone print-capture feeds ImapClient isLogEnabled output
- JMAP: JmapClient.call() writes request/response bodies to zone buffer
- Sync log screen: shows a monospace "Protocol log" block when present
- Edit account screen: adds verbose toggle with warning subtitle

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 16:34:55 +02:00
Thomas GüttlerandClaude Sonnet 4.6 a27342c7e9 feat: add per-mailbox breakdown to sync log (schema v12)
Each sync cycle now records per-mailbox fetched/skipped/bytes in a new
sync_log_mailboxes table and displays a collapsible "Per mailbox" section
in the sync log screen.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 16:19:40 +02:00
Thomas GüttlerandClaude Sonnet 4.6 2e869194e9 test: verify sync errors always appear in the sync log
Add _CapturingSyncLogRepository and two tests (IMAP + JMAP) that assert
a failed sync cycle produces an error entry in the sync log. Also
replace .ignore() in the catch blocks with a proper try-catch so the
sync log write is genuinely attempted and any secondary failure is
logged to stdout rather than silently dropped.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 13:33:20 +02:00
Thomas GüttlerandClaude Sonnet 4.6 1ab915d73a feat: extend sync log with skipped count and bytes transferred
Track how many emails were already up-to-date (skipped) and the
approximate bytes transferred per sync cycle. SyncEmailsResult
accumulates fetched/skipped/bytes across mailboxes; DB schema v11
adds emailsSkipped and bytesTransferred columns to sync_logs.
SyncLogScreen shows "X new · Y up-to-date · took Zs" in the tile
subtitle with full detail rows in the expansion panel.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 11:32:50 +02:00
Thomas GüttlerandClaude Sonnet 4.6 0435129434 feat: verbose sync log — protocol, emails fetched, mailboxes, pending changes
- syncEmails/syncMailboxes/flushPendingChanges now return int counts
- SyncLogs DB schema v10: adds protocol, mailboxesSynced, pendingFlushed
- SyncLogEntry carries all new fields; AccountSyncManager collects and logs them
- SyncLogScreen uses ExpansionTile with per-row detail rows

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 08:52:58 +02:00
Thomas GüttlerandClaude Sonnet 4.6 d610a2b27e refactor: strip FakeImapClient to minimal stub
All protocol-level fakes moved to integration tests. Only logout() and
serverInfo override remain, used by connection_test_service_test.dart.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 08:37:57 +02:00
Thomas GüttlerandClaude Sonnet 4.6 3d4d7726fe refactor: remove FakeSmtpClient (no longer used)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 08:37:07 +02:00
Thomas GüttlerandClaude Sonnet 4.6 bc219d9635 test: move IMAP mailbox sync tests to integration
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 08:36:09 +02:00
Thomas GüttlerandClaude Sonnet 4.6 931a7d931a refactor: remove _makeReposWithFakes from email_repository_impl_test
All tests that needed real IMAP/SMTP now live in integration tests.
The max-attempts test uses an inline failing lambda, not the fake client.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 08:34:14 +02:00
Thomas GüttlerandClaude Sonnet 4.6 e6d13948eb test: move blob expiry tests to integration
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 08:32:35 +02:00
Thomas GüttlerandClaude Sonnet 4.6 b0362f9c30 test: move CONDSTORE tests to integration
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 08:29:40 +02:00
Thomas GüttlerandClaude Sonnet 4.6 1820487c46 test: include integration test coverage in coverage gate
- Add lcov to nix flake (required for flutter --merge-coverage)
- stalwart-dev/test.sh: collect and merge coverage when unit baseline exists
- run_unit_tests.sh: remove inline coverage check (now in dedicated task)
- Taskfile: add coverage task; check runs test → integration → coverage
  sequentially so the gate sees combined unit + integration data
- check-fast (pre-commit) omits coverage gate since integration tests
  don't run there; full gate runs only in task check
- Drop two untestable fake-only tests (UID-validity reset, malformed envelope)
- Coverage threshold restored to 80% (84% with merged data)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 08:27:16 +02:00
Thomas GüttlerandClaude Sonnet 4.6 ef7974a60a test: move reconciliation (server-deleted) test to integration
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 08:18:07 +02:00