Commit Graph
111 Commits
Author SHA1 Message Date
Thomas Güttler 02585bad90 ... 2026-04-24 16:36:37 +02:00
Thomas GüttlerandClaude Sonnet 4.6 eff2940bb8 refactor: extract TryConnectionButton widget shared by account screens
Moved the result banner + spinner/button UI into a new stateless
TryConnectionButton widget, used by both add and edit account screens.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 16:35:10 +02:00
Thomas GüttlerandClaude Sonnet 4.6 d1f77d3eb6 refactor: extract _batchMoveToRole helper in email_list_screen
_batchArchive and _batchMarkSpam shared identical role-lookup+move logic;
collapsed into a single _batchMoveToRole(role, notFoundMessage) helper.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 16:32:22 +02:00
Thomas GüttlerandClaude Sonnet 4.6 e3ba18285d refactor: enforce always_use_package_imports across all lib files
Added lint rule to analysis_options.yaml and ran dart fix --apply to convert
125 relative imports in 33 files to package:sharedinbox/... style.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 16:30:59 +02:00
Thomas GüttlerandClaude Sonnet 4.6 281acdf665 fix: log errors in silent catch (_) blocks instead of swallowing them
JMAP push stream failures and UI-layer search/discovery errors now emit
a log line via the project logger so they are visible during debugging.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 16:28:42 +02:00
Thomas GüttlerandClaude Sonnet 4.6 e7d172eba5 feat: safety hardening before real account use
- Fix BODY[] → BODY.PEEK[] for body and attachment fetches so opening an
  email does not silently set \Seen as an IMAP side-effect
- deleteEmail now moves to Trash (if a trash-role mailbox exists) instead
  of hard-deleting with UID EXPUNGE; falls back to EXPUNGE only when
  already in Trash or no Trash folder is found
- Add confirmation dialogs to all three delete entry-points: detail-view
  delete button, batch-delete in list view, and swipe-to-delete

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 15:48:22 +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 acf9d14043 feat: multi-select with batch Archive/Delete/Spam/Move in email list
Long-press any email to enter selection mode. Tap items to toggle
checkboxes. AppBar shows count and close button. Bottom bar provides
Archive, Delete, Mark as spam, and Move to folder actions for all
selected emails. Swipe gestures are disabled while selecting.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 08:36:51 +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üttlerandClaude Sonnet 4.6 a33e794583 docs: remove completed Sieve task from LATER.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 08:04:26 +02:00
Thomas GüttlerandClaude Sonnet 4.6 93e5829b49 feat: JMAP Sieve script management (email filters)
Add live-connection Sieve script management for JMAP accounts via
RFC 9661: list, create, edit, delete, and activate scripts.
Accessible via "Email filters" in the account popup menu.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 08:01:53 +02:00
Thomas GüttlerandClaude Sonnet 4.6 95588def04 feat: add flutter linter rules for widget best practices
Add use_super_parameters, use_key_in_widget_constructors, and
avoid_positional_boolean_parameters to enforce modern Dart idioms and
prevent widget state-loss bugs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 07:31:31 +02:00
Thomas Güttler aa8d59453b avoid accidental complete delete. 2026-04-24 07:15:15 +02:00
Thomas GüttlerandClaude Sonnet 4.6 fb805201a9 docs: remove already-implemented sync reliability task from NEXT.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 06:49:44 +02:00
Thomas GüttlerandClaude Sonnet 4.6 24ca243edc feat: show content type alongside size in attachment list
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 06:40:12 +02:00
Thomas Güttler b814a3736b fix test. 2026-04-23 17:43:20 +02:00
Thomas GüttlerandQwen-Coder 5984137bdc refactor: use open_filex instead of open_file with xdg-open workaround
- Replace open_file + manual xdg-open with open_filex (maintained fork)
- open_filex has better Linux support built-in
- Removes platform-specific code, cleaner implementation

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-04-22 10:52:45 +02:00
Thomas GüttlerandQwen-Coder 052dd6426d docs: remove completed attachment task from NEXT.md
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-04-22 10:47:48 +02:00
Thomas GüttlerandQwen-Coder a51c2dad9c feat: attachment open via xdg-open on Linux, mime type detection
- Use xdg-open directly on Linux for opening attachments (fixes 'file type not supported' error)
- Add mime package for comprehensive MIME type detection in compose screen
- Show file size and MIME type for attachments in compose screen
- Add open/preview button for attachments in compose screen

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
2026-04-22 10:47:11 +02:00
Thomas GüttlerandClaude Sonnet 4.6 16607f7ea0 test: add JMAP Stalwart integration tests; fix Email/set destroy array
- New test/integration/email_repository_jmap_test.dart covers full sync,
  incremental sync, server-side deletion reconciliation, getEmailBody
  with cache, sendEmail, and flushPendingChanges (flag, delete, move).
- Fix: Email/set destroy must be an array per RFC 8620 §5.3; was passing
  a bare string which Stalwart silently rejected.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 21:45:42 +02:00
Thomas GüttlerandClaude Sonnet 4.6 e7c87f6d60 feat: JMAP attachment download via blob URL
Adds downloadBlob() to JmapClient (RFC 8620 §6 downloadUrl template)
and routes downloadAttachment() to it for JMAP accounts. IMAP path
is unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 21:22:32 +02:00
Thomas GüttlerandClaude Sonnet 4.6 f623a76925 docs: remove completed search everywhere task from NEXT.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 21:12:06 +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 4f9649e60f docs: remove completed NEXT.md items
Bold unread emails was already implemented. All four items are done.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 17:08:25 +02:00
Thomas GüttlerandClaude Sonnet 4.6 1c63d3dc31 feat: burger menu with folder list and account name in top bar
- FolderDrawer widget: shows account name/email in header, sorted
  folder list with unread badges; tapping a folder navigates via
  context.go (replaces route rather than stacking)
- MailboxListScreen and EmailListScreen both get the drawer (burger
  icon auto-appears) and the account display name in the AppBar
- Folder sort logic moved to folder_drawer.dart (shared)
- accountByIdProvider added to di.dart for reactive account lookup

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 17:07:35 +02:00
Thomas GüttlerandClaude Sonnet 4.6 edd10201f1 feat: sort folders and bold unread in mailbox list
INBOX sorts first, Drafts second, remaining folders alphabetically.
Folder names with unread messages are shown in bold.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 17:01:43 +02:00
Thomas GüttlerandClaude Sonnet 4.6 bdc9d3cd5a fix: tapping account navigates to mailbox list
Tapping an account tile now opens the folder list rather than jumping
straight to INBOX. The redundant "All mailboxes" popup entry is removed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 16:50:15 +02:00
Thomas GüttlerandClaude Sonnet 4.6 9d2c297894 fix: suppress IMAP trace logging from stdout
isLogEnabled now depends solely on the verbose zone flag, not kDebugMode.
Logs only flow to the sync log when account.verbose is true; stdout stays
silent in all builds.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 16:45:00 +02:00
Thomas GüttlerandClaude Sonnet 4.6 39cf6b7f6a refactor: remove SettingsScreen, merge into AccountListScreen
The Settings page was a duplicate of the accounts list with less
functionality. AccountListScreen now has a "Sync log" entry in each
account's popup menu and no longer has a settings icon in the app bar.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 16:42:05 +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 81410c337a feat: enable IMAP trace logging in debug builds
Sets isLogEnabled=kDebugMode on ImapClient so raw IMAP traffic appears
in the debug console without touching release builds.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 16:20:54 +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 44d5307ff8 fix: enable WAL mode and busy_timeout on the SQLite database
Multiple account sync loops share one DB. Without WAL, a write
transaction from one account blocks reads from another, causing
SQLITE_BUSY (code 5) errors even on SELECTs.  WAL mode lets readers
and writers proceed concurrently.  busy_timeout = 5 s makes SQLite
retry instead of immediately surfacing SQLITE_LOCKED on the rare
writer-writer conflict that WAL doesn't eliminate.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 13:55:47 +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 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