The 43 react-refresh/only-export-components warnings the gate let through meant an edit to a provider (or AnchoredPopover, Toaster, plexDetailUi, …) could not be hot-swapped: Fast Refresh reloads a module that also exports hooks or helpers. - Each provider's contexts, the types its hooks return and the hooks themselves live in a sibling context module (navigationContext.ts, channelsContext.ts, …); the provider file keeps the component. loadAccountFilters moved next to the feed filters' defaults (feedFiltersInit.ts), following navigationInit/playlistsInit. - Helpers that sat in component files have their own modules: welcomeMode, downloadTitle, plexPlayable, channelSync, ui/anchoredPopoverHooks, ui/pageToolbarSlot, ui/pagerContent, ui/toastLevels, ui/overlayOrder, ui/tableAlign, lib/plexDetail; the cells of the channel columns and the registry's page wrappers are components files of their own (channelCells, modulePages). - usePersistedTab, an alias of useAccountPersistedState, is gone; its callers use the original. npm run lint is now eslint --max-warnings 0, so a warning fails the gate like an error does.
55 lines
2.2 KiB
TypeScript
55 lines
2.2 KiB
TypeScript
import { lazyWithPreload } from "../lib/lazyWithPreload";
|
|
import { useMe } from "../lib/useMe";
|
|
import { useFeedFilters, useFeedFiltersActions } from "./feedFiltersContext";
|
|
import { useFeedView, useFeedViewActions } from "./feedViewContext";
|
|
import { useNavigation, useNavigationActions } from "./navigationContext";
|
|
import PersonalViewsModal from "./PersonalViewsModal";
|
|
import { useSavedViews } from "./savedViewsContext";
|
|
|
|
// 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"));
|
|
export const Messages = lazyWithPreload(() => import("./Messages"));
|
|
|
|
// The GLOBAL feed page: wires <Feed> to the app-wide feed filters / view / live-search. (The channel
|
|
// page renders <Feed> itself with its own channel-scoped filters, so those two never share state.)
|
|
export function FeedPage() {
|
|
const me = useMe();
|
|
const filters = useFeedFilters();
|
|
const { setFilters } = useFeedFiltersActions();
|
|
const view = useFeedView();
|
|
const { setView } = useFeedViewActions();
|
|
const { ytSearch } = useNavigation();
|
|
const { enterYtSearch, exitYtSearch } = useNavigationActions();
|
|
// The Personal Views modal lives at the feed-page level (always mounted while on the feed, unmounts
|
|
// on navigation) rather than inside the collapsible filter rail — otherwise opening it from the
|
|
// always-visible toolbar view chip while the rail is unpinned would render nothing and latch open.
|
|
const { modalOpen } = useSavedViews();
|
|
return (
|
|
<>
|
|
<Feed
|
|
filters={filters}
|
|
setFilters={setFilters}
|
|
view={view}
|
|
setView={setView}
|
|
canRead={me.can_read}
|
|
isDemo={me.is_demo}
|
|
ytSearch={ytSearch}
|
|
onYtSearch={enterYtSearch}
|
|
onExitYtSearch={exitYtSearch}
|
|
/>
|
|
{modalOpen && !me.is_demo && <PersonalViewsModal />}
|
|
</>
|
|
);
|
|
}
|
|
|
|
export function MessagesPage() {
|
|
const me = useMe();
|
|
return (
|
|
<div className="p-4 max-w-3xl w-full mx-auto">
|
|
<Messages meId={me.id} />
|
|
</div>
|
|
);
|
|
}
|