diff --git a/src/components/overlays/details/ConfirmOverlay.tsx b/src/components/overlays/details/ConfirmOverlay.tsx
new file mode 100644
index 00000000..9d73d675
--- /dev/null
+++ b/src/components/overlays/details/ConfirmOverlay.tsx
@@ -0,0 +1,57 @@
+import { Button } from "@/components/buttons/Button";
+import {
+ OverlayDisplay,
+ OverlayPortal,
+} from "@/components/overlays/OverlayDisplay";
+
+interface ConfirmOverlayProps {
+ isOpen: boolean;
+ message: string;
+ onConfirm: (event: React.MouseEvent) => void;
+ onCancel: () => void;
+ confirmButtonTheme?: "white" | "purple" | "secondary" | "danger" | "glass";
+ cancelButtonTheme?: "white" | "purple" | "secondary" | "danger" | "glass";
+ backdropOpacity?: number;
+ backdropColor?: string;
+}
+
+export function ConfirmOverlay({
+ isOpen,
+ message,
+ onConfirm,
+ onCancel,
+ confirmButtonTheme = "purple",
+ cancelButtonTheme = "secondary",
+ backdropOpacity = 0.5,
+ backdropColor = "black",
+}: ConfirmOverlayProps) {
+ return (
+
+
+
+
+
{message}
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/overlays/details/EpisodeCarousel.tsx b/src/components/overlays/details/EpisodeCarousel.tsx
index b671d019..7be52074 100644
--- a/src/components/overlays/details/EpisodeCarousel.tsx
+++ b/src/components/overlays/details/EpisodeCarousel.tsx
@@ -6,6 +6,7 @@ import { Link } from "react-router-dom";
import { Button } from "@/components/buttons/Button";
import { Dropdown } from "@/components/form/Dropdown";
import { Icon, Icons } from "@/components/Icon";
+import { ConfirmOverlay } from "@/components/overlays/details/ConfirmOverlay";
import { hasAired } from "@/components/player/utils/aired";
import { useProgressStore } from "@/stores/progress";
@@ -25,6 +26,8 @@ export function EpisodeCarousel({
const [showEpisodeMenu, setShowEpisodeMenu] = useState(false);
const [customSeason, setCustomSeason] = useState("");
const [customEpisode, setCustomEpisode] = useState("");
+ const [SeasonWatched, setSeasonWatched] = useState(false);
+ const [isConfirmOpen, setIsConfirmOpen] = useState(false);
const [expandedEpisodes, setExpandedEpisodes] = useState<{
[key: number]: boolean;
}>({});
@@ -203,10 +206,66 @@ export function EpisodeCarousel({
}
};
+ // Toggle whole season watch status
+ const toggleSeasonWatchStatus = (event: React.MouseEvent) => {
+ event.preventDefault();
+ event.stopPropagation();
+
+ setIsConfirmOpen(true);
+ };
+
+ const handleCancel = () => {
+ setIsConfirmOpen(false);
+ };
+
const currentSeasonEpisodes = episodes.filter(
(ep) => ep.season_number === selectedSeason,
);
+ const handleConfirm = (event: React.MouseEvent) => {
+ try {
+ const episodeWatchedStatus: boolean[] = [];
+ currentSeasonEpisodes.forEach((episode: any) => {
+ const episodeProgress =
+ progress[mediaId?.toString() ?? ""]?.episodes?.[episode.id];
+ const percentage = episodeProgress
+ ? (episodeProgress.progress.watched /
+ episodeProgress.progress.duration) *
+ 100
+ : 0;
+ const isAired = hasAired(episode.air_date);
+ const isWatched = percentage > 90;
+ if (isAired && !isWatched) {
+ episodeWatchedStatus.push(isWatched);
+ }
+ });
+
+ const hasUnwatched = episodeWatchedStatus.length >= 1;
+
+ currentSeasonEpisodes.forEach((episode: any) => {
+ const episodeProgress =
+ progress[mediaId?.toString() ?? ""]?.episodes?.[episode.id];
+ const percentage = episodeProgress
+ ? (episodeProgress.progress.watched /
+ episodeProgress.progress.duration) *
+ 100
+ : 0;
+ const isAired = hasAired(episode.air_date);
+ const isWatched = percentage > 90;
+ if (hasUnwatched && isAired && !isWatched) {
+ toggleWatchStatus(episode.id, event); // Mark unwatched as watched
+ } else if (!hasUnwatched && isAired && isWatched) {
+ toggleWatchStatus(episode.id, event); // Mark watched as unwatched
+ }
+ });
+
+ setIsConfirmOpen(false);
+ } catch (error) {
+ console.error("Error in handleConfirm:", error);
+ setIsConfirmOpen(false);
+ }
+ };
+
const toggleEpisodeExpansion = (
episodeId: number,
event: React.MouseEvent,
@@ -259,6 +318,34 @@ export function EpisodeCarousel({
};
}, [episodes, expandedEpisodes]);
+ useEffect(() => {
+ const episodeWatchedStatus: boolean[] = [];
+
+ currentSeasonEpisodes.forEach((episode: any) => {
+ const episodeProgress =
+ progress[mediaId?.toString() ?? ""]?.episodes?.[episode.id];
+ const percentage = episodeProgress
+ ? (episodeProgress.progress.watched /
+ episodeProgress.progress.duration) *
+ 100
+ : 0;
+ const isAired = hasAired(episode.air_date);
+ const isWatched = percentage > 90;
+
+ if (isAired && !isWatched) {
+ episodeWatchedStatus.push(isWatched);
+ }
+ });
+
+ let toggle: boolean;
+
+ if (episodeWatchedStatus.length >= 1) {
+ setSeasonWatched(true); // If no episodes are watched, we want to mark all as watched
+ } else {
+ setSeasonWatched(false); // if all episodes are watched, we want to mark all as unwatched
+ }
+ }, [currentSeasonEpisodes, episodes, mediaId, progress]);
+
return (
{/* Season Selector */}
@@ -323,17 +410,43 @@ export function EpisodeCarousel({
)}
- ({
- id: season.season_number.toString(),
- name: `${t("details.season")} ${season.season_number}`,
- }))}
- selectedItem={{
- id: selectedSeason.toString(),
- name: `${t("details.season")} ${selectedSeason}`,
- }}
- setSelectedItem={(item) => onSeasonChange(Number(item.id))}
- />
+
+ {isConfirmOpen && (
+
+ )}
+
+
+ ({
+ id: season.season_number.toString(),
+ name: `${t("details.season")} ${season.season_number}`,
+ }))}
+ selectedItem={{
+ id: selectedSeason.toString(),
+ name: `${t("details.season")} ${selectedSeason}`,
+ }}
+ setSelectedItem={(item) => onSeasonChange(Number(item.id))}
+ />
+
{/* Episodes Carousel */}
@@ -359,7 +472,6 @@ export function EpisodeCarousel({
>
{/* Add padding before the first card */}
-
{currentSeasonEpisodes.map((episode) => {
const isActive =
showProgress?.episode?.id === episode.id.toString();