feat(app): the module registry drives the render (S6b DoD)
Replace App's page-render ternary and the three side-rail page conditionals with a data-driven lookup: components/moduleRegistry maps each page to a prop-free content component (+ optional side rail + shell fadeTop), and App renders PAGE_CONTENT[page]/PAGE_RAIL[page] gated by moduleDef(page).gate (an unreachable gated page falls back to the feed, as the old ternary did). Content modules and the two rails are now prop-free: they read `me` via the new useMe() hook and their module-local state from the scoped providers; FeedPage/MessagesPage are the only wrappers (they wire the global feed scope / meId). App.tsx 796->431 lines, zero `page===X` chrome conditionals — closes the Platform epic DoD. (Platform S6b.3)
This commit is contained in:
@@ -2,7 +2,7 @@ import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { ArrowDown, ArrowUp, ListVideo, Pin, Plus, RefreshCw, Youtube } from "lucide-react";
|
||||
import { api, type Me, type Playlist } from "../lib/api";
|
||||
import { api, type Playlist } from "../lib/api";
|
||||
import { formatDuration } from "../lib/format";
|
||||
import { notify } from "../lib/notifications";
|
||||
import { notifyYouTubeActionError } from "../lib/youtubeErrors";
|
||||
@@ -11,6 +11,7 @@ import { defaultLayout, type PanelLayout } from "../lib/panelLayout";
|
||||
import SidePanel from "./SidePanel";
|
||||
import PanelGroups, { type PanelItem } from "./PanelGroups";
|
||||
import { usePlaylists, usePlaylistsActions } from "./PlaylistsProvider";
|
||||
import { useMe } from "../lib/useMe";
|
||||
|
||||
type PlSort = { key: "custom" | "name" | "count" | "duration"; dir: "asc" | "desc"; dirtyFirst: boolean };
|
||||
const PL_SORT_DEFAULT: PlSort = { key: "custom", dir: "asc", dirtyFirst: false };
|
||||
@@ -20,19 +21,17 @@ const PL_SORT_DEFAULT: PlSort = { key: "custom", dir: "asc", dirtyFirst: false }
|
||||
// controls (sort / new / sync); the selected playlist is App state so the module's detail pane
|
||||
// (Playlists.tsx) reacts to it. Grouped into two islands: "manage" and the playlist list.
|
||||
export default function PlaylistsRail({
|
||||
me,
|
||||
layout,
|
||||
setLayout,
|
||||
collapsed,
|
||||
onToggleCollapse,
|
||||
}: {
|
||||
me: Me;
|
||||
layout: PanelLayout;
|
||||
setLayout: (l: PanelLayout) => void;
|
||||
collapsed: boolean;
|
||||
onToggleCollapse: () => void;
|
||||
}) {
|
||||
const canWrite = me.can_write;
|
||||
const canWrite = useMe().can_write;
|
||||
// The name filter + the selected playlist live in PlaylistsProvider (shared with the header search
|
||||
// box and the Playlists detail pane); this rail owns the list query + its own controls.
|
||||
const { search, selectedId } = usePlaylists();
|
||||
|
||||
Reference in New Issue
Block a user