refactor(channels): scope module state in ChannelsProvider
Lift the Channels manager's shared local state (name filter, status chip,
active tab, column-reset token, focus-a-channel intent) out of App into a
split-context ChannelsProvider, nested inside NavigationProvider so its
focusChannel/goToFullHistory actions can drive navigation. The Channels page,
the header search box + sync chip, the feed filter sidebar, and the
notifications inbox all read it via useChannels()/useChannelsActions() instead
of props drilled through App. Channels also internalises onViewChannel /
onFilterByTag / onFocusChannel (nav + feed-filters straight from providers) and
takes only { me, onOpenWizard } now. App loses five useStates, the focusChannel
helper, the focus-clear effect, and a large block of per-page prop plumbing.
(Platform S6b.2)
This commit is contained in:
@@ -15,7 +15,7 @@ import {
|
||||
UserMinus,
|
||||
X,
|
||||
} from "lucide-react";
|
||||
import { api, type ManagedChannel, type Tag } from "../lib/api";
|
||||
import { api, type ManagedChannel, type Me, type Tag } from "../lib/api";
|
||||
import { notifyYouTubeActionError } from "../lib/youtubeErrors";
|
||||
import { accountKey, LS } from "../lib/storage";
|
||||
import { useDismiss } from "../lib/useDismiss";
|
||||
@@ -30,6 +30,9 @@ import ChannelLink from "./ChannelLink";
|
||||
import ChannelDiscovery from "./ChannelDiscovery";
|
||||
import TagManager from "./TagManager";
|
||||
import { useConfirm } from "./ConfirmProvider";
|
||||
import { useChannels, useChannelsActions } from "./ChannelsProvider";
|
||||
import { useNavigationActions } from "./NavigationProvider";
|
||||
import { useFeedFilters, useFeedFiltersActions } from "./FeedFiltersProvider";
|
||||
|
||||
export type ChannelsView = "subscribed" | "discovery" | "blocked";
|
||||
|
||||
@@ -42,49 +45,36 @@ const STATUS_FILTERS: { id: ChannelStatusFilter; labelKey: string }[] = [
|
||||
{ id: "hidden", labelKey: "channels.filters.hidden" },
|
||||
];
|
||||
|
||||
export default function Channels({
|
||||
canWrite,
|
||||
isAdmin,
|
||||
search,
|
||||
setSearch,
|
||||
onViewChannel,
|
||||
onFilterByTag,
|
||||
onFocusChannel,
|
||||
focusChannelName,
|
||||
focusChannelToken,
|
||||
statusFilter,
|
||||
setStatusFilter,
|
||||
view,
|
||||
setView,
|
||||
filtersResetToken,
|
||||
onOpenWizard,
|
||||
}: {
|
||||
canWrite: boolean;
|
||||
isAdmin: boolean;
|
||||
// The channel-name filter, lifted to App so the shared top SearchBar drives it.
|
||||
search: string;
|
||||
setSearch: (q: string) => void;
|
||||
onViewChannel: (id: string, name: string) => void;
|
||||
onFilterByTag: (tagId: number, name: string) => void;
|
||||
onFocusChannel: (name: string) => void;
|
||||
focusChannelName: string | null;
|
||||
// Bumped every time a focus-channel intent is (re-)issued, so re-clicking the same channel
|
||||
// re-seeds the search box even when the name is unchanged.
|
||||
focusChannelToken: number;
|
||||
statusFilter: ChannelStatusFilter;
|
||||
setStatusFilter: (f: ChannelStatusFilter) => void;
|
||||
// The active tab lives in App so navigation intents that target the subscriptions table
|
||||
// (the header's "without full history" link, focus-channel) can force it back here.
|
||||
view: ChannelsView;
|
||||
setView: (v: ChannelsView) => void;
|
||||
// Bumped by the header's "without full history" intent to clear a stale column filter.
|
||||
filtersResetToken: number;
|
||||
onOpenWizard: () => void;
|
||||
}) {
|
||||
export default function Channels({ me, onOpenWizard }: { me: Me; onOpenWizard: () => void }) {
|
||||
const canWrite = me.can_write;
|
||||
const isAdmin = me.role === "admin";
|
||||
// The manager's name filter, status chip, active tab, reset token, and focus-channel intent live
|
||||
// in ChannelsProvider (shared with the header search box + the cross-module "focus this channel"
|
||||
// callers); this page is their primary editor.
|
||||
const {
|
||||
search,
|
||||
statusFilter,
|
||||
view,
|
||||
filtersResetToken,
|
||||
focusName: focusChannelName,
|
||||
focusToken: focusChannelToken,
|
||||
} = useChannels();
|
||||
const { setSearch, setStatusFilter, setView, focusChannel: onFocusChannel } = useChannelsActions();
|
||||
const { openChannel: onViewChannel, setPage } = useNavigationActions();
|
||||
const feedFilters = useFeedFilters();
|
||||
const { setFilters: setFeedFilters } = useFeedFiltersActions();
|
||||
const { t } = useTranslation();
|
||||
const qc = useQueryClient();
|
||||
const confirm = useConfirm();
|
||||
|
||||
// Clicking a tag chip filters the feed by that tag and jumps there. Internalised (was an App
|
||||
// callback) now that Channels reads the feed filters + navigation straight from their providers.
|
||||
const onFilterByTag = (tagId: number, name: string) => {
|
||||
setFeedFilters({ ...feedFilters, tags: [tagId], channelIds: [] });
|
||||
setPage("feed");
|
||||
notify({ level: "info", message: t("channels.notify.filteredByTag", { name }) });
|
||||
};
|
||||
|
||||
const channelsQuery = useQuery({ queryKey: ["channels"], queryFn: api.channels });
|
||||
const tagsQuery = useQuery({ queryKey: ["tags"], queryFn: api.tags });
|
||||
const statusQuery = useQuery({ queryKey: ["my-status"], queryFn: api.myStatus });
|
||||
|
||||
Reference in New Issue
Block a user