refactor(frontend): each module defines itself in its folder; rails load lazily and keep their own state
A page used to be listed three times (lib/modules for its label, icon and gate; shell/moduleRegistry for its page and rail; lib/pageMeta for its title), and App held every rail's layout and collapse flag. Now each module's `module.ts` says it all — label, icon, gate, its lazy page and rail, its width — handed out through its index.ts, and features/navigation/moduleRegistry lists them in nav order for the rail, the palette, the tab title, the reachable set and the shell's render. A contract test holds the registry to the page ids and each definition to its own folder, with no static import of a page. Every rail is lazy now, loaded together with its page so a pinned rail never arrives after it and shoves the content aside; the feed's and the playlists' rails took @dnd-kit off the boot path (boot 428.5 → 393.9 KiB gzip, the App chunk 122 → 32 KiB). A rail reads its own layout and collapse flag (lib/useRail) and writes a layout change to the `me` cache too, since it now mounts anew on each visit. App's session effects, its one-shot entry-URL handlers and the admin nudge move into shell hooks; App is layout. The registry's three layer suppressions are gone: none is left.
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
import { lazyWithPreload } from "../../lib/lazyWithPreload";
|
||||
import { useMe } from "../../lib/useMe";
|
||||
import { useFeedFilters, useFeedFiltersActions } from "./feedFiltersContext";
|
||||
import { useFeedView, useFeedViewActions } from "./feedViewContext";
|
||||
@@ -6,16 +5,12 @@ import { useNavigation, useNavigationActions } from "../../features/navigation/n
|
||||
import PersonalViewsModal from "../../features/filters/PersonalViewsModal";
|
||||
import { useSavedViews } from "../../features/filters/savedViewsContext";
|
||||
import FeedHeader from "./FeedHeader";
|
||||
|
||||
// The two module-registry pages that need per-render wiring (see moduleRegistry), split out of it so
|
||||
// that file exports only data and this one only components (Fast Refresh). Their lazy chunks are
|
||||
// exported alongside so the registry can attach each chunk's preload to the wrapper that renders it.
|
||||
export const Feed = lazyWithPreload(() => import("./Feed"));
|
||||
import Feed from "./Feed";
|
||||
|
||||
// The GLOBAL feed page: wires <Feed> to the app-wide feed filters / view / live-search, and fills the
|
||||
// header (the channel page's feed has no header). (The channel
|
||||
// page renders <Feed> itself with its own channel-scoped filters, so those two never share state.)
|
||||
export function FeedPage() {
|
||||
export default function FeedPage() {
|
||||
const me = useMe();
|
||||
const filters = useFeedFilters();
|
||||
const { setFilters } = useFeedFiltersActions();
|
||||
@@ -4,7 +4,7 @@ import { keepPreviousData, useQuery } from "@tanstack/react-query";
|
||||
import { qk } from "../../lib/queryKeys";
|
||||
import { SlidersHorizontal } from "lucide-react";
|
||||
import { api, NO_TAG_SENTINEL, type FeedFilters, type Tag, type TagCategory } from "../../lib/api";
|
||||
import { defaultLayout, type PanelLayout } from "../../lib/panelLayout";
|
||||
import { defaultLayout } from "../../lib/panelLayout";
|
||||
import { useDebounced } from "../../lib/useDebounced";
|
||||
import { useActiveFilters } from "../../lib/useActiveFilters";
|
||||
import TagManager from "../../features/channel/TagManager";
|
||||
@@ -23,6 +23,7 @@ import { useFeedFilters, useFeedFiltersActions } from "./feedFiltersContext";
|
||||
import { useChannelsActions } from "../channels";
|
||||
import { useTags } from "../../lib/useTags";
|
||||
import { compareText } from "../../lib/collate";
|
||||
import { useRail } from "../../lib/useRail";
|
||||
|
||||
// Filter ids; display labels resolved at render time via t("sidebar.sort.<id>") etc.
|
||||
const DATE_PRESETS: { days: number; key: string }[] = [
|
||||
@@ -37,17 +38,9 @@ const DATE_PRESETS: { days: number; key: string }[] = [
|
||||
{ days: 1825, key: "5years" },
|
||||
];
|
||||
|
||||
export default function Sidebar({
|
||||
layout,
|
||||
setLayout,
|
||||
collapsed,
|
||||
onToggleCollapse,
|
||||
}: {
|
||||
layout: PanelLayout;
|
||||
setLayout: (l: PanelLayout) => void;
|
||||
collapsed: boolean;
|
||||
onToggleCollapse: () => void;
|
||||
}) {
|
||||
export default function Sidebar() {
|
||||
// Its layout and collapse flag are its own (lib/useRail), so the shell only places it.
|
||||
const { layout, setLayout, collapsed, onToggleCollapse } = useRail("feed");
|
||||
const { t, i18n } = useTranslation();
|
||||
const lang = i18n.language;
|
||||
const { openModal } = useSavedViews();
|
||||
|
||||
@@ -4,3 +4,4 @@ export { FeedViewProvider } from "./FeedViewProvider";
|
||||
export { SavedViewsProvider } from "./SavedViewsProvider";
|
||||
export { useFeedFilters, useFeedFiltersActions } from "./feedFiltersContext";
|
||||
export { useFeedView, useFeedViewActions } from "./feedViewContext";
|
||||
export { ChannelPage, feedModule } from "./module";
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { YouTubeIcon } from "../../lib/brandIcons";
|
||||
import { lazyWithPreload, lazyWithPreloadAlongside } from "../../lib/lazyWithPreload";
|
||||
import type { ModuleDef } from "../../lib/moduleDef";
|
||||
|
||||
// The rail loads with the page (lazyWithPreloadAlongside), so it never arrives after it.
|
||||
const Rail = lazyWithPreload(() => import("./Sidebar"));
|
||||
|
||||
// This page as the shell and the navigation know it (lib/moduleDef). Its page and rail load lazily.
|
||||
export const feedModule: ModuleDef = {
|
||||
id: "feed",
|
||||
labelKey: "header.account.feed",
|
||||
railKey: "nav.rail.feed",
|
||||
icon: YouTubeIcon,
|
||||
Rail,
|
||||
Page: lazyWithPreloadAlongside(() => import("./FeedPage"), Rail),
|
||||
};
|
||||
|
||||
// The channel page: a feed scoped to one channel, opened over any page (App renders it in place of the
|
||||
// page). Lazy like a page, and preloaded with them.
|
||||
export const ChannelPage = lazyWithPreload(() => import("./ChannelPage"));
|
||||
Reference in New Issue
Block a user