- Filter deleted emails locally in _batchDelete so the pop-back fires
immediately instead of waiting for the IMAP server to catch up.
- Add _openSearchResultAndRefresh / _refreshSearchAndPopIfEmpty so that
returning from EmailDetailScreen after deleting the last match also
pops EmailListScreen back to the caller.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When the user searches in a mailbox, selects all results, and deletes
them, re-evaluate the search. If no results remain and there is a
previous screen in the navigation stack, pop back to it instead of
clearing the search and showing the regular inbox.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
After deleting all selected emails from a search view, re-run the
search query. If no emails match any more, clear the search bar so
the user returns to the normal thread list view instead of seeing
a stale list of already-deleted messages.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When a sync failure banner appears in the email list screen, a new
'View log' button navigates directly to the account's sync log screen
so the user can see the full error details.
Also creates issue #75 for the first-snooze-in-new-account failure.
Closes#13
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add Select All button to AppBar during selection mode (#15)
- Replace Unix timestamp build number with yymmdd-hhmm format (#63)
- Gate release.yml on CI workflow success via workflow_run event
- Update golden for email_list_selection to reflect new Select All button
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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.
- 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.
- 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).
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>
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>
- 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>
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>
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>
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>
_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>
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>
- 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>
- 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>
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>
- 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>
- 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>
- EmailRepository: add searchEmails(accountId, mailboxPath, query)
- EmailRepositoryImpl: UID SEARCH with OR SUBJECT/TEXT criteria,
fetch ENVELOPE+FLAGS for matching UIDs
- EmailListScreen: toggle search bar in AppBar; submit triggers server
search; results replace the stream list; ESC/back closes search
- Refactored list into _buildList() shared by stream and search views
- README/PLAN.md updated
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- email_detail: add Move-to-folder button (bottom-sheet mailbox picker,
excludes current folder, calls moveEmail on server)
- email_list: show amber star for flagged, paperclip for attachments
in trailing area alongside date
- PLAN.md: mark phase 8 done
- README: add flag/move/attachment to working features
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
IMAP/SMTP email client with offline-first architecture:
sync engine writes to Drift (SQLite), UI reads reactively
from the local DB. enough_mail vendored under packages/.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>