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.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import type { ComponentProps, ReactNode } from "react";
|
||||
import type { TFunction } from "i18next";
|
||||
import { NO_TAG_SENTINEL, type ManagedChannel, type Tag } from "../lib/api";
|
||||
import { tagPickLabel, type TagList } from "../lib/tagPicks";
|
||||
import { formatCountOrDash, formatTotalHours } from "../lib/format";
|
||||
import type { Column } from "./ui/DataTable";
|
||||
import { PriorityStepper } from "./ui/PriorityStepper";
|
||||
@@ -50,25 +51,28 @@ export function tagsColumnTest(c: ManagedChannel, values: string[]): boolean {
|
||||
return values.some((v) => c.tag_ids.includes(Number(v)));
|
||||
}
|
||||
|
||||
/** The manager's tag-filter chip row (the Tags column filter's values): any picked id whose tag no
|
||||
* longer exists, then each user tag, then the "No tag" sentinel. A stored pick can outlive its tag
|
||||
* (deleted here or on another device) and still narrows the table to nothing, so it stays a visible,
|
||||
* removable chip — FIRST, so the row's "+N" overflow never folds the reason away (E32 S2). */
|
||||
/** The manager's tag-filter chip row (the Tags column filter's values): any picked id that is not one
|
||||
* of the user's tags, then each user tag, then the "No tag" sentinel. A stored pick can outlive its
|
||||
* tag (deleted here or on another device) and still narrows the table to nothing, so it stays a
|
||||
* visible, removable chip — FIRST, so the row's "+N" overflow never folds the reason away (E32 S2) —
|
||||
* and so does every pick while the tag list loads or after it failed, named by lib/tagPicks' rule
|
||||
* (a placeholder, then the name, "Deleted tag" or the id), not hidden (E32 review). */
|
||||
export function tagFilterChips(o: {
|
||||
userTags: Tag[];
|
||||
allTags: Tag[] | undefined;
|
||||
tags: TagList;
|
||||
picked: string[];
|
||||
noTagOn: boolean;
|
||||
toggle: (tagId: number) => void;
|
||||
toggleNoTag: () => void;
|
||||
t: TFunction;
|
||||
}): FilterChip[] {
|
||||
const known = new Set((o.allTags ?? []).map((tg) => String(tg.id)));
|
||||
const gone = o.allTags ? o.picked.filter((id) => id !== NO_TAG_SENTINEL && !known.has(id)) : [];
|
||||
const mine = new Set(o.userTags.map((tg) => String(tg.id)));
|
||||
const others = o.picked.filter((id) => id !== NO_TAG_SENTINEL && !mine.has(id));
|
||||
return [
|
||||
...gone.map((id) => ({
|
||||
...others.map((id) => ({
|
||||
key: `t${id}`,
|
||||
label: o.t("channels.tags.deleted"),
|
||||
label: tagPickLabel(Number(id), o.tags, o.t),
|
||||
pending: o.tags.isPending,
|
||||
active: true,
|
||||
onToggle: () => o.toggle(Number(id)),
|
||||
})),
|
||||
|
||||
Reference in New Issue
Block a user