refactor(fe): query-key factory + migrate feed/channels/playlists (R7 S2a)
Introduce lib/queryKeys.ts as the single source of truth for React Query keys — the R7 driver was ~200 hand-written key literals across 51 files (`["feed"]` alone in 15+), where one typo silently orphans a key so an invalidate never matches. S2a migrates the three highest-traffic domains (feed/video, channels, playlists) plus the shared me/my-status/tags roots: ~90 literal sites in 18 files now call qk.*(). Pure substitution — each factory fn returns the byte-identical array the call sites used before, so TanStack's prefix matching is unchanged (qk.feed() still invalidates qk.feed(filters)). Co-invalidation clusters are deliberately NOT bundled into helpers (they differ per site), so no query's refetch scope changes. Nullable keys (playlist(selectedId), ytSearch(q)) accept null so a null segment is preserved (["playlist", null]) while a no-arg call is the bare prefix key — guard is `!== undefined`. Gate green: typecheck, eslint (0 err), prettier, 68 vitest. Smoke: app boots, feed/facets/tags/my-status all 200, no console errors. Factory extended to plex/messaging/admin/downloads/notifications in S2b.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { qk } from "../lib/queryKeys";
|
||||
import { BookmarkPlus, Check, GripVertical, Pencil, Share2, Star, Trash2, X } from "lucide-react";
|
||||
import {
|
||||
closestCenter,
|
||||
@@ -56,7 +57,7 @@ export default function SavedViewsWidget({
|
||||
const { t } = useTranslation();
|
||||
const qc = useQueryClient();
|
||||
const confirm = useConfirm();
|
||||
const viewsQuery = useQuery({ queryKey: ["saved-views"], queryFn: api.savedViews });
|
||||
const viewsQuery = useQuery({ queryKey: qk.savedViews(), queryFn: api.savedViews });
|
||||
const views = viewsQuery.data ?? [];
|
||||
const [naming, setNaming] = useState(false);
|
||||
const [draftName, setDraftName] = useState("");
|
||||
@@ -69,7 +70,7 @@ export default function SavedViewsWidget({
|
||||
if (viewsQuery.data) syncDefaultMirror(viewsQuery.data);
|
||||
}, [viewsQuery.data]);
|
||||
|
||||
const refresh = () => qc.invalidateQueries({ queryKey: ["saved-views"] });
|
||||
const refresh = () => qc.invalidateQueries({ queryKey: qk.savedViews() });
|
||||
const currentKey = keyOf(filters);
|
||||
|
||||
async function save() {
|
||||
@@ -124,7 +125,7 @@ export default function SavedViewsWidget({
|
||||
const next = arrayMove(ids, oldIndex, newIndex);
|
||||
// Optimistic reorder so the drop sticks visually before the round-trip.
|
||||
qc.setQueryData<SavedView[]>(
|
||||
["saved-views"],
|
||||
qk.savedViews(),
|
||||
next.map((id) => views.find((v) => v.id === id)!),
|
||||
);
|
||||
await api.reorderViews(next);
|
||||
|
||||
Reference in New Issue
Block a user