fix(nav): the address follows the view through overlays, reloads and Back (review round 1)
- A URL mirror names the view again on every entry Back or Forward lands on, so a change made under a dialog, a drawer or a sub-view is not undone by the next reload. - A view pushed from the entry a reload under a dialog started from no longer inherits its overlay mark; a reload under two overlays closes onto the page in one step. - The linked player reopens over a live search, gives itself up when its list moves first, and plays alone over an emptied playlist; a capped Files list keeps the link it cannot resolve. - The Feed press over a channel ends the search; leaving a search ends its asked mark; Files and Playlists searches end with their page; the feed always names its view and debounces its search. - Search-box keys are declared per module and kept out of the client log below TRACE. - Writers outside lib/ go through codecs; the URL contract scans computed keys, overlay writers and two-line reads, and holds every page to a bookmark round trip (Config, Activity, Playlists and the Files sort and viewer added).
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
// the list searched (Channels, Files and Playlists kept it in memory only, Plex in this browser only).
|
||||
// The real navigation and the real stores, read through their hooks; only the network is absent.
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
import { cleanup, render, waitFor } from "@testing-library/react";
|
||||
import { act, cleanup, render, waitFor } from "@testing-library/react";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { useEffect } from "react";
|
||||
|
||||
@@ -14,6 +14,10 @@ vi.mock("react-i18next", async (importOriginal) => ({
|
||||
}));
|
||||
|
||||
import { NavigationProvider } from "./features/navigation/NavigationProvider";
|
||||
import {
|
||||
useNavigationActions,
|
||||
type NavigationActions,
|
||||
} from "./features/navigation/navigationContext";
|
||||
import { ModuleStores } from "./features/navigation/ModuleStores";
|
||||
import { useChannels } from "./modules/channels/channelsContext";
|
||||
import { useFiles } from "./modules/files/filesContext";
|
||||
@@ -22,13 +26,16 @@ import { usePlex } from "./modules/plex/plexContext";
|
||||
import { claimLocalData, getAccountRaw, LS, setAccountRaw } from "./lib/storage";
|
||||
|
||||
const seen: { current: Record<string, string> } = { current: {} };
|
||||
const nav: { current: NavigationActions | null } = { current: null };
|
||||
|
||||
function Probe() {
|
||||
const channels = useChannels();
|
||||
const files = useFiles();
|
||||
const playlists = usePlaylists();
|
||||
const plex = usePlex();
|
||||
const actions = useNavigationActions();
|
||||
useEffect(() => {
|
||||
nav.current = actions;
|
||||
seen.current = {
|
||||
channels: channels.search,
|
||||
files: files.search,
|
||||
@@ -93,3 +100,20 @@ describe("Plex's search", () => {
|
||||
expect(search().get("pqf")).toBe("people");
|
||||
});
|
||||
});
|
||||
|
||||
// The search is the page's while it is on screen: leaving the page ends it, so a trip back through
|
||||
// the rail opens the page unsearched (and the address names none).
|
||||
describe.each([
|
||||
["files", "fq", "notes"],
|
||||
["playlists", "plq", "jazz"],
|
||||
])("%s's name search, once the page is left", (page, key, value) => {
|
||||
it("is gone when the page is opened again", async () => {
|
||||
mountAt(`/?page=${page}&${key}=${value}`, page);
|
||||
await waitFor(() => expect(seen.current[page]).toBe(value));
|
||||
act(() => nav.current!.setPage("feed"));
|
||||
act(() => nav.current!.setPage(page as "files" | "playlists"));
|
||||
await waitFor(() => expect(seen.current[page]).toBe(""));
|
||||
await new Promise((r) => setTimeout(r, 350)); // past the address write's typing pause
|
||||
expect(search().get(key)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user