searchEmails now queries local SQLite FTS5 instead of IMAP directly
(since 65173d3). The test must call syncEmails first to populate the
local index before searching.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Closes#501
searchEmails now queries the local email_fts virtual table filtered by
mailbox_path instead of doing a live IMAP SEARCH. This makes folder-view
search work offline and ensures tapped results always open the correct
email (IDs come from the same local DB that getEmail reads from).
Reuses the existing FTS5 infrastructure (_toFtsQuery + the email_fts
content-table join) from searchEmailsGlobal, adding only the
`AND e.mailbox_path = ?` filter.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The 'tapping search icon shows search bar' test was stale: the SearchBar is
now permanently visible in AppBar.bottom, so both its assertions held before
any tap. Deleted it; the existing 'SearchBar is always visible in the AppBar'
test already covers the same intent.
Added NoSplash.splashFactory to the widget-test ThemeData to prevent Flutter
from loading the pre-compiled ink_sparkle.frag shader, which was built for an
older SDK version and caused an INVALID_ARGUMENT crash on Flutter 3.44.0.
Closes#486
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Closes#373
## Summary
- **Schema v38**: two new columns on `user_preferences` — `prefetch_mode` (default `wifiOnly`) and `body_cache_limit_mb` (default 100 MB).
- **`BodyCacheService`**: queries for emails that have no cached body, fetches them newest-first in batches of 20, and evicts the oldest cached bodies when the configured size limit is exceeded.
- **Separate WorkManager task** (`si_bg_prefetch`): runs hourly with `NetworkType.unmetered` (Wi-Fi) or `NetworkType.connected` (any) depending on the user's choice. The task is cancelled when prefetch is disabled.
- **App startup**: reads the stored preference from the DB and re-registers the WorkManager task with the correct constraint.
- **Preferences screen**: radio group for prefetch mode (Wi-Fi only / Any network / Disabled) and a dropdown for cache size limit (50 / 100 / 200 / 500 MB).
## What is NOT downloaded
Binary attachments are never fetched — `getEmailBody()` stores only `textBody` and `htmlBody`. The cache size limit + per-run batch cap (20 emails) keep storage bounded even on large mailboxes.
## Test plan
- [x] `task analyze` — no issues
- [x] `task test` — all 492 tests pass (incl. updated migration_test.dart for v38)
Co-authored-by: Thomas SharedInbox <sharedinbox@thomas-guettler.de>
Reviewed-on: https://codeberg.org/guettli/sharedinbox/pulls/400
## Summary
Closes#377
- Adds a new `ImageTrustedSenders` Drift table (schema v37) that stores email addresses for which remote images are loaded automatically (per device, not per account)
- When the user taps "Load remote images", the sender's address is saved and a 3-second snackbar appears with a "Settings" hyperlink to undo the choice in preferences
- Both `EmailDetailScreen` and `ThreadDetailScreen` check the trusted senders list on open and auto-load images for known senders
- The Preferences screen gains a new "Trusted image senders" section listing all saved senders with individual remove buttons
## Test plan
- [x] `dart run build_runner build` regenerates `database.g.dart` cleanly (schema v37)
- [x] `flutter analyze` — no issues
- [x] Migration test updated: checks `image_trusted_senders` table exists after upgrade and fresh install
- [x] `FakeUserPreferencesRepository` updated with three new interface methods
- [x] All 490 unit + widget tests pass (1 pre-existing golden test failure unrelated to this change)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Thomas SharedInbox <sharedinbox@thomas-guettler.de>
Reviewed-on: https://codeberg.org/guettli/sharedinbox/pulls/378
## What changed
`searchAddresses` (used by the To/Cc/Bcc autocomplete) now runs two passes over the candidate email rows:
1. **Sent-folder rows first** — the mailboxes table is queried for mailboxes with `role='sent'`; any email row whose `mailboxPath` matches gets processed before inbox/other rows. Within this group addresses are ordered by `receivedAt` DESC as before.
2. **All other rows** — processed after sent rows, also by `receivedAt` DESC.
Within sent-folder rows, `toAddresses` and `ccJson` are checked before `fromJson` (the sender in a sent email is our own address, not a useful suggestion). For non-sent rows the original order (`fromJson`, `toAddresses`, `ccJson`) is kept.
This means: if you wrote to `info@foo.de` yesterday and received spam from `info@spam.de` today, typing "i" surfaces `info@foo.de` first.
## How verified
- All 492 unit tests pass (`task test`).
- Added a dedicated test `searchAddresses prioritises sent-folder addresses over newer received` that inserts an older sent email and a newer received email matching the same query prefix and asserts the sent-folder address is returned first.
Closes#375
Co-authored-by: Thomas SharedInbox <sharedinbox@thomas-guettler.de>
Reviewed-on: https://codeberg.org/guettli/sharedinbox/pulls/380
During _load(), check whether a password exists in secure storage and track the result
in _hasStoredPassword. The password field validator now requires user input when no
password is stored, so _tryConnection() fails fast at form validation instead of
throwing an unhandled StateError.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>