feat(e24): reach the Home surface — the landing toggle, a palette command, Shift+W
S4's reachability half. Home already led the nav order; what was missing was every way IN other than the rail. - Settings → Appearance → Startup: "Start on Home", the documented escape hatch for the landing override. Deliberately still localStorage and not a server pref — `loadInitialPage` answers before /api/me lands, and a landing module decided after the first paint is a visible jump. Its two stored values are now ONE exported constant, so the writer and the boot-time reader cannot drift on a magic string. - The command palette gets "Meglepetés" / "Surprise me": it navigates to Home and rerolls the hero on arrival. No second "Mit nézzek?" row beside it — the Home module row already matches that phrase through its descriptive title. - A global Shift+W jumps to Home. A chord, not a bare `w`: the Plex player binds bare letters and `w` sits on `s`, so a blind reach for the subtitle key would have thrown a viewer out of the film. The palette's request crosses a lazy module boundary, so it is a token that waits (lib/homeIntent) rather than an event fired into an empty room. Browser-measured: taken with a compare-and-clear, because StrictMode's double-invoked mount effect otherwise rerolled twice and landed on the page two picks deep. HU/EN parity kept; UAT on localdev in both languages, including the empty shelf.
This commit is contained in:
@@ -30,8 +30,16 @@ afterEach(() => {
|
||||
});
|
||||
|
||||
/** Renders the live stores so an assertion reads what a real consumer would render. */
|
||||
function Host({ enabled = true, onState }: { enabled?: boolean; onState: (s: string) => void }) {
|
||||
useGlobalKeymap(enabled);
|
||||
function Host({
|
||||
enabled = true,
|
||||
onState,
|
||||
onHome = () => {},
|
||||
}: {
|
||||
enabled?: boolean;
|
||||
onState: (s: string) => void;
|
||||
onHome?: () => void;
|
||||
}) {
|
||||
useGlobalKeymap(enabled, onHome);
|
||||
const help = useKeyboardHelpOpen();
|
||||
const palette = usePaletteOpen();
|
||||
useEffect(() => {
|
||||
@@ -111,6 +119,45 @@ describe("useGlobalKeymap", () => {
|
||||
expect(state).toBe("none");
|
||||
});
|
||||
|
||||
// E24. The chord is the point: the Plex player binds bare letters and `w` sits on top of `s`, so a
|
||||
// bare `w` would yank a viewer out of the film on a blind reach for the subtitle key.
|
||||
it("jumps Home on Shift+W and ignores a bare w", () => {
|
||||
const onHome = vi.fn();
|
||||
render(<Host onState={() => {}} onHome={onHome} />);
|
||||
key({ key: "w" });
|
||||
expect(onHome).not.toHaveBeenCalled();
|
||||
key({ key: "w", shiftKey: true });
|
||||
expect(onHome).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("leaves Shift+W to the text field it was typed into", () => {
|
||||
const onHome = vi.fn();
|
||||
render(<Host onState={() => {}} onHome={onHome} />);
|
||||
const field = document.createElement("input");
|
||||
document.body.appendChild(field);
|
||||
field.focus();
|
||||
act(() => {
|
||||
field.dispatchEvent(
|
||||
new KeyboardEvent("keydown", { key: "w", shiftKey: true, bubbles: true, cancelable: true }),
|
||||
);
|
||||
});
|
||||
expect(onHome).not.toHaveBeenCalled();
|
||||
field.remove();
|
||||
});
|
||||
|
||||
// The handler is App's `() => setPage("home")` — a fresh closure every render. It is held in a ref
|
||||
// rather than in the effect's deps, so the listener is bound once; this pins that the ref is the
|
||||
// CURRENT one and not the closure captured at mount.
|
||||
it("calls the latest handler after a re-render, without rebinding", () => {
|
||||
const first = vi.fn();
|
||||
const second = vi.fn();
|
||||
const { rerender } = render(<Host onState={() => {}} onHome={first} />);
|
||||
rerender(<Host onState={() => {}} onHome={second} />);
|
||||
key({ key: "w", shiftKey: true });
|
||||
expect(first).not.toHaveBeenCalled();
|
||||
expect(second).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
// Bound above the branch that mounts the overlays, the keys were swallowed on the loading/Welcome
|
||||
// screens and the flipped store then sprang the overlay open by itself once the shell mounted.
|
||||
it("binds nothing, and clears a stale store, while disabled", () => {
|
||||
|
||||
Reference in New Issue
Block a user