show total watched to details modal

This commit is contained in:
Pas 2026-01-17 12:08:28 -07:00
parent e544334bea
commit 310a7839ef
4 changed files with 40 additions and 3 deletions

View file

@ -489,6 +489,7 @@
"seasons": "Season/s", "seasons": "Season/s",
"season": "Season", "season": "Season",
"episode": "Episode", "episode": "Episode",
"watched": "Watched {{watched}} of {{total}} ({{percentage}}%)",
"airs": "Airs", "airs": "Airs",
"endsAt": "Ends at {{time}}", "endsAt": "Ends at {{time}}",
"trailer": "Trailer", "trailer": "Trailer",

View file

@ -23,6 +23,7 @@ export function EpisodeCarousel({
mediaId, mediaId,
mediaTitle, mediaTitle,
mediaPosterUrl, mediaPosterUrl,
totalEpisodes,
}: EpisodeCarouselProps) { }: EpisodeCarouselProps) {
const [showEpisodeMenu, setShowEpisodeMenu] = useState(false); const [showEpisodeMenu, setShowEpisodeMenu] = useState(false);
const [customSeason, setCustomSeason] = useState(""); const [customSeason, setCustomSeason] = useState("");
@ -250,6 +251,30 @@ export function EpisodeCarousel({
[mediaId, getFavoriteEpisodes], [mediaId, getFavoriteEpisodes],
); );
// Calculate watched episodes count and percentage
const watchedStats = useMemo(() => {
if (!mediaId || !totalEpisodes) return { watched: 0, percentage: 0 };
let watchedCount = 0;
episodes.forEach((episode) => {
const episodeProgress =
progress[mediaId.toString()]?.episodes?.[episode.id];
const percentage = episodeProgress
? getProgressPercentage(
episodeProgress.progress.watched,
episodeProgress.progress.duration,
)
: 0;
if (percentage > 90) {
watchedCount += 1;
}
});
const percentage = Math.round((watchedCount / totalEpisodes) * 100);
return { watched: watchedCount, percentage };
}, [episodes, progress, mediaId, totalEpisodes]);
// Load favorite episodes when favorites is selected // Load favorite episodes when favorites is selected
useEffect(() => { useEffect(() => {
if (showFavorites && mediaId && favoriteEpisodeIds.length > 0) { if (showFavorites && mediaId && favoriteEpisodeIds.length > 0) {
@ -423,7 +448,7 @@ export function EpisodeCarousel({
{showEpisodeMenu && ( {showEpisodeMenu && (
<div <div
ref={episodeMenuRef} ref={episodeMenuRef}
className="absolute top-full left-0 mt-2 p-4 bg-background-main rounded-lg shadow-lg border border-white/10 z-50 min-w-[250px]" className="absolute top-full left-0 mt-2 p-4 bg-background-main rounded-xl shadow-lg z-50 min-w-[250px]"
> >
<div className="space-y-4"> <div className="space-y-4">
<div> <div>
@ -436,7 +461,7 @@ export function EpisodeCarousel({
onChange={(e) => setCustomSeason(e.target.value)} onChange={(e) => setCustomSeason(e.target.value)}
min="1" min="1"
max={seasons.length} max={seasons.length}
className="w-full px-3 py-2 bg-white/5 rounded border border-white/10 text-white focus:outline-none focus:border-white/30" className="w-full px-3 py-2 bg-white/5 rounded-xl text-white focus:outline-none focus:border-white/30"
placeholder={t("details.season")} placeholder={t("details.season")}
/> />
</div> </div>
@ -449,7 +474,7 @@ export function EpisodeCarousel({
value={customEpisode} value={customEpisode}
onChange={(e) => setCustomEpisode(e.target.value)} onChange={(e) => setCustomEpisode(e.target.value)}
min="1" min="1"
className="w-full px-3 py-2 bg-white/5 rounded border border-white/10 text-white focus:outline-none focus:border-white/30" className="w-full px-3 py-2 bg-white/5 rounded-xl text-white focus:outline-none focus:border-white/30"
placeholder={t("details.episode")} placeholder={t("details.episode")}
/> />
</div> </div>
@ -464,6 +489,15 @@ export function EpisodeCarousel({
</div> </div>
)} )}
</div> </div>
{totalEpisodes && (
<span className="text-xs md:text-sm text-white/70">
{t("details.watched", {
watched: watchedStats.watched,
total: totalEpisodes,
percentage: watchedStats.percentage,
})}
</span>
)}
</div> </div>
{/* Season Watched Confirmation */} {/* Season Watched Confirmation */}

View file

@ -411,6 +411,7 @@ export function DetailsContent({ data, minimal = false }: DetailsContentProps) {
mediaId={data.id} mediaId={data.id}
mediaTitle={data.title} mediaTitle={data.title}
mediaPosterUrl={data.posterUrl} mediaPosterUrl={data.posterUrl}
totalEpisodes={data.episodes}
/> />
)} )}

View file

@ -105,6 +105,7 @@ export interface EpisodeCarouselProps {
mediaId?: number; mediaId?: number;
mediaTitle?: string; mediaTitle?: string;
mediaPosterUrl?: string; mediaPosterUrl?: string;
totalEpisodes?: number;
} }
export interface DetailsBodyProps { export interface DetailsBodyProps {