feat(nav): tabs, list views and name searches are named in the address
Settings, Configuration and Users name their tab (stab/cfgtab/utab); a link's tab wins and a tab pre-selected while its page is on screen now switches it. A playlist's sort, filters and grouping (plsort/plf/plgroup), the Files sort (fsort, always written) and the name searches of Channels, Files, Playlists and Plex (cq/fq/plq/pq+pqf) reopen from the address; the searches are written once the typing pauses.
This commit is contained in:
@@ -41,6 +41,13 @@ import { playlistName } from "../../lib/playlistName";
|
||||
import { reinsertAt } from "../../lib/reinsert";
|
||||
import { useUndoable } from "../../lib/useUndoable";
|
||||
import { cycleSort, type SortState } from "../../lib/columnSort";
|
||||
import { useUrlMirror } from "../../lib/useUrlMirror";
|
||||
import {
|
||||
isStoredSort,
|
||||
PLAYLIST_STORED_SORT,
|
||||
playlistViewFrom,
|
||||
playlistViewParamUpdates,
|
||||
} from "../../lib/playlistsUrlState";
|
||||
import { formatRuntime } from "../../lib/format";
|
||||
import { applyView, sameOrder } from "../../lib/playlistView";
|
||||
const PlayerModal = lazy(() => import("../../features/player/PlayerModal"));
|
||||
@@ -95,11 +102,8 @@ import PlaylistsHeader from "./PlaylistsHeader";
|
||||
// It composes `tableHeader`'s pieces instead of rendering a `DataTable`, because DataTable owns
|
||||
// its own filter/sort/page state and the stored order needs a single owner.
|
||||
|
||||
// The neutral view: "#" ascending IS the stored order, so the header can show a real arrow for it
|
||||
// instead of the list looking unsorted (or, worse, looking sorted by whatever column the stored
|
||||
// order happens to correlate with — the user read a channel-grouped playlist as sorted by Channel).
|
||||
const STORED_SORT: SortState = { key: "pos", dir: "asc" };
|
||||
const isStoredSort = (s: SortState) => s?.key === "pos" && s.dir === "asc";
|
||||
// The neutral view ("#" ascending, the stored order) lives beside the address-bar codec of the view.
|
||||
const STORED_SORT = PLAYLIST_STORED_SORT;
|
||||
|
||||
const idsKey = (items: Video[]) =>
|
||||
items
|
||||
@@ -232,10 +236,13 @@ function PlaylistDetail() {
|
||||
},
|
||||
});
|
||||
const items = order.value;
|
||||
// VIEW state — never written to the server on its own.
|
||||
const [viewSort, setViewSort] = useState<SortState>(STORED_SORT);
|
||||
const [filters, setFilters] = useState<FilterMap>({});
|
||||
const [groupBy, setGroupBy] = useState(false);
|
||||
// VIEW state — never written to the server on its own. The address names it, so a reload or a
|
||||
// bookmark opens the playlist sorted, filtered and grouped as it was left (the view it opens on is
|
||||
// the address's; a switch to another playlist starts over, below).
|
||||
const [opened] = useState(() => playlistViewFrom(new URLSearchParams(window.location.search)));
|
||||
const [viewSort, setViewSort] = useState<SortState>(opened.sort);
|
||||
const [filters, setFilters] = useState<FilterMap>(opened.filters);
|
||||
const [groupBy, setGroupBy] = useState(opened.groupBy);
|
||||
// Tracks the membership (id set) currently loaded into `order`, so a refetch that only
|
||||
// re-confirms our own reorder doesn't wipe the undo history — we reset history only when
|
||||
// the actual set of items changes (different playlist, or an add/remove).
|
||||
@@ -354,6 +361,9 @@ function PlaylistDetail() {
|
||||
setFilters({});
|
||||
setGroupBy(false);
|
||||
}
|
||||
useUrlMirror(
|
||||
playlistViewParamUpdates(selectedId != null ? { sort: viewSort, filters, groupBy } : null),
|
||||
);
|
||||
|
||||
const builtin = detail?.kind === "watch_later";
|
||||
// YouTube-mirrored playlists can be edited locally and synced back (the read-sync skips a
|
||||
|
||||
Reference in New Issue
Block a user