A refactor that separates “which board does this note live on?” from “wait for it to fold, then pop the detail” — because a cross-app deep link only wants the first half.
The epic behind this chain is a one-line bug report: clicking an inline headway card reference from another app needs two back presses, the first one landing on the headway board root. The cause is that one cross-app open pushes two global-nav entries — a bare app-switch entry, then a second one that Headway's own board → card diff emits a frame later. The fix (commit 5) is for the chrome to ask the target app for a route token and push one tagged entry.
This commit is the plumbing that makes that possible. Nothing user-visible changes.
process_pending_open did four things in one straight line, and a deep link wants
exactly the first two of them. Here is where the seam is:
Step 2 is not an optimisation — it is a correctness step. A card's a
tag records the board it was born on; a cross-board move changes only its placement.
locate_card_in_boards finds where it actually is, and the deep link needs that answer
just as much as the retry loop does.
OpenTarget grew a titleA nav history entry carries a label. The in-app path gets one for free: by the time the board
grid pushes a card entry, it is drawing that board, so card_title(&view, card)
has a folded BoardView to read. A deep link has no such luck — it mints its route
token during the very frame that switches boards, and a board you just switched to has not folded
yet. Two different sources for the same string:
| path | title from | available when |
|---|---|---|
in-appreconcile_nav → PushCard |
card_title(&view, card) |
after the fold — the view is already on screen |
cross-appopen_note_route |
OpenTarget::title, off the event |
immediately — IssueEvent.subject needs no fold |
Reading the subject straight off the note makes it a snapshot that can lag a later
rename. That is not a regression: every HeadwayRoute title already works this way,
for the same reason — nav_title is handed only the token, with no
Ndb handle to re-resolve through. A back/forward label wants the name the entry had
when you opened it anyway.
Nothing reads title until commit 4, so it ships behind an
#[allow(dead_code)] with a comment naming its reader. The alternative was to defer
the field into the commit that consumes it, but it belongs with resolve_open_target
— the resolve half is what this commit is about, and commit 4's spec already reaches
for target.title.
Collapsing the straight line into a call changes the timing slightly. Both changes are in the same direction:
returned on
the frame it switched boards, before ever looking at the fold. The check now runs immediately
after the switch, so a board that happens to fold synchronously opens its card one frame sooner
instead of waiting for the next repaint. Strictly better, and the slow path is unchanged.if self.active != board { …
return } doubled as the gate in front of the fold check. Once
activate_open_target has just set self.active, re-testing it is
tautological, so it is dropped rather than reproduced.The self.active != target.board test inside activate_open_target
still matters, and not just for tidiness: process_pending_open re-runs every frame
until the fold lands, and an unguarded switch would republish the encrypted board preference on
each one of those frames.
Headway::open, pending_open and process_pending_open
are all still here. Commit 4 keeps the pending request alongside the route token:
the retry loop corrects active for a card whose placement hasn't folded in, and it
is the only open path in a chrome-less embedding (the nav_token: None branch the
snapshot harness drives). It costs no extra history entry, because
render_nav seeds before = Card(id) from the token and the retry's
open_card sets that same card — so reconcile_nav sees
before == after and pushes nothing.
./scripts/ci-local passed whole: changelog-check, lint, linux-test, android,
snapshot-test. The epic's drift number came out at 419 passing lib tests
(351 notedeck + 12 notedeck_chrome + 56 notedeck_headway), unchanged from the
802f471a860f baseline. notedeck_headway's integration suite — the flaky one
— ran clean at 10 passed, including all six existing nav tests
(chrome_nav_loop_card_open_then_back_returns_to_board,
a_card_that_has_not_folded_in_keeps_its_route_entry, and friends).
cargo clippy reports nothing new; the two needless_borrow warnings in
notedeck/src/note/context.rs and the private-intra-doc-link warnings in
notedeck_headway/src/ui.rs all predate this commit.
No new test was added, and none of the above exercises the new seam as a seam —
the existing coverage drives process_pending_open, which is the half that did not
change shape. The refactor is only load-bearing once open_note_route calls
activate_open_target, and that is commit 4, whose headline test
(chrome_nav_loop_cross_app_open_pushes_one_entry) is where the split is actually
proven. The one-frame-earlier fold path in particular is reasoned about, not observed: no test
distinguishes a synchronous fold from a deferred one.