* fix(metadata): persist series show_status from provider metadata
Plugin-reported series status (proto status field 31) was mapped into
MetadataResult.ShowStatus but dropped by both metadataResultToItem and
itemToMetadataResult, so media_items.show_status stayed empty for every
movie and series - only the manga enrichment path ever wrote it. This
left the Show Status card overlay permanently blank for series.
- carry ShowStatus through both converters; series values normalize to
a canonical lowercase domain (returning/ended/cancelled/in_production/
upcoming) so TMDB "Returning Series"/"Canceled" and TVDB
"Continuing"/"Upcoming" converge on one spelling
- pass non-series values through verbatim so the manga status domain
("Ongoing", ...) can never be mangled by a generic refresh round-trip
- round-tripping the existing item's status also stops refreshes from
wiping a previously persisted value via show_status = EXCLUDED.show_status
- extend the web overlay formatter with continuing/upcoming/planned
TMDB/TVDB plugins need follow-up changes to actually emit the status
field; prepped separately in their repos.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(web): overlay badge layout, ordering, and wordmark rendering
Fixes from a full card-overlay audit (every issue verified by DOM
geometry measurement before/after):
- render each card edge as one flex row holding both corner stacks so
opposing badges share the width (min-w-0 + truncate) instead of
overlapping on narrow cards; long labels ellipsize instead of
wrapping over the opposite corner
- honor prefs.order via orderedOverlaysForPosition — the renderer
previously ignored the stored order entirely
- cap corners at 3 badges so maxed-out configs can't collide with the
opposite vertical corner
- lift bottom-right badges above the card menu button, which is always
visible on touch devices and occluded them
- suppress the text label when a wordmark icon (HDR10/ATMOS/AV1/HDR)
already spells it — pill/vibrant presets rendered "HDR10 HDR10" —
and widen the wordmark viewBoxes, which clipped their own text;
drop the never-used iconOnly flag the wordmark rule supersedes
- standalone resolution badge now uses prettyResolution ("4K", not
"2160P"), matching the combined badge
- manga cards skip generic overlays (their status/count chips own both
top corners) and the two chips now share a row and truncate instead
of overlapping each other
- useOverlayPrefs returns null while loading so cards no longer flash
default badges before the user's config or admin kill switch arrives
- settings rows for Resolution/HDR now say why they're hidden while
the combined badge is enabled
- add a CardOverlays test suite covering every registered overlay,
ordering, suppression, wordmarks, the corner cap, and menu clearance
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
60 lines
2.0 KiB
TypeScript
60 lines
2.0 KiB
TypeScript
import { useMemo, useCallback } from "react";
|
|
import { useQuery } from "@tanstack/react-query";
|
|
import { api } from "@/api/client";
|
|
import { useSetting, useSetSetting } from "@/hooks/queries/settings";
|
|
import { settingsKeys } from "@/hooks/queries/keys";
|
|
import { parseOverlayPrefs, serializeOverlayPrefs, type CardOverlayPrefs } from "@/lib/overlays";
|
|
|
|
const SETTING_KEY = "card_overlays";
|
|
|
|
interface OverlayConfig {
|
|
enabled: boolean;
|
|
defaults?: string;
|
|
}
|
|
|
|
function useOverlayConfig() {
|
|
return useQuery({
|
|
queryKey: [...settingsKeys.all, "overlay-config"] as const,
|
|
queryFn: () => api<OverlayConfig>("/settings/overlay-config"),
|
|
staleTime: 60_000,
|
|
});
|
|
}
|
|
|
|
export function useOverlayPrefs() {
|
|
const { data: raw, isLoading: userLoading } = useSetting(SETTING_KEY);
|
|
const { data: config, isLoading: configLoading } = useOverlayConfig();
|
|
const setSetting = useSetSetting();
|
|
|
|
const prefs = useMemo(() => {
|
|
// User setting takes priority; fall back to admin defaults
|
|
const source = raw ?? config?.defaults ?? null;
|
|
return parseOverlayPrefs(source);
|
|
}, [raw, config?.defaults]);
|
|
|
|
// Admin kill switch: if disabled server-wide, return null prefs
|
|
const enabled = config?.enabled !== false;
|
|
|
|
const setPrefs = useCallback(
|
|
(next: CardOverlayPrefs) => {
|
|
const serialized = serializeOverlayPrefs(next);
|
|
// Avoid a network round-trip and downstream re-render cascade when
|
|
// the user toggles a control to its current value.
|
|
if (raw === serialized) return;
|
|
setSetting.mutate({ key: SETTING_KEY, value: serialized });
|
|
},
|
|
[raw, setSetting],
|
|
);
|
|
|
|
// While either query is in flight, report null prefs instead of built-in
|
|
// defaults: rendering defaults first would flash badges that vanish (or
|
|
// change) the moment the user's own config or the admin kill switch loads.
|
|
const isLoading = userLoading || configLoading;
|
|
|
|
return {
|
|
prefs: enabled && !isLoading ? prefs : null,
|
|
setPrefs,
|
|
isLoading,
|
|
enabled,
|
|
};
|
|
}
|