diff --git a/web/NavPanel.tsx b/web/NavPanel.tsx index bb6e97f..4841208 100644 --- a/web/NavPanel.tsx +++ b/web/NavPanel.tsx @@ -1,9 +1,11 @@ +import { useEffect, useRef } from 'react'; import { Link } from 'react-router'; +import { useClient } from 'hooks/useClient'; import { Lock } from 'lucide-react'; -import { LAYOUT_BANDS, SECTIONS, sectionOpen } from './sections'; +import { SECTIONS, sectionOpen } from './sections'; import { href, useNav } from './routes'; import { useReady } from './useReady'; -import { pageParam, usePages } from './usePages'; +import { pageParam, usePages, type Page } from './usePages'; /** * The left column: which part of the fofos site the detail panel is showing. @@ -50,12 +52,34 @@ const rowClass = (active: boolean) => }`; export const NavPanel = () => { - // Announced by PagesPanel rather than fetched here — see usePages. Null until it has spoken, which - // is why nothing is drawn instead of an empty list. - const { pages } = usePages(); + const { pages, announce } = usePages(); const { section: active, item } = useNav(); const { ready, rootSet } = useReady(); + /* + The pages, fetched HERE when nobody has said yet. + + They were announced by PagesPanel and only by PagesPanel — which mounts when Pages is the open + section, so the site's pages were invisible in the nav until you had already been to the page that + lists them. The one place they most need to be is the one place they were not. + + The channel stays and is still the reason the nav renames a page the moment the editor does: two + fetchers would agree until somebody typed. This only fills a silence. + */ + const client = useClient(); + const clientRef = useRef(client); + clientRef.current = client; + + useEffect(() => { + if (pages !== null || !ready) return; + void clientRef.current + .get<{ data: Page[] }>('/fofos-admin/pages') + .then((body) => announce(body.data)) + // Left null: the nav then draws no children, which is what it did before and is not a failure + // worth a message in a list of links. + .catch(() => {}); + }, [pages, ready, announce]); + const renderRow = (section: (typeof SECTIONS)[number]) => { const Icon = section.icon; // Each section asks its own question now — Media wants a root location, the site's pages want a @@ -118,42 +142,6 @@ export const NavPanel = () => { Drawn whether or not Pages is the open section. The list of pages IS the site, and hiding it until you click makes the nav a menu of menus. */ - /* - Layout's three bands, as rows under Layout. - - The layout IS a page — a header, the slot a page goes in, and a footer — and those three are the - only things on this screen. They were reachable by scrolling and by knowing which tab held what; - as rows they are the same shape as the pages under Pages, which is the shape the nav already - teaches. - - Fixed rather than fetched: unlike pages, these three are structural. A band cannot be added or - removed from the admin, only the code that draws it can, so a list read from the database would - be a query that can only ever return the same three rows. - */ - if (section.key === 'layout') { - return ( -