fix(plex): facet + sort refinements

- Facets now respect the watch-state filter too (In progress / Watched / Unwatched),
  so e.g. the Year bounds match the visible set (was showing the library min, not the
  filtered min).
- Genre chips sorted alphabetically (was by count).
- Sort chips ordered alphabetically by their localized label.
- Ascending/Descending chips swapped (Ascending first) and the default sort direction
  is now Ascending.
- Default sort is now Title (ascending) instead of Recently added / Descending.
This commit is contained in:
npeter83 2026-07-11 00:45:05 +02:00
parent 017be5f8ca
commit fe024ab29d
4 changed files with 65 additions and 34 deletions

View file

@ -65,8 +65,8 @@ export default function PlexSidebar({
// Facet values (genres/ratings/bounds) for the current scope, ADAPTED to the active filters
// (faceted search) — so picking one filter narrows what the others offer. Refetches on any change.
const facetsQ = useQuery({
queryKey: ["plex-facets", scope, filters],
queryFn: () => api.plexFacets(scope, filters),
queryKey: ["plex-facets", scope, show, filters],
queryFn: () => api.plexFacets(scope, filters, show),
enabled: !!scope,
placeholderData: (prev) => prev, // keep the old chips visible during the refetch (no flicker)
});
@ -75,7 +75,7 @@ export default function PlexSidebar({
// (Collection filtering: the active-collection chip is set from an item info page's "Browse
// collection"; there's no per-library picker in the unified cross-library scope.)
const fCount = plexFilterCount(filters);
const activeCount = fCount + (show !== "all" ? 1 : 0) + (sort !== "added" ? 1 : 0);
const activeCount = fCount + (show !== "all" ? 1 : 0) + (sort !== "title" ? 1 : 0);
const anyActive = activeCount > 0;
const patch = (p: Partial<PlexFilters>) => setFilters({ ...filters, ...p });
@ -84,11 +84,14 @@ export default function PlexSidebar({
const clearAll = () => {
setFilters(EMPTY_PLEX_FILTERS);
setShow("all");
setSort("added");
setSort("title");
};
// Movie-only scope offers the duration sort; mixed/show scopes drop it (a show has no runtime).
const sorts = scope === "movie" ? MOVIE_SORTS : SHOW_SORTS;
// Ordered alphabetically by their localized label.
const sorts = [...(scope === "movie" ? MOVIE_SORTS : SHOW_SORTS)].sort((a, b) =>
t(`plex.sort.${a}`).localeCompare(t(`plex.sort.${b}`)),
);
const durBucketKey = DURATION_BUCKETS.find(
(b) => (filters.durationMin ?? null) === b.min && (filters.durationMax ?? null) === b.max,
)?.key;
@ -210,8 +213,8 @@ export default function PlexSidebar({
))}
</ChipRow>
<div className="mt-1.5 flex gap-1">
{(["desc", "asc"] as const).map((d) => (
<Chip key={d} active={(filters.sortDir ?? "desc") === d} onClick={() => patch({ sortDir: d })}>
{(["asc", "desc"] as const).map((d) => (
<Chip key={d} active={(filters.sortDir ?? "asc") === d} onClick={() => patch({ sortDir: d })}>
{t(`plex.filter.dir.${d}`)}
</Chip>
))}