fix(nav): the address keeps what the app rewrites around it (review round 3)

- The Feed press over a live search ends only the search: the feed's own view stays named.
- Leaving a search steps back only off an entry the app pushed (_ytOwn); one a link or a reload
  opened is cleared in place, also where the Navigation API is missing.
- useUrlMirror rewrites a changed view through replaceView, so a reload's overlay marks go; its
  first write and its landing writes keep them for an overlay that reopens.
- Files and Playlists: a box emptied and filled again settles at once, never naming the ended search.
- Playlists: a linked player is scoped to the playlist the store started with, and only the rail's
  own move to the first playlist is no pick.
- Contract: writer objects are scanned to their matching brace, a computed or shorthand key is
  refused, and every useHistorySubview call hands a named definition.
- Lint: playwright/no-skipped-test also refuses fixme (gate snapshot regenerated).
- FEED_DEFAULT_SORT at the three remaining sites; linkedNotListed says to search by name.
This commit is contained in:
2026-10-27 23:33:22 +01:00
parent 66e427dfa4
commit 02906d0acb
18 changed files with 282 additions and 53 deletions
+8 -5
View File
@@ -192,7 +192,7 @@ export default function Playlists() {
function PlaylistDetail() {
// The selected playlist lives in PlaylistsProvider, shared with the PlaylistsRail (which owns the
// list + its rail controls). This detail pane reacts to the selection.
const { selectedId } = usePlaylists();
const { selectedId, linkedId } = usePlaylists();
const { t, i18n } = useTranslation();
const lang = i18n.language;
const qc = useQueryClient();
@@ -586,11 +586,14 @@ function PlaylistDetail() {
// (filtered out, gone from the list, the list emptied or out of reach) plays alone, and nothing
// removes it from a list. In: the order seeded from it (an empty one has none to wait for), or its
// read refused. Scoped to the playlist it was linked over: another one picked first gives it up.
// One the account's list does not hold (deleted since, another account's link) is out of reach
// as soon as that list is in: the rail then moves to another one by itself, which is no pick.
const [linkedOver] = useState(selectedId);
// Linked over the playlist the store started with, not the selection this page mounted on: the rail
// may have moved that already. One the account's list does not hold (deleted since, another
// account's link) is out of reach as soon as that list is in: the rail then moves to the first one
// by itself, which is no pick — any other is.
const linkedOver = linkedId;
const linkedOutOfReach =
listQuery.data != null && !listQuery.data.some((p) => p.id === linkedOver);
const railMovedTo = listQuery.data?.[0]?.id ?? null;
const linkedListIn =
linkedOutOfReach ||
(detail != null && (detail.items.length === 0 || items.length > 0)) ||
@@ -605,7 +608,7 @@ function PlaylistDetail() {
setPlaying({ video, queue: [video], index: 0, listId, editable: false });
}
},
linkedOutOfReach ? linkedOver : selectedId,
linkedOutOfReach && selectedId === railMovedTo ? linkedOver : selectedId,
);
async function moveItemTo(video: Video, target: Playlist) {