Compare commits
1
Commits
main
...
plan-issue-474
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea860521b3 |
@@ -0,0 +1,42 @@
|
|||||||
|
## Goal
|
||||||
|
|
||||||
|
Make each email row in **Undo Log Detail** (`lib/ui/screens/undo_log_detail_screen.dart`) tappable, navigating to that email's current location in the app. The issue gates this on "when structured search is implemented" — structured search now exists (`lib/core/filter/filter_expression.dart`, `lib/ui/screens/search_screen.dart`), and the repository already exposes `findEmailByMessageId(accountId, messageId)` (`lib/data/repositories/email_repository_impl.dart:1899`), so we have the building block needed to locate an email regardless of moves.
|
||||||
|
|
||||||
|
## Why direct lookup, not a search-screen handoff
|
||||||
|
|
||||||
|
After a Move, the email lives at `destinationMailboxPath` with a *new* UID, so the `Email.id` stored in `UndoAction.originalEmails` is stale. The stable identifier across moves is `messageId`. `findEmailByMessageId` returns the current row (with current `mailboxPath` and `id`), giving us a one-tap deep link to the email detail screen — better UX than dumping the user into search.
|
||||||
|
|
||||||
|
## Implementation
|
||||||
|
|
||||||
|
### 1. `lib/ui/screens/undo_log_detail_screen.dart`
|
||||||
|
|
||||||
|
- Convert `_EmailTile` from `StatelessWidget` to `ConsumerWidget` so it can read `emailRepositoryProvider`.
|
||||||
|
- Take `accountId` as an additional ctor arg (passed in from the `originalEmails.map` call site so we don't depend on the email's own field, matching how the action scopes lookups).
|
||||||
|
- Add an `onTap` handler:
|
||||||
|
1. If `email.messageId == null` → no-op tap, show `SnackBar('Cannot locate this email — no Message-ID.')`.
|
||||||
|
2. Otherwise call `ref.read(emailRepositoryProvider).findEmailByMessageId(action.accountId, email.messageId!)`.
|
||||||
|
3. On hit, `context.go('/accounts/${accountId}/mailboxes/${Uri.encodeComponent(found.mailboxPath)}/emails/${Uri.encodeComponent(found.id)}')` — matches the encoding pattern used in `combined_inbox_screen.dart:280` and `email_list_screen.dart:540`.
|
||||||
|
4. On miss, show `SnackBar('Email no longer exists at its previous location. Use Undo to restore it.')` — covers the hard-deleted case and the not-yet-resynced case.
|
||||||
|
- Add `trailing: const Icon(Icons.chevron_right)` to give the row a "navigates" affordance consistent with other tappable `ListTile`s in the app.
|
||||||
|
- Leave styling otherwise unchanged; the existing `Icons.email_outlined` leading + subject/sender layout stays.
|
||||||
|
|
||||||
|
### 2. No router changes
|
||||||
|
|
||||||
|
The existing email-detail route (`router.dart:153`) is reused as-is.
|
||||||
|
|
||||||
|
### 3. No model / repository changes
|
||||||
|
|
||||||
|
`findEmailByMessageId` is already on `EmailRepository` and scoped per account, which is what we want.
|
||||||
|
|
||||||
|
### 4. Tests — `test/widget/`
|
||||||
|
|
||||||
|
Add a new widget test `test/widget/undo_log_detail_screen_test.dart` covering:
|
||||||
|
- Tapping a row whose `messageId` resolves via a fake `EmailRepository` navigates to `/accounts/<acc>/mailboxes/<encoded-path>/emails/<encoded-id>` (assert via a `GoRouter` test harness similar to `test/widget/email_detail_screen_test.dart`).
|
||||||
|
- Tapping a row when `findEmailByMessageId` returns `null` shows the "no longer exists" SnackBar and does not navigate.
|
||||||
|
- Tapping a row with `messageId == null` shows the "no Message-ID" SnackBar.
|
||||||
|
|
||||||
|
## Out of scope
|
||||||
|
|
||||||
|
- Adding Message-ID as a structured `FilterField` — not needed for direct navigation; can be revisited if a UI for "search for this email" is ever wanted.
|
||||||
|
- Changing the Undo Log list screen (`undo_log_screen.dart`) — the issue is specifically about the *detail* screen.
|
||||||
|
- Persisting/refreshing a stale `originalEmails` list — Move/Snooze update the row in place, so subsequent re-lookups by Message-ID will find them; nothing to maintain.
|
||||||
Reference in New Issue
Block a user