# Done This file contains tasks which got implemented. Tasks get moved from next.md to done.md ## Tasks ## Mark as unread button in email detail Added `mark_email_unread_outlined` icon button to `EmailDetailScreen` toolbar. Calls `setFlag(seen: false)` then pops back to the list. ## Pull-to-refresh on email list Wrapped `_buildStreamBody` in a `RefreshIndicator` that calls `syncEmails`. The empty-state is now a scrollable `ListView` so the pull gesture works even when the folder has no messages. ## Show email preview snippet in list Added `preview` field to `EmailThread` (populated from the latest email in `_groupIntoThreads`). Thread tiles now show subject + a one-line preview snippet in the subtitle. ## Extract TryConnectionButton widget shared by account screens Extracted `lib/ui/widgets/try_connection_button.dart` — a stateless widget rendering the result banner (ok/error text) and the spinner/button. Both `add_account_screen` and `edit_account_screen` now use it, removing ~30 lines of duplicated UI code. ## Extract _batchMoveToRole helper in email_list_screen `_batchArchive()` and `_batchMarkSpam()` collapsed into a shared `_batchMoveToRole(role, notFoundMessage)` helper, eliminating ~20 lines of duplication. ## Enable always_use_package_imports lint rule Added rule to `analysis_options.yaml`; `dart fix --apply` converted 125 relative imports across 33 files to `package:sharedinbox/...` style automatically. ## Replace silent catch (_) with logged errors 5 `catch (_)` blocks in JMAP push stream setup and 2 in UI screens now use `catch (e)` with `log(...)` via the project's `logger.dart` wrapper. The two intentionally silent catches (malformed SSE JSON, Sent folder already exists) were left as-is since they already had explanatory comments. ## Safety hardening before real account use ### 1. Fix non-PEEK body fetch (silently sets \Seen) `lib/data/repositories/email_repository_impl.dart` ~line 163 Change `'(BODY[])'` → `'(BODY.PEEK[])'` so fetching the body does not set \Seen as a side-effect of the IMAP FETCH command. Same fix at ~line 1696 for attachment part fetches: `BODY[partId]` → `BODY.PEEK[partId]`. ### 2. Move to Trash instead of EXPUNGE `lib/data/repositories/email_repository_impl.dart` `deleteEmail` method Before enqueuing a hard delete, query the local mailboxes cache for a 'trash'-role folder for that account. - If found AND the email is not already in Trash: call `moveEmail` to that path. - If not found OR already in Trash: fall back to the existing EXPUNGE path. This makes delete reversible — the user can recover from Trash. ### 3. Confirmation dialog for delete Three call sites need a `showDialog` confirmation before deleting: a) Delete button in detail view `lib/ui/screens/email_detail_screen.dart` ~line 97 Show AlertDialog "Delete this email?" with Cancel / Delete buttons. b) Batch delete in list view `lib/ui/screens/email_list_screen.dart` `_batchDelete` ~line 268 Show AlertDialog "Delete N emails?" with Cancel / Delete buttons. c) Swipe-to-delete in list view `lib/ui/screens/email_list_screen.dart` `Dismissible.onDismissed` ~line 436 Use `Dismissible.confirmDismiss` callback (fires before the item is removed) to show a confirmation for the right-swipe (delete) direction only. Return false to cancel and keep the item in the list.