mirror of
https://github.com/stashapp/stash.git
synced 2025-12-18 04:44:37 +03:00
Scene card preview refactor (#787)
* Refactor scene card preview * Add delay to video preview
This commit is contained in:
@@ -1,13 +1,62 @@
|
||||
import React, { useState } from "react";
|
||||
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 { useVideoHover } from "src/hooks";
|
||||
import { Icon, TagLink, HoverPopover, SweatDrops } from "src/components/Shared";
|
||||
import { TextUtils } from "src/utils";
|
||||
|
||||
interface IScenePreviewProps {
|
||||
isPortrait: boolean;
|
||||
image?: string;
|
||||
video?: string;
|
||||
soundActive: boolean;
|
||||
}
|
||||
|
||||
const ScenePreview: React.FC<IScenePreviewProps> = ({
|
||||
image,
|
||||
video,
|
||||
isPortrait,
|
||||
soundActive,
|
||||
}) => {
|
||||
const videoEl = useRef<HTMLVideoElement>(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();
|
||||
});
|
||||
},
|
||||
{ root: document.documentElement }
|
||||
);
|
||||
|
||||
if (videoEl.current) observer.observe(videoEl.current);
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (videoEl?.current?.volume)
|
||||
videoEl.current.volume = soundActive ? 0.05 : 0;
|
||||
}, [soundActive]);
|
||||
|
||||
return (
|
||||
<div className={cx("scene-card-preview", { portrait: isPortrait })}>
|
||||
<img className="scene-card-preview-image" src={image} alt="" />
|
||||
<video
|
||||
className="scene-card-preview-video"
|
||||
loop
|
||||
preload="none"
|
||||
ref={videoEl}
|
||||
src={video}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface ISceneCardProps {
|
||||
scene: GQL.SlimSceneDataFragment;
|
||||
selecting?: boolean;
|
||||
@@ -19,11 +68,6 @@ interface ISceneCardProps {
|
||||
export const SceneCard: React.FC<ISceneCardProps> = (
|
||||
props: ISceneCardProps
|
||||
) => {
|
||||
const [previewPath, setPreviewPath] = useState<string>();
|
||||
const hoverHandler = useVideoHover({
|
||||
resetOnMouseLeave: false,
|
||||
});
|
||||
|
||||
const config = useConfiguration();
|
||||
const showStudioAsText =
|
||||
config?.data?.configuration.interface.showStudioAsText ?? false;
|
||||
@@ -220,18 +264,6 @@ export const SceneCard: React.FC<ISceneCardProps> = (
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseEnter() {
|
||||
if (!previewPath || previewPath === "") {
|
||||
setPreviewPath(props.scene.paths.preview || "");
|
||||
}
|
||||
hoverHandler.onMouseEnter();
|
||||
}
|
||||
|
||||
function onMouseLeave() {
|
||||
hoverHandler.onMouseLeave();
|
||||
setPreviewPath("");
|
||||
}
|
||||
|
||||
function handleSceneClick(
|
||||
event: React.MouseEvent<HTMLAnchorElement, MouseEvent>
|
||||
) {
|
||||
@@ -272,11 +304,7 @@ export const SceneCard: React.FC<ISceneCardProps> = (
|
||||
let shiftKey = false;
|
||||
|
||||
return (
|
||||
<Card
|
||||
className={`scene-card zoom-${props.zoomIndex}`}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
>
|
||||
<Card className={`scene-card zoom-${props.zoomIndex}`}>
|
||||
<Form.Control
|
||||
type="checkbox"
|
||||
className="scene-card-check"
|
||||
@@ -298,16 +326,16 @@ export const SceneCard: React.FC<ISceneCardProps> = (
|
||||
onDragOver={handleDragOver}
|
||||
draggable={props.selecting}
|
||||
>
|
||||
<ScenePreview
|
||||
image={props.scene.paths.screenshot ?? undefined}
|
||||
video={props.scene.paths.preview ?? undefined}
|
||||
isPortrait={isPortrait()}
|
||||
soundActive={
|
||||
config.data?.configuration?.interface?.soundOnPreview ?? false
|
||||
}
|
||||
/>
|
||||
{maybeRenderRatingBanner()}
|
||||
{maybeRenderSceneSpecsOverlay()}
|
||||
<video
|
||||
loop
|
||||
className={cx("scene-card-video", { portrait: isPortrait() })}
|
||||
poster={props.scene.paths.screenshot || ""}
|
||||
ref={hoverHandler.videoEl}
|
||||
>
|
||||
{previewPath ? <source src={previewPath} /> : ""}
|
||||
</video>
|
||||
</Link>
|
||||
{maybeRenderSceneStudioOverlay()}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user