mirror of
https://github.com/stashapp/stash.git
synced 2026-04-01 19:11:33 +02:00
Make hover volume configurable (#6712)
This commit is contained in:
parent
c583e88caf
commit
5fd0d7bd68
5 changed files with 85 additions and 31 deletions
|
|
@ -30,12 +30,14 @@ import { StudioOverlay } from "../Shared/GridCard/StudioOverlay";
|
|||
import { GroupTag } from "../Groups/GroupTag";
|
||||
import { FileSize } from "../Shared/FileSize";
|
||||
import { OCounterButton } from "../Shared/CountButton";
|
||||
import { defaultPreviewVolume } from "src/core/config";
|
||||
|
||||
interface IScenePreviewProps {
|
||||
isPortrait: boolean;
|
||||
image?: string;
|
||||
video?: string;
|
||||
soundActive: boolean;
|
||||
volume?: number;
|
||||
vttPath?: string;
|
||||
onScrubberClick?: (timestamp: number) => void;
|
||||
disabled?: boolean;
|
||||
|
|
@ -49,6 +51,7 @@ export const ScenePreview: React.FC<IScenePreviewProps> = ({
|
|||
vttPath,
|
||||
onScrubberClick,
|
||||
disabled,
|
||||
volume,
|
||||
}) => {
|
||||
const videoEl = useRef<HTMLVideoElement>(null);
|
||||
|
||||
|
|
@ -67,8 +70,8 @@ export const ScenePreview: React.FC<IScenePreviewProps> = ({
|
|||
|
||||
useEffect(() => {
|
||||
if (videoEl?.current?.volume)
|
||||
videoEl.current.volume = soundActive ? 0.05 : 0;
|
||||
}, [soundActive]);
|
||||
videoEl.current.volume = soundActive ? (volume ?? 0) / 100 : 0;
|
||||
}, [volume, soundActive]);
|
||||
|
||||
return (
|
||||
<div className={cx("scene-card-preview", { portrait: isPortrait })}>
|
||||
|
|
@ -431,6 +434,7 @@ const SceneCardImage = PatchComponent(
|
|||
video={props.scene.paths.preview ?? undefined}
|
||||
isPortrait={isPortrait()}
|
||||
soundActive={configuration?.interface?.soundOnPreview ?? false}
|
||||
volume={configuration?.ui.previewVolume ?? defaultPreviewVolume}
|
||||
vttPath={props.scene.paths.vtt ?? undefined}
|
||||
onScrubberClick={onScrubberClick}
|
||||
disabled={props.selecting}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,10 @@
|
|||
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import React, {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { Form } from "react-bootstrap";
|
||||
import * as GQL from "src/core/generated-graphql";
|
||||
import { SceneQueue } from "src/models/sceneQueue";
|
||||
|
|
@ -15,6 +21,7 @@ import TextUtils from "src/utils/text";
|
|||
import { useIntl } from "react-intl";
|
||||
import { useDragMoveSelect } from "../Shared/GridCard/dragMoveSelect";
|
||||
import cx from "classnames";
|
||||
import { defaultPreviewVolume } from "src/core/config";
|
||||
|
||||
interface IScenePhoto {
|
||||
scene: GQL.SlimSceneDataFragment;
|
||||
|
|
@ -42,6 +49,7 @@ export const SceneWallItem: React.FC<
|
|||
|
||||
const { configuration } = useConfigurationContext();
|
||||
const playSound = configuration?.interface.soundOnPreview ?? false;
|
||||
const volume = configuration?.ui.previewVolume ?? defaultPreviewVolume;
|
||||
const showTitle = configuration?.interface.wallShowTitle ?? false;
|
||||
|
||||
const height = Math.min(props.maxHeight, props.photo.height);
|
||||
|
|
@ -75,7 +83,31 @@ export const SceneWallItem: React.FC<
|
|||
};
|
||||
|
||||
const video = props.photo.src.includes("preview");
|
||||
const ImagePreview = video ? "video" : "img";
|
||||
const previewProps = {
|
||||
loading: "lazy",
|
||||
loop: video,
|
||||
muted: !video || !playSound || !active,
|
||||
autoPlay: video,
|
||||
playsInline: video,
|
||||
key: props.photo.key,
|
||||
src: props.photo.src,
|
||||
width,
|
||||
height,
|
||||
alt: props.photo.alt,
|
||||
onMouseEnter: () => setActive(true),
|
||||
onMouseLeave: () => setActive(false),
|
||||
onClick: handleClick,
|
||||
onError: () => {
|
||||
props.photo.onError?.(props.photo);
|
||||
},
|
||||
};
|
||||
|
||||
const videoEl = useRef<HTMLVideoElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (video && videoEl?.current?.volume)
|
||||
videoEl.current.volume = playSound ? volume / 100 : 0;
|
||||
}, [video, playSound, volume]);
|
||||
|
||||
const { scene } = props.photo;
|
||||
const title = objectTitle(scene);
|
||||
|
|
@ -111,24 +143,11 @@ export const SceneWallItem: React.FC<
|
|||
}}
|
||||
/>
|
||||
)}
|
||||
<ImagePreview
|
||||
loading="lazy"
|
||||
loop={video}
|
||||
muted={!video || !playSound || !active}
|
||||
autoPlay={video}
|
||||
playsInline={video}
|
||||
key={props.photo.key}
|
||||
src={props.photo.src}
|
||||
width={width}
|
||||
height={height}
|
||||
alt={props.photo.alt}
|
||||
onMouseEnter={() => setActive(true)}
|
||||
onMouseLeave={() => setActive(false)}
|
||||
onClick={handleClick}
|
||||
onError={() => {
|
||||
props.photo.onError?.(props.photo);
|
||||
}}
|
||||
/>
|
||||
{video ? (
|
||||
<video {...previewProps} ref={videoEl} />
|
||||
) : (
|
||||
<img {...previewProps} loading="lazy" />
|
||||
)}
|
||||
<div className="lineargradient">
|
||||
<footer className="wall-item-footer">
|
||||
<Link
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ import {
|
|||
defaultImageWallDirection,
|
||||
defaultImageWallMargin,
|
||||
} from "src/utils/imageWall";
|
||||
import { defaultMaxOptionsShown } from "src/core/config";
|
||||
import { defaultMaxOptionsShown, defaultPreviewVolume } from "src/core/config";
|
||||
import { PatchComponent } from "src/patch";
|
||||
|
||||
const allMenuItems = [
|
||||
|
|
@ -309,6 +309,32 @@ export const SettingsInterfacePanel: React.FC = PatchComponent(
|
|||
/>
|
||||
</SettingSection>
|
||||
|
||||
<SettingSection headingID="config.ui.scene_view.heading">
|
||||
<BooleanSetting
|
||||
id="sound-on-hover"
|
||||
headingID="config.ui.scene_wall.options.toggle_sound"
|
||||
checked={iface.soundOnPreview ?? undefined}
|
||||
onChange={(v) => saveInterface({ soundOnPreview: v })}
|
||||
/>
|
||||
<ModalSetting<number>
|
||||
id="preview-volume"
|
||||
headingID="config.ui.scene_view.options.preview_volume.heading"
|
||||
subHeadingID="config.ui.scene_view.options.preview_volume.description"
|
||||
value={ui.previewVolume ?? defaultPreviewVolume}
|
||||
onChange={(v) => saveUI({ previewVolume: v })}
|
||||
disabled={!iface.soundOnPreview}
|
||||
renderField={(value, setValue) => (
|
||||
<PercentInput
|
||||
numericValue={value}
|
||||
onValueChange={(v) => setValue(v ?? 0)}
|
||||
/>
|
||||
)}
|
||||
renderValue={(v) => {
|
||||
return <span>{v}%</span>;
|
||||
}}
|
||||
/>
|
||||
</SettingSection>
|
||||
|
||||
<SettingSection headingID="config.ui.scene_wall.heading">
|
||||
<BooleanSetting
|
||||
id="wall-show-title"
|
||||
|
|
@ -316,13 +342,6 @@ export const SettingsInterfacePanel: React.FC = PatchComponent(
|
|||
checked={iface.wallShowTitle ?? undefined}
|
||||
onChange={(v) => saveInterface({ wallShowTitle: v })}
|
||||
/>
|
||||
<BooleanSetting
|
||||
id="wall-sound-enabled"
|
||||
headingID="config.ui.scene_wall.options.toggle_sound"
|
||||
checked={iface.soundOnPreview ?? undefined}
|
||||
onChange={(v) => saveInterface({ soundOnPreview: v })}
|
||||
/>
|
||||
|
||||
<SelectSetting
|
||||
advanced
|
||||
id="wall-preview"
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ export type DefaultFilters = {
|
|||
export type FrontPageContent = ISavedFilterRow | ICustomFilter;
|
||||
|
||||
export const defaultMaxOptionsShown = 200;
|
||||
export const defaultPreviewVolume = 25;
|
||||
|
||||
export interface IUIConfig {
|
||||
// unknown to prevent direct access - use getFrontPageContent
|
||||
|
|
@ -48,6 +49,8 @@ export interface IUIConfig {
|
|||
showLinksOnPerformerCard?: boolean;
|
||||
showTagCardOnHover?: boolean;
|
||||
|
||||
previewVolume?: number;
|
||||
|
||||
abbreviateCounters?: boolean;
|
||||
|
||||
ratingSystemOptions?: RatingSystemOptions;
|
||||
|
|
|
|||
|
|
@ -843,11 +843,20 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"scene_view": {
|
||||
"heading": "Scene View",
|
||||
"options": {
|
||||
"preview_volume": {
|
||||
"description": "Volume of the preview when hovering over a scene. Set to 0 to mute.",
|
||||
"heading": "Preview volume"
|
||||
}
|
||||
}
|
||||
},
|
||||
"scene_wall": {
|
||||
"heading": "Scene / Marker Wall",
|
||||
"options": {
|
||||
"display_title": "Display title and tags",
|
||||
"toggle_sound": "Enable sound"
|
||||
"toggle_sound": "Enable sound on preview hover"
|
||||
}
|
||||
},
|
||||
"scroll_attempts_before_change": {
|
||||
|
|
|
|||
Loading…
Reference in a new issue