Commit Graph
77 Commits
Author SHA1 Message Date
Thomas GüttlerandClaude Sonnet 4.6 fca9e1aecf fix: fire-and-forget sync log write in error catch blocks
Previously the catch block awaited _syncLog.log(), so a second DB error
(e.g. lock contention) would escape the catch, skip the log() call and
the backoff delay, and surface as an unhandled exception. Using .ignore()
means a log write failure is silently dropped but the error is always
printed to stdout and the backoff always runs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 13:28:39 +02:00
Thomas GüttlerandClaude Sonnet 4.6 2bd082e90e fix: wrap IMAP email batch insert in a single transaction
Without a transaction, N individual inserts each re-acquire the SQLite
write lock, creating a window where a concurrent sync-log write hits
SQLITE_LOCKED. The whole batch then throws, no checkpoint is saved, and
the inbox ends up with only the emails that inserted before the failure.
Wrapping in one transaction makes the batch atomic and holds the lock
for a single commit instead of N.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 12:11:44 +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üttler 593313f069 later.md 2026-04-21 08:42:26 +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
Thomas GüttlerandClaude Sonnet 4.6 5a12f9a482 test: move incremental sync test to integration
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 08:17:00 +02:00
Thomas GüttlerandClaude Sonnet 4.6 0da88bbc4b test: move syncEmails checkpoint test to integration
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 08:15:02 +02:00
Thomas GüttlerandClaude Sonnet 4.6 6a457a9f7a fix: IMAP full sync via UID SEARCH+FETCH; add sync log UI
- Replace full-sync fetchMessages(1:*) with UID SEARCH ALL + UID FETCH
  so every message gets a reliable UID on all servers
- Guard CONDSTORE select on server capability to avoid BAD from
  servers that do not advertise CONDSTORE/QRESYNC
- Add SyncLogEntry model + observeSyncLogs stream to SyncLogRepository
- Add SyncLogScreen with per-entry duration/error display
- Wire history icon in SettingsScreen → /accounts/:id/sync-log route
- Fix FakeImapClient to expose initialized serverInfo via field override

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 07:43:30 +02:00
Thomas GüttlerandClaude Sonnet 4.6 733da201ee fix: integration tests — sequential execution, IMAP timeouts, JMAP timeout
Root cause: flutter test ran all 3 integration test files in parallel
against the same Stalwart instance. Concurrent SMTP/IMAP from
email_repository_imap_test and concurrent_sync_test caused SMTP rate
limiting (4th send hung for ~27s) and flushPendingChanges race failures.

Fixes:
- stalwart-dev/test.sh: add --concurrency=1 so test files run serially
- concurrent_sync_test: reduce timeout 2 min → 30 s (tests now pass in ~2s)
- imap_client_factory + test helpers: set defaultResponseTimeout=20s on
  ImapClient so individual IMAP commands never block indefinitely
- jmap_client: reduce HTTP call timeout 30 s → 10 s (local server; keeps
  stacked-timeout total well below any reasonable per-test limit)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 06:29:49 +02:00
Thomas GüttlerandClaude Sonnet 4.6 be56232f00 feat: linting + format automation + IMAP integration tests against Stalwart
- Add `format` task (fvm dart format .) and pre-commit dart-format hook
- Fix pre-commit task-check hook to use nix develop --command task
- Add CI format-check step (dart format --set-exit-if-changed .)
- Enable directives_ordering, curly_braces_in_flow_control_structures,
  discarded_futures, unnecessary_await_in_return, require_trailing_commas
- Apply 330 trailing-comma fixes (dart fix --apply) across all files
- Wrap intentional fire-and-forget futures with unawaited() to satisfy
  discarded_futures lint in account_sync_manager, email_repository_impl,
  and UI screens
- Add test/integration/email_repository_imap_test.dart: 8 tests against
  real Stalwart (sync, body fetch+cache, send, search, flag/move/delete)
- Remove 14 fake-IMAP unit tests migrated to Stalwart integration tests
- Fix flushPendingChanges move test: create Trash folder before IMAP MOVE
- Lower coverage gate 85%→80%: IMAP paths now tested by Stalwart (real),
  not counted in unit-test lcov
- Delete LINTING.md (plan fully executed)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 18:08:09 +02:00
Thomas GüttlerandClaude Sonnet 4.6 d5a5c7fbe3 feat: IMAP CONDSTORE fast-path, JMAP blob TTL, offline compose queue UI
- IMAP CONDSTORE (RFC 7162): skip sync when HIGHESTMODSEQ is unchanged;
  refresh only changed flags via CHANGEDSINCE on incremental sync
- JMAP blob expiry: re-fetch email bodies older than 7 days (schema v8→v9
  adds nullable cachedAt column to email_bodies)
- Offline compose queue: expose stuck pending_changes rows via
  observeFailedMutations / retryMutation / discardMutation; surface them
  in a FailedMutationBanner on the mailbox list screen
- Unit tests for all three features (236 passing)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 06:32:33 +02:00
Thomas GüttlerandClaude Sonnet 4.6 650c7a70f5 docs: update DB-SYNC.md — all planned features now implemented
Move JMAP send, push, and conflict-resolution items from Next steps
into Implemented features. Replace the next-steps section with
optional future work (CONDSTORE, blob expiry, UI for stuck mutations).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 06:14:29 +02:00
Thomas GüttlerandClaude Sonnet 4.6 cf69bb71de test: concurrent IMAP + JMAP sync integration test against Stalwart
Sends 4 emails (2 per direction) between alice (IMAP) and bob (JMAP),
then concurrently syncs both accounts and verifies the in-memory Drift
DB cache has no duplicates and contains all expected rows.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 21:39:18 +02:00
Thomas GüttlerandClaude Sonnet 4.6 93ac5afbcf feat: conflict resolution hardening — server-wins policy, max-retry eviction
- Check notUpdated/notDestroyed per-item errors in Email/set; throw
  JmapSetItemException for permanent failures (notFound, forbidden) so
  they are discarded immediately rather than retried
- Add _maxChangeAttempts=5 constant; _recordChangeError() evicts the
  pending-change row when attempts reach the limit, preventing unbounded
  queue growth from transient errors
- Both IMAP and JMAP flush paths now use _recordChangeError() consistently
- Document server-wins conflict-resolution policy in DB-SYNC.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 21:05:48 +02:00
Thomas GüttlerandClaude Sonnet 4.6 795001d268 feat: JMAP push via EventSource instead of polling
- Add watchJmapPush(accountId, password) to EmailRepository; IMAP and
  JMAP-without-push return Stream.empty() so callers fall through to polling
- EmailRepositoryImpl opens an SSE (text/event-stream) connection to the
  server's eventSourceUrl; yields void on each StateChange event; properly
  cancellable via StreamController.onCancel
- _JmapAccountSync._wait() subscribes to watchJmapPush and races it against
  the 30 s poll timer and the stop signal — whichever fires first unblocks
  the next sync cycle

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 17:48:40 +02:00
Thomas GüttlerandClaude Sonnet 4.6 8d8dbc33db feat: JMAP send via EmailSubmission/set; role column on Mailboxes
- sendEmail dispatches on account type: IMAP keeps SMTP+APPEND path,
  JMAP chains Email/set create + EmailSubmission/set in one API call
- Sent mailbox looked up by role='sent' from local DB so sent mail lands
  in the right folder
- JmapClient gains uploadUrl/eventSourceUrl/capabilities from session,
  supportsSubmission getter, withSubmission flag on call(), and uploadBlob()
  for attachment upload before send
- Mailboxes table gains nullable role column (schema v8); _upsertJmapMailboxes
  persists role from JMAP Mailbox/get response

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 17:41:21 +02:00
Thomas GüttlerandClaude Sonnet 4.6 7e34ca45de feat: JMAP body caching during sync and ifInState conflict detection
- Include bodyValues/textBody/htmlBody/attachments in every Email/get call
  during syncEmails; _upsertJmapEmails writes to email_bodies so first open
  is instant even for freshly synced messages
- Extract _parseJmapBody helper shared by sync path and on-demand fetch
- Add JmapStateMismatchException; _applyPendingChangeJmap passes ifInState
  and returns newState; on stateMismatch the local checkpoint is cleared so
  the next cycle does a full re-sync before retrying the mutation
- Update DB-SYNC.md to reflect what has been implemented

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 17:21:08 +02:00
Thomas GüttlerandClaude Sonnet 4.6 db548a7d8b feat: cross-protocol sync log — record success/failure for every sync cycle
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 17:09:13 +02:00
Thomas GüttlerandClaude Sonnet 4.6 8a0e09301b feat: JMAP Email/query pagination via calculateTotal
_jmapFullEmailSync now loops with position offset until all emails are
fetched.  Each iteration sends calculateTotal=true; if the accumulated
position < total, another page is requested.  The Email state from the
first page is saved so incremental sync picks up exactly from there.

Servers that omit total (non-RFC 8620) are handled gracefully: the loop
stops after the first page.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 17:01:14 +02:00
Thomas GüttlerandClaude Sonnet 4.6 cc12a53bc1 feat: JMAP getEmailBody via Email/get with bodyValues
getEmailBody now dispatches on account type. For JMAP accounts it calls
Email/get with fetchHTMLBodyValues and fetchTextBodyValues, extracts the
first text and HTML body part via partId references, and caches the
result in email_bodies — same as the IMAP path.

Before this change, JMAP body requests fell through to the IMAP path
which would fail for accounts without IMAP credentials.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 16:55:25 +02:00
Thomas GüttlerandClaude Sonnet 4.6 bf66b2118e feat: IMAP incremental sync via sync_state checkpoints
_syncEmailsImap now stores {uidValidity, lastUid} per mailbox in the
sync_state table after each full sync.  Subsequent syncs only fetch
UIDs newer than lastUid (UID N+1:*) and then do an ALL search to
reconcile remote deletions — avoiding a full re-download on every poll.

When UID validity changes the stale local emails are discarded and a
full re-sync is performed automatically.

fake_imap: add uidValidityResult + searchCallQueue so tests can feed
distinct responses to consecutive uidSearchMessages calls.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 16:52:30 +02:00
Thomas GüttlerandClaude Sonnet 4.6 091c848d0e feat: IMAP durable outbound queue via pending_changes
setFlag/moveEmail/deleteEmail for IMAP accounts now enqueue to
pending_changes (with uid + mailboxPath in the payload) and apply an
optimistic local update, instead of calling the IMAP server directly.

flushPendingChanges dispatches on account type: JMAP uses the existing
Email/set path; IMAP opens one connection and drains all queued changes.
Connection failure marks every queued row with an incremented attempt
count so retries work correctly.

_AccountSync._sync() now calls flushPendingChanges before syncing so
queued mutations are delivered on the next poll.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 16:48:13 +02:00
Thomas GüttlerandClaude Sonnet 4.6 4bf157c550 feat: IMAP sync all folders, not just INBOX
Move ImapConnectFn typedef to imap_client_factory so it can be shared.
Inject it into AccountSyncManager/_AccountSync so tests can substitute a
no-op instead of hitting a real IMAP server.

_AccountSync._sync() now iterates all mailboxes from the repository after
syncMailboxes, mirroring the JMAP loop that was already in place.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 16:41:54 +02:00
Thomas GüttlerandClaude Sonnet 4.6 0797dd914b feat: JMAP outbound changes via pending_changes queue (Step 7)
For JMAP accounts, setFlag/moveEmail/deleteEmail now write to the
pending_changes table instead of making direct server calls, enabling
offline-first mutation with durable retries.

flushPendingChanges() drains the queue at the start of each JMAP
sync cycle via Email/set (flag updates use keyword patches; move
updates mailboxIds; delete uses Email/set destroy). On failure the
attempt count and last error are recorded; the change remains queued.

Local DB is updated optimistically on mutation so the UI responds
immediately.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 16:25:44 +02:00
Thomas GüttlerandClaude Sonnet 4.6 559eb9a467 feat: JMAP background sync worker (Step 6)
AccountSyncManager now starts a _JmapAccountSync loop for JMAP accounts
alongside the existing _AccountSync for IMAP accounts.

_JmapAccountSync:
- Syncs mailboxes then emails for each known mailbox per cycle.
- Polls every 30 seconds (no IDLE for JMAP; EventSource deferred).
- Reuses the same exponential backoff (5–300 s) on failure.
- stop() interrupts the poll wait immediately via a Completer.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 16:22:09 +02:00
Thomas GüttlerandClaude Sonnet 4.6 2efeba9d2e feat: JMAP Email sync — full and incremental (Step 5)
EmailRepositoryImpl.syncEmails now dispatches on account type.
For JMAP accounts:
- First run: Email/query (filtered by mailbox, limit 500) + Email/get
  via back-reference → upsert emails, persist state.
- Subsequent runs: Email/changes → fetch new/updated via Email/get,
  delete destroyed rows, update state in sync_state.

Maps JMAP keywords ($seen, $flagged), mailboxIds, addresses, and
hasAttachment to the existing Emails table. uid stored as 0 for
JMAP emails (unused; JMAP operations go through Email/set in Step 7).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 16:19:03 +02:00
Thomas GüttlerandClaude Sonnet 4.6 f580cd0197 feat: JMAP Mailbox sync — full and incremental (Step 4)
MailboxRepositoryImpl.syncMailboxes now dispatches on account type.
For JMAP accounts:
- First run: Mailbox/get → upsert all mailboxes, persist state.
- Subsequent runs: Mailbox/changes → fetch new/updated via Mailbox/get,
  delete destroyed rows, update state in sync_state.

path stores the JMAP mailbox ID so Email rows can reference it via
mailboxPath consistently with the IMAP convention.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 16:15:34 +02:00
Thomas GüttlerandClaude Sonnet 4.6 0054322068 feat: add JmapClient session client (Step 3)
Parses the JMAP Session object (RFC 8620 §2): fetches GET {jmapUrl},
extracts apiUrl and primary accountId, and wraps API calls via
call(methodCalls) which POSTs to apiUrl with Basic Auth.

Handles relative apiUrl, primaryAccounts fallback, and top-level
JMAP error responses. Covered by unit tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 16:11:32 +02:00
Thomas GüttlerandClaude Sonnet 4.6 c6fb5154fb feat: add pending_changes table (Step 2 — outbound sync queue)
Protocol-agnostic queue for local mutations (flag, move, delete) that
need to be sent to the server. Enables offline-first behaviour: changes
are written here first and drained by the sync worker. Tracks attempt
count and last error for durable retries.

DB schema bumped to v6.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 16:08:17 +02:00
Thomas GüttlerandClaude Sonnet 4.6 475ba34d28 feat: add sync_state table (Step 1 — DB foundation for incremental sync)
Protocol-agnostic checkpoint table stores one state token per
(account_id, resource_type). JMAP uses the opaque state string from
Mailbox/get and Email/get; IMAP will use a JSON checkpoint per mailbox.

DB schema bumped to v5.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 16:05:31 +02:00
Thomas Güttler 4cefc8aac3 deploy-android is working. 2026-04-19 15:30:42 +02:00
Thomas GüttlerandClaude Sonnet 4.6 9f12a1aa9c chore: remove completed 'Draft auto-save' from LATER.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 19:06:34 +02:00
Thomas GüttlerandClaude Sonnet 4.6 e1e95e97ee feat: draft auto-save in compose screen
- Add Drafts table (schema v4 migration) with autoincrement id,
  accountId, replyToEmailId, to/cc/subject/body text, updatedAt
- DraftRepository interface + DraftRepositoryImpl (Drift)
- draftRepositoryProvider wired in di.dart
- ComposeScreen debounces saves (2 s after last keystroke), shows
  transient "Saved" indicator, restores the latest matching draft on
  open when no prefill fields are provided, deletes draft on send
- 6 new unit tests for DraftRepositoryImpl
- New widget test verifying draft restore behaviour
- FakeDraftRepository added to widget test helpers
- draft_repository.dart added to coverage no-code exclusion list

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 19:06:02 +02:00
Thomas GüttlerandClaude Sonnet 4.6 2f1924be9c feat: email attachments — send, download and open
- Add file_picker and open_file dependencies
- EmailDraft gains attachmentFilePaths; EmailAttachment gains fetchPartId
- sendEmail attaches files via MessageBuilder.addFile()
- downloadAttachment fetches the specific MIME part from IMAP, caches to
  local filesystem; subsequent calls return the cached file without a
  network round-trip
- ComposeScreen: attach-file button + removable attachment list
- EmailDetailScreen: per-attachment download/open button with spinner
- 3 new unit tests covering send-with-attachment, download, and cache hit

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 17:04:25 +02:00
Thomas GüttlerandClaude Sonnet 4.6 169e563e3d security: enforce encrypted connections; pre-commit uses check-fast
IMAP/SMTP encryption:
- connectImap throws if account.imapSsl is false
- connectSmtp removes STARTTLS plaintext fallback; startTls failure is fatal
- Remove IMAP SSL/TLS toggle from add/edit account screens (always SSL)
- UI shows "IMAP (SSL/TLS)" section label to communicate the requirement

Pre-commit speed:
- Add check-fast task (analyze + unit + widget, no build-linux, no integration)
- pre-commit hook now runs task check-fast instead of task check
- task check remains the full suite for manual/CI use

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 16:16:35 +02:00
Thomas GüttlerandClaude Sonnet 4.6 b144dba5ec feat: username field separate from email, try-connection button, JMAP auth verification
- Account model gains `username` field (default empty → falls back to email then local-part)
- ConnectionTestService returns the effective username that succeeded; tries email then local-part when blank
- JMAP connection probe uses Basic-auth GET to /.well-known/jmap (401/403 = auth failure)
- IMAP/SMTP factory passes explicit username parameter
- Add/edit account screens show username field and "Try connection" button
- EditAccountScreen reuses stored password when no new password is entered
- Unit tests for ConnectionTestServiceImpl (IMAP + JMAP paths, fallback logic)
- Fix unit test lambda signatures for updated ImapConnectFn/SmtpConnectFn

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 15:37:11 +02:00
Thomas GüttlerandClaude Sonnet 4.6 442c3c4087 feat: add-account wizard, edit account, inbox tap, connection status
- Add account wizard: email-first flow with JMAP/IMAP auto-detection
  via well-known URLs; falls back to manual type selection
- Fix JMAP connection probe: GET session URL with Basic auth instead
  of the API endpoint, so 401 reliably signals bad credentials
- Account list tile: tap → open INBOX directly; popup menu for
  all mailboxes / edit / delete (with confirmation dialog)
- Show account type (JMAP/IMAP) and async connection status per tile:
  spinner while checking, green check on success, red error on failure
- Add EditAccountScreen: edit name, password, server settings; runs
  connection test only when password is changed
- Fix GTK window initialisation order so app starts with correct size
- Fix 42 lint issues (avoid_redundant_argument_values,
  unnecessary_non_null_assertion, unawaited_futures)
- 147 tests, 87% coverage, task check green

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 15:13:47 +02:00
Thomas Güttler e2a87fc2b0 UI shows add account ... nice. 2026-04-18 14:00:46 +02:00
Thomas Güttler a423c4d4ca ... 2026-04-18 13:43:42 +02:00
Thomas GüttlerandClaude Sonnet 4.6 33d1e21bc9 perf: cut integration-ui test time from 250s to 28s
- Fix HOME override that caused FVM to re-download 220MB Flutter SDK on
  every run; use XDG_DATA_HOME instead to isolate app data without
  touching HOME
- Switch DB path from getApplicationDocumentsDirectory() to
  getApplicationSupportDirectory() so XDG_DATA_HOME isolation works and
  stale accounts don't leak between test runs
- Replace fixed pump(5s/3s) waits with pumpUntil() polling at 200ms so
  tests stop waiting as soon as the UI is ready (23s of dead wait → 8s)
- Add timing instrumentation (ts() in shell, _log()/Stopwatch in Dart)
- Fix CI integration-ui job: was mixing subosito flutter with fvm flutter;
  now uses fvm consistently with actions/cache for ~/.fvm, ~/.pub-cache,
  and build/linux

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 13:25:16 +02:00
Thomas Güttler c7a121e386 stalwart-dev/integration_ui_test.sh working! great! 2026-04-18 12:05:20 +02:00
Thomas Güttler f0d3d9e6a2 task run show a window. Empty, but at least a window. 2026-04-17 22:20:10 +02:00