import { type ReactNode } from "react"; import { useTranslation } from "react-i18next"; import { Youtube } from "lucide-react"; import type { Me } from "../lib/api"; import { moduleLabelKey, moduleOrder, stepModule } from "../lib/modules"; import ModuleName from "./ModuleName"; import SearchBar from "./SearchBar"; import SyncStatus from "./SyncStatus"; import { useNavigation, useNavigationActions } from "./NavigationProvider"; import { useFeedFilters, useFeedFiltersActions } from "./FeedFiltersProvider"; import { usePlaylists, usePlaylistsActions } from "./PlaylistsProvider"; import { usePlex, usePlexActions } from "./PlexProvider"; import { useChannels, useChannelsActions } from "./ChannelsProvider"; // The floating top header: a row of glass pills over the content — a ModuleName pill (label + // accent dot + back/forward arrows), the module's SearchBar where it has one, and the per-user // sync chip at the right end. export default function Header({ me, onYtSearch, }: { me: Me; // Trigger a live YouTube search for the current term (feed only; hidden for the demo account, // which can't spend the shared quota). onYtSearch: (q: string) => void; }) { const { t } = useTranslation(); const { page } = useNavigation(); const { setPage } = useNavigationActions(); const filters = useFeedFilters(); const { setFilters } = useFeedFiltersActions(); // Each module's search term lives in its own provider (shared with that module's rail/content): // playlists name filter, Plex search term, channels name filter. The channel sync chip's // "go to full history" jump is a ChannelsProvider action. const { search: playlistsSearch } = usePlaylists(); const { setSearch: setPlaylistsSearch } = usePlaylistsActions(); const { q: plexQ } = usePlex(); const { setQ: setPlexQ } = usePlexActions(); const { search: channelsSearch } = useChannels(); const { setSearch: setChannelsSearch, goToFullHistory: onGoToFullHistory } = useChannelsActions(); // The ◀/▶ arrows step cyclically through the user's available modules (derived from `me`, so // it stays correct as modules are added/gated). Labels of every active module feed ModuleName's // dynamic width so the pill is sized to the widest reachable title in the current language. const order = moduleOrder(me); const moduleLabels = order.map((p) => t(moduleLabelKey[p])); const multiModule = order.length > 1; // Drop input focus on Go/Enter for the live-filter modules (feed's Go escalates to YouTube). const blur = () => (document.activeElement as HTMLElement | null)?.blur?.(); // Per-page SearchBar wiring — null for modules without a search. let search: ReactNode = null; if (page === "feed") { const trimmed = filters.q.trim(); search = ( { // When a search first appears, rank the feed by relevance — set atomically with the // query (race-free vs. per-keystroke updates). Only overrides the default "newest" // sort; a custom sort the user chose is left alone. Cleared in Feed. const startSearch = !filters.q.trim() && !!q.trim() && filters.sort === "newest"; setFilters({ ...filters, q, ...(startSearch ? { sort: "relevance" } : {}) }); }} onGo={() => onYtSearch(trimmed)} goLabel={t("header.searchYoutube")} goTitle={t("header.searchYoutubeTip")} goIcon={} goDisabled={!trimmed || me.is_demo} /> ); } else if (page === "plex" && me.plex_enabled) { search = ( ); } else if (page === "channels") { search = ( ); } else if (page === "playlists") { search = ( ); } return ( // Absolutely positioned within the (relative) content column, so its left edge tracks the // actual left zone: it sits just inside the content, reclaiming the space when the nav rail is // slim and/or no filter sidebar is present — no hard-coded offset.
setPage(stepModule(me, page, -1))} onNext={() => setPage(stepModule(me, page, 1))} canPrev={multiModule} canNext={multiModule} /> {search} {/* Per-user sync status — it used to cost the nav rail a whole block. It sits with the other pills rather than pinned to the far right: on a wide window that just strands it. Only on the two modules it says anything about: the feed those subscriptions fill, and the manager where you curate them. It's noise on Plex (a different source entirely), playlists, etc. */} {(page === "feed" || page === "channels") && ( )}
); }