Files
siftlode/frontend/src/lib/usePlexCollectionTitles.ts
T
peter e5b155ada9 refactor(ui): reuse the store, media-query and deleted-tag rules the review found rebuilt, and name a loading pick
E32 review round 1 (F3–F6).

- Channels' open-row store and the chat dock use lib/store's createStore instead of a hand-rolled
  listener set (the chat dock gets a test of its own).
- The Plex backdrop reads prefers-reduced-motion through useMediaQuery; the query string is one
  constant (REDUCED_MOTION) for the hook and the carousel's one-off read.
- A link-restored collection filter showed its raw rating_key in the picker while the titles
  loaded: usePlexCollectionTitles now says it is pending, the picker draws a placeholder
  (labelsPending, as the channel picker does) and so does the chip row.
- The "deleted tag" rule was written twice with two i18n keys and had drifted: the channel manager
  showed NO chip for a pick while the tag list loaded, or ever after a failed load, while the pick
  still narrowed the table. One rule now (lib/tagPicks), one key (common.deletedTag); the manager's
  row shows every pick, as a placeholder while loading and as its id after a failure.
2026-10-04 02:29:03 +02:00

34 lines
1.9 KiB
TypeScript

import { useQuery } from "@tanstack/react-query";
import { api } from "./api";
import { qk } from "./queryKeys";
// A Plex link carries the collection filter as bare rating_keys (`?pf=collections%3D17045`) — nothing
// in the URL says what "17045" is called. The title is normally picked up as a side effect of choosing
// the collection in the sidebar (`collectionTitles`), so a link opened cold had no name for it and the
// picker token rendered the raw id while the filter chip could only say "Collection".
//
// This resolves the missing ones from the server and merges them over what the filter state already
// knows. It stays OUT of `collectionTitles`: that map is hashed into the Plex query keys, so writing a
// resolved title back in would split the cache from the logically identical state and refetch the grid
// for a label. Titles are display-only, so they belong in a display-only read. `pending` says the
// missing ones are still on their way, so a token or chip can show a placeholder rather than the raw id
// (Combobox `labelsPending`, ActiveFilter `pending` — the feed's channel picker does the same).
export function usePlexCollectionTitles(
ids: string[],
known: Record<string, string> | undefined,
): { titles: Record<string, string>; pending: boolean } {
// Sorted so the same set of ids in a different order is the same query.
const missing = ids.filter((id) => !known?.[id]).sort();
const q = useQuery({
queryKey: qk.plexCollections("titles", missing.join(",")),
queryFn: () => api.plexCollections(undefined, undefined, missing),
enabled: missing.length > 0,
// A collection's name is about as static as data gets, and this can be asked for on every render
// of the sidebar and the chip row.
staleTime: 60 * 60_000,
});
const titles: Record<string, string> = { ...known };
for (const c of q.data?.collections ?? []) titles[c.id] = c.title;
return { titles, pending: missing.length > 0 && q.isPending };
}