fix(e4): address code-review findings — usePref flush, ?pl guard, dedup (E4 review)
Applies the /code-review high findings on the E4 branch:
- usePref: flush the pending debounced server write on unmount instead of
clearing it, so the last change isn't lost (and can't later be overwritten by
the stale server value via the adopt effect). [correctness]
- PlaylistsProvider.readSelectedId: accept ?pl only when it's a positive integer
— "?pl=" gave Number("")===0 (finite) and "?pl=1.5" a fractional id, both of
which selected a non-existent playlist. [correctness]
- usePref: extract mergePrefsCache() so useSavePref and usePref.set share the
one me-cache merge shape instead of duplicating it. [reuse]
- New lib/useUrlMirror: the shared replaceState(urlWithParams(...)) mirror
effect, so FeedFiltersProvider and PlaylistsProvider stop hand-rolling the
identical block (keyed on the serialized delta so it only fires on change).
[reuse]
- plexCards: swapBadgeReveal() helper for the repeated
reveal-idle/group-hover:opacity-0 swap-badge ternary. [simplification]
Gate green (tsc, knip, lint 0-err, 247 vitest). Self-E2E (localdev): feed filter
mirrors to ?show and clears on leaving the feed / re-mirrors on return via
useUrlMirror; ?pl= (empty) and ?pl=abc no longer select a bogus playlist (fall
back to the stored selection); Plex reveal-idle badges still display:none on
touch after the helper refactor; 0 console errors.
This commit is contained in:
@@ -16,6 +16,14 @@ const PLAYABLE_TINT: Record<string, string> = {
|
||||
transcode: "bg-danger",
|
||||
};
|
||||
|
||||
// The reveal class for a status pill (watched / in-progress) that shares a corner with a hover-reveal
|
||||
// control (the watched-toggle). When that control is present it takes over on touch, so the pill yields
|
||||
// via `.reveal-idle` (hidden on touch, faded on hover); when there's no such control the pill is the
|
||||
// sole indicator and just fades on hover. (The single-episode card, where the pill is itself the
|
||||
// full-size watched marker with no fade, keeps its own inline rule.)
|
||||
const swapBadgeReveal = (hasControl: boolean): string =>
|
||||
hasControl ? "reveal-idle" : "group-hover:opacity-0";
|
||||
|
||||
export function PlexPosterCard({
|
||||
card,
|
||||
onOpen,
|
||||
@@ -105,9 +113,9 @@ export function PlexPosterCard({
|
||||
where the toggle is always shown); a watched show keeps the badge as its sole marker. */}
|
||||
{card.status === "watched" && (
|
||||
<span
|
||||
className={`pointer-events-none absolute top-1.5 right-1.5 text-3xs bg-black/70 text-white px-1.5 py-0.5 rounded-sm ${
|
||||
isPlayable ? "reveal-idle" : "group-hover:opacity-0"
|
||||
}`}
|
||||
className={`pointer-events-none absolute top-1.5 right-1.5 text-3xs bg-black/70 text-white px-1.5 py-0.5 rounded-sm ${swapBadgeReveal(
|
||||
isPlayable,
|
||||
)}`}
|
||||
>
|
||||
{t("plex.watched")}
|
||||
</span>
|
||||
@@ -229,18 +237,18 @@ export function SeasonCard({
|
||||
)}
|
||||
{watched && (
|
||||
<span
|
||||
className={`pointer-events-none absolute top-1.5 right-1.5 text-3xs bg-black/70 text-white px-1.5 py-0.5 rounded-sm ${
|
||||
onToggleWatched ? "reveal-idle" : "group-hover:opacity-0"
|
||||
}`}
|
||||
className={`pointer-events-none absolute top-1.5 right-1.5 text-3xs bg-black/70 text-white px-1.5 py-0.5 rounded-sm ${swapBadgeReveal(
|
||||
!!onToggleWatched,
|
||||
)}`}
|
||||
>
|
||||
{t("plex.watched")}
|
||||
</span>
|
||||
)}
|
||||
{se.status === "in_progress" && (
|
||||
<span
|
||||
className={`pointer-events-none absolute top-1.5 right-1.5 text-3xs bg-black/75 text-accent px-1.5 py-0.5 rounded-sm ${
|
||||
onToggleWatched ? "reveal-idle" : "group-hover:opacity-0"
|
||||
}`}
|
||||
className={`pointer-events-none absolute top-1.5 right-1.5 text-3xs bg-black/75 text-accent px-1.5 py-0.5 rounded-sm ${swapBadgeReveal(
|
||||
!!onToggleWatched,
|
||||
)}`}
|
||||
>
|
||||
{t("plex.inProgress")}
|
||||
</span>
|
||||
|
||||
Reference in New Issue
Block a user