Undo Log: detail view #450

Closed
opened 2026-06-05 19:49:03 +00:00 by guettli · 2 comments
guettli commented 2026-06-05 19:49:03 +00:00 (Migrated from codeberg.org)

Currently, the undo log has only a list View.

On selecting one row the user should see a detail view.

List every mail which was part of that transaction.

If it was a move show source and target folder.

Show account of that transaction.

Currently, the undo log has only a list View. On selecting one row the user should see a detail view. List every mail which was part of that transaction. If it was a move show source and target folder. Show account of that transaction.
guettlibot commented 2026-06-05 19:55:13 +00:00 (Migrated from codeberg.org)

Now I have everything I need. Here's the plan:


Implementation Plan: Undo Log Detail View

Overview

All data needed for the detail view (accountId, type, sourceMailboxPath, destinationMailboxPath, originalEmails, timestamp) is already present on the UndoAction model. No schema changes or build_runner run are required.

1. Add a nested route in lib/ui/router.dart

Convert the existing undo-log flat GoRoute into one with a routes list. Add a child route ':actionId' that receives the UndoAction via state.extra:

/accounts/undo-log           → UndoLogScreen (unchanged)
/accounts/undo-log/:actionId → UndoLogDetailScreen(action: state.extra as UndoAction)

2. Make list tiles tappable in lib/ui/screens/undo_log_screen.dart

In _UndoActionTile.build, add an onTap to the ListTile that navigates to the detail route:

onTap: () => context.go('/accounts/undo-log/${action.id}', extra: action),

No other changes to the list screen.

3. Create lib/ui/screens/undo_log_detail_screen.dart

A ConsumerWidget that accepts a single UndoAction action parameter. Layout:

  • AppBar: title "Undo Log Detail"; trailing TextButton('Undo') with the same undo logic already in _UndoActionTile (calls undoServiceProvider.notifier.undo(actionId: action.id), then pops and shows a snackbar).

  • Body as a ListView with these sections (use ListTile / Padding widgets — no new dependencies):

    1. AccountListTile(leading: Icon(Icons.account_circle), title: Text(action.accountId)).
      If an accountsProvider is available that maps id → display name, use it; otherwise render the raw accountId.

    2. Action type & timestampListTile with the same icon+colour logic already in _UndoActionTile, title = action.type.name.toUpperCase(), subtitle = full DateFormat('yyyy-MM-dd HH:mm:ss').format(action.timestamp.toLocal()).

    3. Folders — always show source folder. For UndoType.move, show a second tile (or a two-row ListTile) for the destination folder. Use Icons.folder_open / Icons.drive_file_move to distinguish source vs. target visually. Example:

      • Source: "from: INBOX"
      • Destination (move only): "to: Archive"
    4. Emails — a section header ("Emails in this transaction"), then one ListTile per entry in action.originalEmails:

      • title = email.subject ?? '(No Subject)'
      • subtitle = sender name/email (same fallback as the existing tile)
      • Use action.emailIds.length as the source of truth for count if originalEmails is shorter (hard-deleted case where snapshots may be absent).

4. No other files need changing

  • No Drift schema change → no build_runner run.
  • No new Riverpod provider needed.
  • The UndoAction is passed as GoRouter extra; it is always in memory because undoServiceProvider holds the full history.

File summary

File Change
lib/ui/router.dart Nest a ':actionId' child route under undo-log, passing extra to the new screen
lib/ui/screens/undo_log_screen.dart Add onTap to _UndoActionTile to navigate to detail route
lib/ui/screens/undo_log_detail_screen.dart New file — detail screen as described above
Now I have everything I need. Here's the plan: --- ## Implementation Plan: Undo Log Detail View ### Overview All data needed for the detail view (`accountId`, `type`, `sourceMailboxPath`, `destinationMailboxPath`, `originalEmails`, `timestamp`) is already present on the `UndoAction` model. No schema changes or `build_runner` run are required. ### 1. Add a nested route in `lib/ui/router.dart` Convert the existing `undo-log` flat `GoRoute` into one with a `routes` list. Add a child route `':actionId'` that receives the `UndoAction` via `state.extra`: ``` /accounts/undo-log → UndoLogScreen (unchanged) /accounts/undo-log/:actionId → UndoLogDetailScreen(action: state.extra as UndoAction) ``` ### 2. Make list tiles tappable in `lib/ui/screens/undo_log_screen.dart` In `_UndoActionTile.build`, add an `onTap` to the `ListTile` that navigates to the detail route: ```dart onTap: () => context.go('/accounts/undo-log/${action.id}', extra: action), ``` No other changes to the list screen. ### 3. Create `lib/ui/screens/undo_log_detail_screen.dart` A `ConsumerWidget` that accepts a single `UndoAction action` parameter. Layout: - **AppBar**: title "Undo Log Detail"; trailing `TextButton('Undo')` with the same undo logic already in `_UndoActionTile` (calls `undoServiceProvider.notifier.undo(actionId: action.id)`, then pops and shows a snackbar). - **Body** as a `ListView` with these sections (use `ListTile` / `Padding` widgets — no new dependencies): 1. **Account** — `ListTile(leading: Icon(Icons.account_circle), title: Text(action.accountId))`. If an `accountsProvider` is available that maps id → display name, use it; otherwise render the raw `accountId`. 2. **Action type & timestamp** — `ListTile` with the same icon+colour logic already in `_UndoActionTile`, title = `action.type.name.toUpperCase()`, subtitle = full `DateFormat('yyyy-MM-dd HH:mm:ss').format(action.timestamp.toLocal())`. 3. **Folders** — always show source folder. For `UndoType.move`, show a second tile (or a two-row `ListTile`) for the destination folder. Use `Icons.folder_open` / `Icons.drive_file_move` to distinguish source vs. target visually. Example: - Source: `"from: INBOX"` - Destination (move only): `"to: Archive"` 4. **Emails** — a section header ("Emails in this transaction"), then one `ListTile` per entry in `action.originalEmails`: - title = `email.subject ?? '(No Subject)'` - subtitle = sender name/email (same fallback as the existing tile) - Use `action.emailIds.length` as the source of truth for count if `originalEmails` is shorter (hard-deleted case where snapshots may be absent). ### 4. No other files need changing - No Drift schema change → no `build_runner` run. - No new Riverpod provider needed. - The `UndoAction` is passed as `GoRouter` `extra`; it is always in memory because `undoServiceProvider` holds the full history. ### File summary | File | Change | |---|---| | `lib/ui/router.dart` | Nest a `':actionId'` child route under `undo-log`, passing `extra` to the new screen | | `lib/ui/screens/undo_log_screen.dart` | Add `onTap` to `_UndoActionTile` to navigate to detail route | | `lib/ui/screens/undo_log_detail_screen.dart` | **New file** — detail screen as described above |
guettlibot commented 2026-06-05 20:35:02 +00:00 (Migrated from codeberg.org)

agentloop: agent exceeded its 30m0s runtime budget and was stopped.

agent stderr tail:

[acpx] session agentloop-sharedinbox-issue-450 (768a4efe-32ab-4a96-afec-0d9ccbc07c59) · /home/si/agentloop/loop-data/sharedinbox/issues/450 · agent needs reconnect
agentloop: agent exceeded its 30m0s runtime budget and was stopped. agent stderr tail: ``` [acpx] session agentloop-sharedinbox-issue-450 (768a4efe-32ab-4a96-afec-0d9ccbc07c59) · /home/si/agentloop/loop-data/sharedinbox/issues/450 · agent needs reconnect ```
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: guettli/sharedinbox#450