SeasonsListEpisode: Show spoilers when status is set to FINISHED and allow changing status while spoilers are hidden

Based on PR by goestav: https://github.com/sbondCo/Watcharr/pull/1055

Different in these ways:
- Using $derived here for `ws` variable
- Using z-index to allow status button to be clicked while spoilers are hidden
- Don't hide spoilers again (if they are shown) when episode is deleted

Also refactored `handleStatusClick()` a bit by removing the redundant `ws` lookup (it can now just use `we` set at the top of the component).

Co-Authored-By: Goestav <27970303+goestav@users.noreply.github.com>
This commit is contained in:
IRHM
2026-07-18 12:40:13 +01:00
committed by momi
parent 9b64932af1
commit f5bfa29877
2 changed files with 61 additions and 16 deletions
+3
View File
@@ -6,6 +6,9 @@ These changes are awaiting release:
- Profile: Stats no longer care about the `Include Previously Watched` setting. All previously watched items will be counted in the stats now. - Profile: Stats no longer care about the `Include Previously Watched` setting. All previously watched items will be counted in the stats now.
- Activity: Added `index` to `WatchedID` column to speed up queries. - Activity: Added `index` to `WatchedID` column to speed up queries.
- SeasonsListEpisode when `Hide Spoilers` is on:
- Show episode spoilers if its status is `FINISHED`;
- Allow changing status without showing spoilers (useful for when setting an episode to PLANNED, etc).
## Fixed ## Fixed
+58 -16
View File
@@ -10,6 +10,7 @@
import { notify } from "../util/notify"; import { notify } from "../util/notify";
import { store } from "@/store.svelte"; import { store } from "@/store.svelte";
import { removeWatchedEpisode, updateWatchedEpisode } from "./api"; import { removeWatchedEpisode, updateWatchedEpisode } from "./api";
import { onMount } from "svelte";
interface Props { interface Props {
ep: TMDBSeasonDetailsEpisode; ep: TMDBSeasonDetailsEpisode;
@@ -18,32 +19,65 @@
let { ep, watchedItem }: Props = $props(); let { ep, watchedItem }: Props = $props();
const we = $derived(
watchedItem?.watchedEpisodes?.find(
(s) =>
s.seasonNumber === ep.season_number &&
s.episodeNumber === ep.episode_number,
),
);
let isHidden: boolean = $state(!!store?.userSettings?.hideSpoilers); let isHidden: boolean = $state(!!store?.userSettings?.hideSpoilers);
function handleStatusClick(type: WatchedStatus | "DELETE") { /**
* Re-sets `isHidden` state.
*/
function reSetIsHidden() {
// If the episode status is "FINISHED", ensure `isHidden` is set to
// `false` (so finished episodes aren't blurred when hideSpoilers is on).
if (we?.status == "FINISHED") {
isHidden = false;
}
}
onMount(() => {
reSetIsHidden();
});
async function handleStatusClick(type: WatchedStatus | "DELETE") {
if (!watchedItem) { if (!watchedItem) {
console.error("SeasonListEpisode: handleStatusClick: No watched item."); console.error("SeasonListEpisode: handleStatusClick: No watched item.");
return; return;
} }
if (type === "DELETE") { if (type === "DELETE") {
const ws = watchedItem.watchedEpisodes?.find( if (!we || !we.id) {
(s) =>
s.seasonNumber === ep.season_number &&
s.episodeNumber === ep.episode_number,
);
if (!ws) {
notify({ notify({
text: "Failed to find watched episode id. Please try refreshing.", text: "Failed to find watched episode id. Please try refreshing.",
type: "error", type: "error",
}); });
console.error(
"handleStatusClick(DELETE): `we` doesn't exist or have an id",
we,
);
return; return;
} }
removeWatchedEpisode(watchedItem, ws.id); removeWatchedEpisode(watchedItem, we.id);
// NOTE: Similar to below where we `reSetIsHidden` to unhide spoilers
// automatically if status is set to FINISHED, we WONT do the opposite
// here and re-hide the spoilers (if unhidden) after removing an episode
// because that would probably be annoying to users (eg: click to
// show spoilers, then delete episode, spoilers re-hidden automatically).
return; return;
} }
updateWatchedEpisode(watchedItem, ep.season_number, ep.episode_number, { await updateWatchedEpisode(
status: type, watchedItem,
}); ep.season_number,
ep.episode_number,
{
status: type,
},
);
reSetIsHidden();
} }
function handleStarClick(rating: number) { function handleStarClick(rating: number) {
@@ -90,11 +124,6 @@
<span class="overview">{ep.overview}</span> <span class="overview">{ep.overview}</span>
</div> </div>
{#if watchedItem} {#if watchedItem}
{@const we = watchedItem.watchedEpisodes?.find(
(s) =>
s.seasonNumber === ep.season_number &&
s.episodeNumber === ep.episode_number,
)}
<div class="status-rating-ctr"> <div class="status-rating-ctr">
<div class="rating" style={"width: 45px"}> <div class="rating" style={"width: 45px"}>
<PosterRating <PosterRating
@@ -221,6 +250,19 @@
min-height: 40px; min-height: 40px;
height: 40px; height: 40px;
overflow: visible; overflow: visible;
/* z-index of 2 so the button is higher than .spoiler-text
which makes it clickable while whole ep is still hidden.
Which is useful if you don't want spoilers while setting
the episode to WATCHING, etc. */
z-index: 2;
&:hover {
/* On hover, the z-index is higher than all other status
buttons on the page to avoid the active one being put
below others (making it unuseable). */
z-index: 3;
}
} }
} }
} }