refactor(playlists): scope module state in PlaylistsProvider

Lift the Playlists module's shared local state (rail name filter + selected
playlist) out of App into a split-context PlaylistsProvider, mirroring
FeedFiltersProvider. Header, PlaylistsRail, and the Playlists detail pane read
it via usePlaylists()/usePlaylistsActions() instead of props drilled through
App; the modules take only a uniform `me` prop now. App loses two useStates and
the selected-playlist persistence helper. (Platform S6b.2)
This commit is contained in:
2026-07-18 23:43:19 +02:00
parent f4a68575c5
commit d0fc061795
6 changed files with 95 additions and 48 deletions
+9 -9
View File
@@ -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 Playlist } from "../lib/api";
import { api, type Me, type Playlist } from "../lib/api";
import { formatDuration } from "../lib/format";
import { notify } from "../lib/notifications";
import { notifyYouTubeActionError } from "../lib/youtubeErrors";
@@ -10,6 +10,7 @@ import { LS, readAccountMerged, writeAccount } from "../lib/storage";
import { defaultLayout, type PanelLayout } from "../lib/panelLayout";
import SidePanel from "./SidePanel";
import PanelGroups, { type PanelItem } from "./PanelGroups";
import { usePlaylists, usePlaylistsActions } from "./PlaylistsProvider";
type PlSort = { key: "custom" | "name" | "count" | "duration"; dir: "asc" | "desc"; dirtyFirst: boolean };
const PL_SORT_DEFAULT: PlSort = { key: "custom", dir: "asc", dirtyFirst: false };
@@ -19,24 +20,23 @@ 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({
canWrite,
search,
selectedId,
setSelectedId,
me,
layout,
setLayout,
collapsed,
onToggleCollapse,
}: {
canWrite: boolean;
search: string;
selectedId: number | null;
setSelectedId: (id: number | null) => void;
me: Me;
layout: PanelLayout;
setLayout: (l: PanelLayout) => void;
collapsed: boolean;
onToggleCollapse: () => void;
}) {
const canWrite = me.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();
const { setSelectedId } = usePlaylistsActions();
const { t } = useTranslation();
const qc = useQueryClient();
const [plSort, setPlSort] = useState<PlSort>(() => readAccountMerged(LS.plSort, PL_SORT_DEFAULT));