Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea860521b3 |
@@ -32,7 +32,7 @@ repos:
|
||||
- id: dart-check
|
||||
name: dart format (autofix) + check-fast (parallel)
|
||||
language: system
|
||||
entry: bash -c 'cd "$(git rev-parse --show-toplevel)" && scripts/precommit_dart_check.sh'
|
||||
entry: bash -c 'cd "$(git rev-parse --show-toplevel)" && nix develop --command dagger call --progress=plain -q -m ci --source=. check-fast'
|
||||
pass_filenames: false
|
||||
always_run: true
|
||||
- id: ci-no-direct-dagger
|
||||
|
||||
@@ -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.
|
||||
@@ -124,6 +124,9 @@
|
||||
# nix develop --command does not set IN_NIX_SHELL; set it so _preflight passes in CI
|
||||
export IN_NIX_SHELL=1
|
||||
|
||||
# Point Dagger client at the running engine socket
|
||||
export DAGGER_HOST=unix:///run/dagger/engine.sock
|
||||
|
||||
# Disable Flutter telemetry inside dev shell
|
||||
export FLUTTER_SUPPRESS_ANALYTICS=true
|
||||
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Pre-commit wrapper for the `dart-check` hook.
|
||||
#
|
||||
# `dagger call ... check-fast` needs a Dagger engine. On a dev machine or in
|
||||
# CI that engine is provisioned from a local container runtime (docker/podman)
|
||||
# or reached through _EXPERIMENTAL_DAGGER_RUNNER_HOST. In engine-less sandboxes
|
||||
# (e.g. the agentloop agent pods that commit on our behalf) none of those
|
||||
# exist, so dagger falls back to its default engine image reference and aborts
|
||||
# with:
|
||||
# start engine: driver for scheme "image" was not available
|
||||
# which blocked every commit the agent tried to make.
|
||||
#
|
||||
# Codeberg CI still runs check-fast on every push, so skipping here is safe:
|
||||
# warn loudly and let the commit through when no engine can be reached.
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(git rev-parse --show-toplevel)"
|
||||
|
||||
# True when dagger has some way to reach/provision an engine.
|
||||
engine_available() {
|
||||
# A shared engine reached over the wire wins outright.
|
||||
[ -n "${_EXPERIMENTAL_DAGGER_RUNNER_HOST:-}" ] && return 0
|
||||
# Otherwise dagger provisions the engine from a local container runtime.
|
||||
# `info` (not `version`) confirms the daemon is actually reachable; cap it
|
||||
# with a timeout so a stale docker context cannot hang the commit.
|
||||
if command -v docker >/dev/null 2>&1 && timeout 10 docker info >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
if command -v podman >/dev/null 2>&1 && timeout 10 podman info >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
if ! engine_available; then
|
||||
echo "WARNING: no Dagger engine available (no container runtime, and" \
|
||||
"_EXPERIMENTAL_DAGGER_RUNNER_HOST is unset); skipping dart-check." \
|
||||
"Codeberg CI still runs check-fast on push." >&2
|
||||
exit 0
|
||||
fi
|
||||
|
||||
exec nix develop --command dagger call --progress=plain -q -m ci --source=. check-fast
|
||||
Reference in New Issue
Block a user