fix(nav): a page settling keeps a reload's overlay entry; an old entry's sub-view goes to its own (review round 4)
- useUrlMirror: a change before the user has done anything since the load is the page settling (the rail stepping off a playlist deleted since, Files off a folder gone), not another view: the reload mark stays, and the player that reopens takes its entry over — one Back closes it, as before. - useHistorySubview: an entry an older build wrote is a module's only when its _sub is the view the address names under that module's key (the card's push kept the season's `v` beside its `m`). - Playlists: the rail's default selection and the page's "the rail moved there" read one helper. - moduleUrl contract: writer literals in a choice's branches are scanned too; the sub-view census walks type arguments of any depth and refuses an aliased import. - A stale comment on the nav press over a live search.
This commit is contained in:
@@ -13,6 +13,12 @@ import { urlWithParams } from "./urlState";
|
||||
// or a drawer's, a sub-view's) is not in it, and a reload would bring the old view back over the one
|
||||
// on screen. (One landed on by an overlay's step back that another step will leave is written too,
|
||||
// and harmlessly: the next landing writes the entry that stays.)
|
||||
// Whether a click or a key press has reached the page since it loaded. A browser without the API
|
||||
// counts as yes: every change is then another view, as before it existed.
|
||||
const userHasActed = (): boolean =>
|
||||
(navigator as { userActivation?: { hasBeenActive: boolean } }).userActivation?.hasBeenActive ??
|
||||
true;
|
||||
|
||||
export function useUrlMirror(updates: Record<string, string | null>): void {
|
||||
// The caller typically passes an inline object literal (new identity every render); key on its
|
||||
// serialized value so the effect only fires when a param actually changes, not every render.
|
||||
@@ -23,11 +29,13 @@ export function useUrlMirror(updates: Record<string, string | null>): void {
|
||||
// A change of what this mirror writes, after its first write, is another view on the same entry
|
||||
// (replaceView: it drops the marks a reload under an overlay left). Its first write on a mount and
|
||||
// its write on a landing name the view the entry already shows, so the marks stay: an overlay that
|
||||
// reopens after a reload still finds its entry.
|
||||
// reopens after a reload still finds its entry. So does a change before the user has done anything
|
||||
// since the load: that is the page settling (the rail stepping off a playlist deleted since), and
|
||||
// the address under the entry names the same screen, settled again on the way back.
|
||||
const written = useRef<string | null>(null);
|
||||
const mirror = useEffectEvent(() => {
|
||||
const href = urlWithParams(updates);
|
||||
const changed = written.current != null && written.current !== dep;
|
||||
const changed = written.current != null && written.current !== dep && userHasActed();
|
||||
written.current = dep;
|
||||
// Every mirror runs on every landing: one with nothing to change writes nothing (a browser
|
||||
// throttles a page that rewrites its address too often).
|
||||
|
||||
Reference in New Issue
Block a user