Commit Graph
86 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 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 64fdc53bbd refactor(A1): extract EmailDetailNotifier, drop initState DB coupling (#39) 2026-05-14 09:49:38 +02:00
Bot of Thomas Güttler 92e91d9fad fix(R3): wrap flutter_html in error boundary to prevent screen crash (#35) 2026-05-14 08:27:02 +02:00
Bot of Thomas Güttler 3125713e6b refactor(A2): extract shared EmailTile widget (#33) 2026-05-14 05:20:11 +02:00
Bot of Thomas Güttler 7096c27ede feat(U6): show sync status indicator in email list app bar (#29) 2026-05-14 04:23:07 +02:00
Bot of Thomas Güttler 7421855922 feat(U1): show Unsubscribe chip in email detail (#26) 2026-05-14 00:09:14 +02:00
Bot of Thomas Güttler 855f9a3a6d feat(S2): validate IMAP/SMTP hostnames against injection (#25) 2026-05-13 23:49:30 +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 58f4b13c6a ui: stabilize email list during selection (Issue #14) 2026-05-11 08:35:38 +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 9815e105d3 fix: improve crash reporting on Codeberg
- Pre-fill Codeberg issue with crash details (title and body).
- Remove unreliable canLaunchUrl check.
- Add SnackBar error handling if launch fails.
- Add https intent to Android queries manifest for better link visibility.
- Add widget test for CrashScreen.
2026-05-10 22:21:09 +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 0855049c30 ui: move undo log access to drawer 2026-05-10 18:43:19 +02:00
Thomas SharedInbox c1fbf7ece1 Merge branch 'feat/issue-7-undo-log' 2026-05-10 14:45:11 +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 3ca71cd4e5 feat: add hyperlinks to Codeberg commits in ChangeLog (Issue #9) 2026-05-10 13:18:35 +02:00
Thomas SharedInbox 2ecb29d6ee feat: implement in-app ChangeLog from git history 2026-05-10 11:23:39 +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
guettlibotandThomas SharedInbox 6d58ee1e00 Fix Issues 1, 2, and 3: Headers, Undo, and Crash Reporting (#6)
## Overview
This PR implements several fixes and enhancements requested in the latest session:

### Fixes
1. **Issue 1: Raw Email Headers**
   - Added database support for raw headers.
   - Added a new Headers tab in the email detail screen with a zebra-colored table display.
2. **Issue 2: Exception on Undo of Delete**
   - Added `toJson` and `fromJson` to `EmailAddress` model to fix serialization during undo.
3. **Issue 3: Crash Reporting**
   - Added a button to the Crash Screen to report issues directly on Codeberg.

### Infrastructure
- Added Nix experimental features check to `Taskfile.yml` to ensure a consistent dev environment.

## Verification
- Manually verified the Headers display on Linux.
- Verified Undo for IMAP and JMAP accounts.
- Verified the Crash Screen button.

Co-authored-by: Thomas SharedInbox <sharedinbox@thomas-guettler.de>
Reviewed-on: https://codeberg.org/guettli/sharedinbox/pulls/6
2026-05-09 18:49:34 +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 674b21298d feat: implement exponential backoff and permanent error handling for sync engine 2026-05-09 13:25:59 +02:00
Thomas SharedInbox 03a68a00c6 feat: implement periodic sync reliability verification and health indicator 2026-05-09 09:47:42 +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 656d4b46d7 Optimize deployment, fix E2E flakiness, and implement database-backed threading
- Optimize task deploy-android with marker files and source/generate tracking.
- Fix flaky Android E2E test with pumpAndSettle and safety delays.
- Implement global CrashScreen and error handlers in main.dart.
- Refactor threading to use a persistent Threads table for performance.
- Add database indexes and migration for schema v18.
- Enhance coverage gate with ghost path checks and increased coverage (82%).
2026-05-07 22:07:54 +02:00
Thomas GüttlerandClaude Sonnet 4.6 569273d7ff feat: restrict plain-text connections to localhost only
Hides the SSL/TLS toggle in add/edit account screens when the host is
not localhost; enforces SSL in connectImap/connectSmtp for non-localhost
hosts so plaintext can never be configured accidentally.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 07:35:56 +02:00
Thomas GüttlerandClaude Sonnet 4.6 d4d61b2b39 feat: sync-now button on sync log screen
Adds a sync icon to the AppBar of the sync log screen. Tapping it wakes
the account's idle/wait loop immediately and shows a spinner until the
next log entry arrives.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 07:23:34 +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 fc270590c4 feat: ManageSieve (RFC 5804) Sieve script editing for IMAP accounts
Adds a minimal ManageSieve client so the existing "Email filters" UI
works for IMAP accounts, not just JMAP. SieveRepository becomes a
dispatcher that routes to JMAP or ManageSieve based on account.type.

Account model + DB schema v15 grow manageSieveHost/Port/Ssl fields
(default 4190 / TLS, host falls back to imapHost when blank). The Add
and Edit account screens expose them inside a collapsed ExpansionTile
to keep the form short for users who accept defaults.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 06:53:31 +02:00
Thomas GüttlerandClaude Opus 4.7 44d02afc46 feat: render HTML email bodies via flutter_html
Email detail screen now renders the message htmlBody with the flutter_html
widget when present, falling back to SelectableText for plain-text-only mail.
Remote http(s) images are blocked by default to defeat tracking pixels; an
opt-in "Load remote images" button reveals them per-screen.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 20:10:18 +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 65c75c365a feat: replace custom search TextField with Flutter SearchBar widget
SearchBar is always visible in the AppBar bottom slot — no toggle needed.
Removed _isSearching, manual debounce timer, and slide animation.
SearchController listener clears results when text is emptied.
Updated E2E and widget tests for the new widget tree.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 08:04:20 +02:00
Thomas GüttlerandClaude Sonnet 4.6 e22418c6dd feat: add Email filters entry to FolderDrawer for JMAP accounts
The Sieve script editor was only reachable via the hidden popup menu on
the account list. Adding an 'Email filters' tile to the drawer makes it
discoverable from any mailbox view for JMAP accounts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 07:40:01 +02:00
Thomas GüttlerandClaude Sonnet 4.6 527683172c feat: bulk actions on search results via long-press selection
Long-pressing a search result enters selection mode; tapping further
results toggles them. The existing bottom bar (Archive, Delete, Mark
as spam, Move to folder) operates on the selected emails via a new
_selectedSearchIds set. _selectedEmailIds returns the right IDs for
each mode (search vs. normal thread list).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 07:32:44 +02:00
Thomas GüttlerandClaude Sonnet 4.6 0103454e31 feat: add 'All accounts' entry to FolderDrawer for back-navigation
Users had no obvious way to return to the account list after opening an
account. Adding an 'All accounts' tile at the top of the drawer (visible in
both MailboxListScreen and EmailListScreen) lets users navigate to /accounts
from anywhere inside an account.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 07:20:16 +02:00
Thomas GüttlerandClaude Sonnet 4.6 c4928ef362 fix: Android E2E — robust account-tile finder, search debounce, DEFUNCT error filter
- pumpUntil uses ListTile-scoped finder so it doesn't exit early when
  'Alice' is still in the form's EditableText before navigation pops
- tap(aliceTile) reuses that same finder instead of a second find.text
- EmailListScreen search bar adds onChanged debounce (300ms) so the
  test never needs receiveAction(TextInputAction.search), which caused
  a keyboard-dismiss animation that triggered layout overflow in
  disposed render objects
- FlutterError.onError filter in the test suppresses DEFUNCT/DISPOSED
  overflow errors from Android's route-teardown layout passes
- integration_android_test.sh: force-stop + pm clear before uninstall
  so stale app data can't bleed into subsequent runs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 20:04:31 +02:00
Thomas GüttlerandClaude Sonnet 4.6 077ddbd9c3 fix: Android app startup — INTERNET permission, MobSF scan, E2E robustness
- Add INTERNET permission to main AndroidManifest.xml (was missing from
  release builds, causing all network calls to fail on device)
- Add scripts/mobsf_scan.sh: uploads release APK to MobSF after each
  build and asserts required permissions are declared; docker pull -q
  suppresses progress-bar noise
- Wire MobSF scan into build-android task; add mobsf-stop convenience task
- Fix _AccountTile subtitle overflow on Android: replace Column([Text,Text])
  with single Text('email\ntype') so ListTile can measure height correctly
- E2E test robustness on Android: use pumpUntil(find.text('Alice')) instead
  of pumpUntil(FAB)+expect to handle Drift background-isolate stream delay;
  add skipOffstage:false to tap; remove stale email-address assertion
- Uninstall app before each Android integration test run to clear leftover
  DB state and prevent "Unable to start the app" on repeated runs
- Update widget tests to use find.textContaining for merged subtitle text

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 19:02:19 +02:00
Thomas GüttlerandClaude Sonnet 4.6 d3646e350b fix: surface sync errors via snackbar instead of silently swallowing them
The sync button used a fire-and-forget lambda — any exception from
syncEmails was discarded with no feedback. Now it awaits the call and
shows a snackbar on failure, making errors visible in the UI and in tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 21:38:40 +02:00
Thomas GüttlerandClaude Sonnet 4.6 8a51496181 fix: INBOX sync misses SMTP-delivered mail due to Stalwart CONDSTORE bug
Stalwart 0.14.x does not increment HIGHESTMODSEQ when new mail arrives
via SMTP delivery, so the incremental sync's CONDSTORE fast-path saw
serverModSeq == storedModSeq and returned early — silently skipping
UID SEARCH and missing any newly received messages.

Fix: remove the early-return fast-path. Incremental sync now always
runs UID SEARCH UID ${lastUid+1}:* to discover new messages. CONDSTORE
is still used for the flag-refresh gate (only runs when modseq changed),
which is its correct, narrower role.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 17:55:52 +02:00
Thomas GüttlerandClaude Sonnet 4.6 71692665cc feat: quoted reply body and Forward button in email detail
Reply and reply-all now prefill the compose body with the original message
quoted as plain text (> lines). New Forward button sets Fwd: subject and
the same quoted body, leaving To/Cc empty for the user to fill.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 16:48:19 +02:00
Thomas GüttlerandClaude Sonnet 4.6 cdd5e5a6fc feat: mark as unread button in email detail screen
Added mark_email_unread_outlined icon to the toolbar. Calls setFlag(seen:false)
then pops back to the list so the email reappears as unread.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 16:46:09 +02:00
Thomas GüttlerandClaude Sonnet 4.6 14b1ee14ac feat: pull-to-refresh on email list screen
Wrapped the stream body in RefreshIndicator calling syncEmails.
Empty-state is now a scrollable ListView so the pull gesture triggers
even when the folder contains no messages.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 16:44:53 +02:00
Thomas GüttlerandClaude Sonnet 4.6 39c3d1ea1a feat: show email preview snippet in thread list tiles
Added preview field to EmailThread (from latest email's preview via
_groupIntoThreads). Thread tiles now show subject + one-line body snippet.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 16:43:35 +02:00