"use client"; // The archives on this hub as every hub surface lists them — the shelf's cards, // the scope chips, the search (its result stripes, chip dots and channel groups) // and /ask: ONE order and ONE colour per archive, decided here. // // The official instances come in the homepage's order, each in the colour its // homepage card wears — its own accent (a named one per base), else its chart // colour (officialInstances, common/lib/hubSummary.ts). That order is // /hub-summary.json's, so they are listed only once that file has settled — // found, missing or unreadable — rather than in hub-sites.json's order first // and reordered a moment later. Both files are small, same-origin and // requested together, so the wait is the gap between two requests already in // flight. The archives a visitor added follow, in the order they were added, // each in its own accent or none: the hex its /site.json published (a named // accent's on-dark value, or the site's own), FITTED to each base like any // custom hex (lib/siteColor.ts fittedHex, release 11 O2b) — so a pale one reads // on light on its card, chip and result stripe. A value that is not // a hex is dropped, never passed into a style. // // `listed` says the list is the hub's WHOLE list: `/hub-sites.json` has been // answered (siteRegistry's `builtinsLoaded`) and the summary has settled. Until // then it may hold only the archives this browser added (they are read from // storage at once). A surface that must not act on half the hub — /ask — // waits for it; the front page does not. import { useMemo } from "react"; import { useRegistry, type RegisteredSite, } from "yt-dlp-transcript-common/components/siteRegistry"; import type { FederatedSite } from "yt-dlp-transcript-common/components/SearchDataContext"; import { officialInstances } from "yt-dlp-transcript-common/lib/hubSummary"; import { fittedHex } from "yt-dlp-transcript-common/lib/siteColor"; import { useHubScope } from "./useHubScope"; import { useHubSummary } from "./useHubSummary"; export function useHubSites() { const { sites, removeSite, builtinsLoaded } = useRegistry(); const { summary, settled } = useHubSummary(); const listed = builtinsLoaded && settled; return useMemo(() => { const official = settled ? officialInstances( sites.filter((s) => s.kind === "builtin"), summary, ) : []; const added = sites .filter((s) => s.kind === "external") .map((s) => ({ ...s, accent: fittedHex(s.accent) })); // Every archive, in the one order, each official one wearing its colour. const all: RegisteredSite[] = [ ...official.map((o) => ({ ...o.site, accent: o.accent })), ...added, ]; return { official, added, all, summary, removeSite, listed }; }, [sites, summary, settled, removeSite, listed]); } // What the hub's search surfaces hand MultiSiteDataProvider: every archive in // the one order, with its colour and this browser's scope (the chips on the // front page; an archive switched off there is not fetched), and whether that // is the whole list yet (`listed`, above). export function useFederatedSites(): { federated: FederatedSite[]; toggle: (origin: string) => void; listed: boolean; } { const { all, listed } = useHubSites(); const { isOn, toggle } = useHubScope(); const federated = useMemo( () => all.map((s) => ({ origin: s.origin, siteTitle: s.siteTitle, accent: s.accent, enabled: isOn(s.origin), })), [all, isOn], ); return { federated, toggle, listed }; }