pstream is dead; long live pstream taciturnaxolotl.github.io/pstream-ng/
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

add mark as watched to movie details modal

Pas 605abb9a 6242d787

+78 -15
+71 -14
src/components/overlays/detailsModal/components/layout/DetailsContent.tsx
··· 7 7 import { Icon, Icons } from "@/components/Icon"; 8 8 import { useLanguageStore } from "@/stores/language"; 9 9 import { usePreferencesStore } from "@/stores/preferences"; 10 - import { useProgressStore } from "@/stores/progress"; 10 + import { getProgressPercentage, useProgressStore } from "@/stores/progress"; 11 11 import { shouldShowProgress } from "@/stores/progress/utils"; 12 12 import { scrapeIMDb } from "@/utils/imdbScraper"; 13 13 import { getTmdbLanguageCode } from "@/utils/language"; ··· 38 38 const [logoHeight, setLogoHeight] = useState<number>(0); 39 39 const logoRef = useRef<HTMLDivElement>(null); 40 40 const progress = useProgressStore((s) => s.items); 41 + const updateItem = useProgressStore((s) => s.updateItem); 41 42 const enableImageLogos = usePreferencesStore( 42 43 (state) => state.enableImageLogos, 43 44 ); 45 + 46 + // Check if movie is watched (>90% progress) 47 + const isMovieWatched = useMemo(() => { 48 + if (data.type !== "movie" || !data.id) return false; 49 + const movieProgress = progress[data.id.toString()]?.progress; 50 + if (!movieProgress) return false; 51 + const percentage = getProgressPercentage( 52 + movieProgress.watched, 53 + movieProgress.duration, 54 + ); 55 + return percentage > 90; 56 + }, [data.type, data.id, progress]); 44 57 45 58 const showProgress = useMemo(() => { 46 59 if (!data.id) return null; ··· 189 202 } 190 203 }; 191 204 205 + const toggleMovieWatchStatus = () => { 206 + if (data.type !== "movie" || !data.id) return; 207 + 208 + // Get the poster URL from the data 209 + const posterUrl = data.posterUrl; 210 + 211 + // Update progress - if watched, set to 0%, otherwise set to 100% 212 + updateItem({ 213 + meta: { 214 + tmdbId: data.id.toString(), 215 + title: data.title || "", 216 + type: "movie", 217 + releaseYear: data.releaseDate 218 + ? new Date(data.releaseDate).getFullYear() 219 + : new Date().getFullYear(), 220 + poster: posterUrl, 221 + }, 222 + progress: { 223 + watched: isMovieWatched ? 0 : 60, // 60 seconds for "watched" 224 + duration: 60, 225 + }, 226 + }); 227 + }; 228 + 192 229 return ( 193 230 <div className="relative h-full flex flex-col"> 194 231 {/* Share notification popup */} ··· 290 327 291 328 {/* Genres */} 292 329 {data.genres && data.genres.length > 0 && ( 293 - <div className="flex flex-wrap gap-2 items-center"> 294 - {data.genres.map((genre, index) => ( 295 - <span 296 - key={genre.id} 297 - className="text-[11px] px-2 py-0.5 rounded-full bg-white/20 text-white/80 transition-all duration-300 hover:scale-110 animate-[scaleIn_0.6s_ease-out_forwards]" 298 - style={{ 299 - animationDelay: `${((data.genres?.length ?? 0) - 1 - index) * 60}ms`, 300 - transform: "scale(0)", 301 - opacity: 0, 302 - }} 330 + <div className="flex justify-between items-center"> 331 + <div className="flex flex-wrap gap-2 items-center"> 332 + {data.genres.map((genre, index) => ( 333 + <span 334 + key={genre.id} 335 + className="text-[11px] px-2 py-0.5 rounded-full bg-white/20 text-white/80 transition-all duration-300 hover:scale-110 animate-[scaleIn_0.6s_ease-out_forwards]" 336 + style={{ 337 + animationDelay: `${((data.genres?.length ?? 0) - 1 - index) * 60}ms`, 338 + transform: "scale(0)", 339 + opacity: 0, 340 + }} 341 + > 342 + {genre.name} 343 + </span> 344 + ))} 345 + </div> 346 + {/* Movie Watch Toggle Button - Only show for movies and not in minimal modal */} 347 + {data.type === "movie" && !minimal && ( 348 + <button 349 + type="button" 350 + onClick={toggleMovieWatchStatus} 351 + className="p-1.5 bg-dropdown-background hover:bg-dropdown-hoverBackground transition-colors rounded-full ml-2" 352 + title={ 353 + isMovieWatched 354 + ? t("player.menus.episodes.markAsUnwatched") 355 + : t("player.menus.episodes.markAsWatched") 356 + } 303 357 > 304 - {genre.name} 305 - </span> 306 - ))} 358 + <Icon 359 + icon={isMovieWatched ? Icons.EYE_SLASH : Icons.EYE} 360 + className="h-5 w-5 text-white" 361 + /> 362 + </button> 363 + )} 307 364 </div> 308 365 )} 309 366
+7 -1
src/components/overlays/detailsModal/components/layout/DetailsModal.tsx
··· 23 23 import { OverlayPortal } from "../../../OverlayDisplay"; 24 24 import { DetailsModalProps } from "../../types"; 25 25 26 - export function DetailsModal({ id, data: _data, minimal }: DetailsModalProps) { 26 + export function DetailsModal({ 27 + id, 28 + data: _data, 29 + minimal: _minimal, 30 + }: DetailsModalProps) { 31 + // Player details modal should always be minimal (hide episode carousel and movie watch button) 32 + const minimal = _minimal || id === "player-details"; 27 33 const { hideModal, isModalVisible, modalStack, getModalData } = 28 34 useOverlayStack(); 29 35 const [detailsData, setDetailsData] = useState<any>(null);