import React, { useEffect, useRef } from "react"; import { Button, ButtonGroup, Card, Form } from "react-bootstrap"; import { Link } from "react-router-dom"; import cx from "classnames"; import * as GQL from "src/core/generated-graphql"; import { useConfiguration } from "src/core/StashService"; import { Icon, TagLink, HoverPopover, SweatDrops, TruncatedText, } from "src/components/Shared"; import { TextUtils } from "src/utils"; import { PerformerPopoverButton } from "../Shared/PerformerPopoverButton"; interface IScenePreviewProps { isPortrait: boolean; image?: string; video?: string; soundActive: boolean; } export const ScenePreview: React.FC = ({ image, video, isPortrait, soundActive, }) => { const videoEl = useRef(null); useEffect(() => { const observer = new IntersectionObserver((entries) => { entries.forEach((entry) => { if (entry.intersectionRatio > 0) // Catch is necessary due to DOMException if user hovers before clicking on page videoEl.current?.play().catch(() => {}); else videoEl.current?.pause(); }); }); if (videoEl.current) observer.observe(videoEl.current); }); useEffect(() => { if (videoEl?.current?.volume) videoEl.current.volume = soundActive ? 0.05 : 0; }, [soundActive]); return (
); }; interface ISceneCardProps { scene: GQL.SlimSceneDataFragment; compact?: boolean; selecting?: boolean; selected?: boolean | undefined; zoomIndex?: number; onSelectedChanged?: (selected: boolean, shiftKey: boolean) => void; onSceneClicked?: () => void; } export const SceneCard: React.FC = ( props: ISceneCardProps ) => { const config = useConfiguration(); // studio image is missing if it uses the default const missingStudioImage = props.scene.studio?.image_path?.endsWith( "?default=true" ); const showStudioAsText = missingStudioImage || (config?.data?.configuration.interface.showStudioAsText ?? false); function maybeRenderRatingBanner() { if (!props.scene.rating) { return; } return (
RATING: {props.scene.rating}
); } function maybeRenderSceneSpecsOverlay() { return (
{props.scene.file.width && props.scene.file.height ? ( {" "} {TextUtils.resolution( props.scene.file.width, props.scene.file.height )} ) : ( "" )} {(props.scene.file.duration ?? 0) >= 1 ? TextUtils.secondsToTimestamp(props.scene.file.duration ?? 0) : ""}
); } function maybeRenderSceneStudioOverlay() { if (!props.scene.studio) return; return (
{showStudioAsText ? ( props.scene.studio.name ) : ( {props.scene.studio.name} )}
); } function maybeRenderTagPopoverButton() { if (props.scene.tags.length <= 0) return; const popoverContent = props.scene.tags.map((tag) => ( )); return ( ); } function maybeRenderPerformerPopoverButton() { if (props.scene.performers.length <= 0) return; return ; } function maybeRenderMoviePopoverButton() { if (props.scene.movies.length <= 0) return; const popoverContent = props.scene.movies.map((sceneMovie) => (
{sceneMovie.movie.name
)); return ( ); } function maybeRenderSceneMarkerPopoverButton() { if (props.scene.scene_markers.length <= 0) return; const popoverContent = props.scene.scene_markers.map((marker) => { const markerPopover = { ...marker, scene: { id: props.scene.id } }; return ; }); return ( ); } function maybeRenderOCounter() { if (props.scene.o_counter) { return (
); } } function maybeRenderGallery() { if (props.scene.galleries.length <= 0) return; const popoverContent = props.scene.galleries.map((gallery) => ( )); return ( ); } function maybeRenderOrganized() { if (props.scene.organized) { return (
); } } function maybeRenderPopoverButtonGroup() { if ( !props.compact && (props.scene.tags.length > 0 || props.scene.performers.length > 0 || props.scene.movies.length > 0 || props.scene.scene_markers.length > 0 || props.scene?.o_counter || props.scene.galleries.length > 0 || props.scene.organized) ) { return ( <>
{maybeRenderTagPopoverButton()} {maybeRenderPerformerPopoverButton()} {maybeRenderMoviePopoverButton()} {maybeRenderSceneMarkerPopoverButton()} {maybeRenderOCounter()} {maybeRenderGallery()} {maybeRenderOrganized()} ); } } function handleSceneClick( event: React.MouseEvent ) { const { shiftKey } = event; if (props.selecting && props.onSelectedChanged) { props.onSelectedChanged(!props.selected, shiftKey); event.preventDefault(); } else if (props.onSceneClicked) { props.onSceneClicked(); event.preventDefault(); } } function handleDrag(event: React.DragEvent) { if (props.selecting) { event.dataTransfer.setData("text/plain", ""); event.dataTransfer.setDragImage(new Image(), 0, 0); } } function handleDragOver(event: React.DragEvent) { const ev = event; const shiftKey = false; if (props.selecting && props.onSelectedChanged && !props.selected) { props.onSelectedChanged(true, shiftKey); } ev.dataTransfer.dropEffect = "move"; ev.preventDefault(); } function isPortrait() { const { file } = props.scene; const width = file.width ? file.width : 0; const height = file.height ? file.height : 0; return height > width; } function zoomIndex() { if (!props.compact && props.zoomIndex !== undefined) { return `zoom-${props.zoomIndex}`; } } let shiftKey = false; return ( props.onSelectedChanged?.(!props.selected, shiftKey)} onClick={(event: React.MouseEvent) => { // eslint-disable-next-line prefer-destructuring shiftKey = event.shiftKey; event.stopPropagation(); }} />
{maybeRenderRatingBanner()} {maybeRenderSceneSpecsOverlay()} {maybeRenderSceneStudioOverlay()}
{props.scene.date}

{maybeRenderPopoverButtonGroup()}
); };