import { useCallback, useRef } from 'react'; import { defineChannel } from 'hooks/usePanelChannel'; /** A page, as the site's admin API describes it. */ export type Page = { id: number; slug: string; /** `/` for Home, `/about` for the rest — built by the server so nothing here reasons about slugs. */ path: string; title: string; menu_label: string | null; position: number; is_home: boolean; }; /** * The page list, shared between the two panels. * * The nav draws the pages as rows under Pages, and the editor owns them — and they are SIBLINGS in a * workspace, so neither can pass the other a prop. Both fetching would give two lists that agree until * somebody renames a page, at which point the nav keeps the old name until something reloads it. * * So the editor announces what it has and the nav listens. A channel is the right tool for this and * the wrong one for SELECTION: which page is open lives in the URL, per docs/navigation-audit.md. * This carries data, not state. * * Null means nobody has said yet — the nav then shows no children rather than an empty list, which * would read as "this site has no pages". */ const usePagesList = defineChannel('fofos-admin:pages', null); export const usePages = () => { const [pages, setPages] = usePagesList(); // The setter is a fresh closure every render and `announce` goes into dependency lists, so it is // read through a ref — stable identity without freezing the first render's setter. Same reasoning // as channel.ts. const setRef = useRef(setPages); setRef.current = setPages; const announce = useCallback((rows: Page[] | null) => setRef.current(rows), []); return { pages, announce }; };