From 410dd27d93bd3fce553902f334eccde0311f17da Mon Sep 17 00:00:00 2001 From: WithoutPants <53250216+WithoutPants@users.noreply.github.com> Date: Wed, 25 Feb 2026 10:54:20 +1100 Subject: [PATCH] Fix misclicks resulting in navigating to new page during selection (#6599) * Disable studio overlay link if selecting * Prevent scene preview scrubber click navigating during selection * Prevent gallery preview scrubber click navigating during selection --- .../src/components/Galleries/GalleryCard.tsx | 17 +++++++++++++++-- .../Galleries/GalleryPreviewScrubber.tsx | 3 +++ ui/v2.5/src/components/Images/ImageCard.tsx | 8 +++++++- .../src/components/Scenes/PreviewScrubber.tsx | 3 +++ ui/v2.5/src/components/Scenes/SceneCard.tsx | 18 ++++++++++++++++-- .../Shared/GridCard/StudioOverlay.tsx | 11 +++++++++-- .../src/components/Shared/HoverScrubber.tsx | 7 +++++++ 7 files changed, 60 insertions(+), 7 deletions(-) diff --git a/ui/v2.5/src/components/Galleries/GalleryCard.tsx b/ui/v2.5/src/components/Galleries/GalleryCard.tsx index e4e227f3e..01e0b6045 100644 --- a/ui/v2.5/src/components/Galleries/GalleryCard.tsx +++ b/ui/v2.5/src/components/Galleries/GalleryCard.tsx @@ -1,5 +1,5 @@ import { Button, ButtonGroup, OverlayTrigger, Tooltip } from "react-bootstrap"; -import React, { useState } from "react"; +import React, { useMemo, useState } from "react"; import * as GQL from "src/core/generated-graphql"; import { GridCard } from "../Shared/GridCard/GridCard"; import { HoverPopover } from "../Shared/HoverPopover"; @@ -21,11 +21,13 @@ import { PatchComponent } from "src/patch"; interface IGalleryPreviewProps { gallery: GQL.SlimGalleryDataFragment; onScrubberClick?: (index: number) => void; + disabled?: boolean; } export const GalleryPreview: React.FC = ({ gallery, onScrubberClick, + disabled, }) => { const [imgSrc, setImgSrc] = useState( gallery.paths.cover ?? undefined @@ -48,6 +50,7 @@ export const GalleryPreview: React.FC = ({ imageCount={gallery.image_count} onClick={onScrubberClick} onPathChanged={setImgSrc} + disabled={disabled} /> )} @@ -195,7 +198,16 @@ const GalleryCardDetails = PatchComponent( const GalleryCardOverlays = PatchComponent( "GalleryCard.Overlays", (props: IGalleryCardProps) => { - return ; + const ret = useMemo(() => { + return ( + + ); + }, [props.gallery.studio, props.selecting]); + + return ret; } ); @@ -211,6 +223,7 @@ const GalleryCardImage = PatchComponent( onScrubberClick={(i) => { history.push(`/galleries/${props.gallery.id}/images/${i}`); }} + disabled={props.selecting} /> diff --git a/ui/v2.5/src/components/Galleries/GalleryPreviewScrubber.tsx b/ui/v2.5/src/components/Galleries/GalleryPreviewScrubber.tsx index ef47782bf..5c0a07356 100644 --- a/ui/v2.5/src/components/Galleries/GalleryPreviewScrubber.tsx +++ b/ui/v2.5/src/components/Galleries/GalleryPreviewScrubber.tsx @@ -10,6 +10,7 @@ export const GalleryPreviewScrubber: React.FC<{ imageCount: number; onClick?: (imageIndex: number) => void; onPathChanged: React.Dispatch>; + disabled?: boolean; }> = ({ className, previewPath, @@ -17,6 +18,7 @@ export const GalleryPreviewScrubber: React.FC<{ imageCount, onClick, onPathChanged, + disabled, }) => { const [activeIndex, setActiveIndex] = useState(); const debounceSetActiveIndex = useThrottle(setActiveIndex, 50); @@ -48,6 +50,7 @@ export const GalleryPreviewScrubber: React.FC<{ activeIndex={activeIndex} setActiveIndex={(i) => debounceSetActiveIndex(i)} onClick={onScrubberClick} + disabled={disabled} /> ); diff --git a/ui/v2.5/src/components/Images/ImageCard.tsx b/ui/v2.5/src/components/Images/ImageCard.tsx index adaee9923..a1189c844 100644 --- a/ui/v2.5/src/components/Images/ImageCard.tsx +++ b/ui/v2.5/src/components/Images/ImageCard.tsx @@ -148,7 +148,13 @@ const ImageCardDetails = PatchComponent( const ImageCardOverlays = PatchComponent( "ImageCard.Overlays", (props: IImageCardProps) => { - return ; + const ret = useMemo(() => { + return ( + + ); + }, [props.image.studio, props.selecting]); + + return ret; } ); diff --git a/ui/v2.5/src/components/Scenes/PreviewScrubber.tsx b/ui/v2.5/src/components/Scenes/PreviewScrubber.tsx index 8ecb6e557..8c9d3097d 100644 --- a/ui/v2.5/src/components/Scenes/PreviewScrubber.tsx +++ b/ui/v2.5/src/components/Scenes/PreviewScrubber.tsx @@ -13,6 +13,7 @@ import { HoverScrubber } from "../Shared/HoverScrubber"; interface IScenePreviewProps { vttPath: string | undefined; onClick?: (timestamp: number) => void; + disabled?: boolean; } function scaleToFit(dimensions: { w: number; h: number }, bounds: DOMRect) { @@ -32,6 +33,7 @@ const defaultSprites = 81; // 9x9 grid by default export const PreviewScrubber: React.FC = ({ vttPath, onClick, + disabled, }) => { const imageParentRef = useRef(null); const [style, setStyle] = useState({}); @@ -113,6 +115,7 @@ export const PreviewScrubber: React.FC = ({ activeIndex={activeIndex} setActiveIndex={(i) => debounceSetActiveIndex(i)} onClick={onScrubberClick} + disabled={disabled} /> ); diff --git a/ui/v2.5/src/components/Scenes/SceneCard.tsx b/ui/v2.5/src/components/Scenes/SceneCard.tsx index 2cb4a9af3..b7c263168 100644 --- a/ui/v2.5/src/components/Scenes/SceneCard.tsx +++ b/ui/v2.5/src/components/Scenes/SceneCard.tsx @@ -38,6 +38,7 @@ interface IScenePreviewProps { soundActive: boolean; vttPath?: string; onScrubberClick?: (timestamp: number) => void; + disabled?: boolean; } export const ScenePreview: React.FC = ({ @@ -47,6 +48,7 @@ export const ScenePreview: React.FC = ({ soundActive, vttPath, onScrubberClick, + disabled, }) => { const videoEl = useRef(null); @@ -86,7 +88,11 @@ export const ScenePreview: React.FC = ({ ref={videoEl} src={video} /> - + ); }; @@ -336,7 +342,13 @@ const SceneCardDetails = PatchComponent( const SceneCardOverlays = PatchComponent( "SceneCard.Overlays", (props: ISceneCardProps) => { - return ; + const ret = useMemo(() => { + return ( + + ); + }, [props.scene.studio, props.selecting]); + + return ret; } ); @@ -390,6 +402,7 @@ const SceneCardImage = PatchComponent( } function onScrubberClick(timestamp: number) { + if (props.selecting) return; const link = props.queue ? props.queue.makeLink(props.scene.id, { sceneIndex: props.index, @@ -416,6 +429,7 @@ const SceneCardImage = PatchComponent( soundActive={configuration?.interface?.soundOnPreview ?? false} vttPath={props.scene.paths.vtt ?? undefined} onScrubberClick={onScrubberClick} + disabled={props.selecting} /> {maybeRenderSceneSpecsOverlay()} diff --git a/ui/v2.5/src/components/Shared/GridCard/StudioOverlay.tsx b/ui/v2.5/src/components/Shared/GridCard/StudioOverlay.tsx index 9bfd25071..6fe07a454 100644 --- a/ui/v2.5/src/components/Shared/GridCard/StudioOverlay.tsx +++ b/ui/v2.5/src/components/Shared/GridCard/StudioOverlay.tsx @@ -10,7 +10,8 @@ interface IStudio { export const StudioOverlay: React.FC<{ studio: IStudio | null | undefined; -}> = ({ studio }) => { + disabled?: boolean; +}> = ({ studio, disabled }) => { const { configuration } = useConfigurationContext(); const configValue = configuration?.interface.showStudioAsText; @@ -29,12 +30,18 @@ export const StudioOverlay: React.FC<{ return false; }, [configValue, studio?.image_path]); + function onClick(e: React.MouseEvent) { + if (disabled) { + e.preventDefault(); + } + } + if (!studio) return <>; return ( // this class name is incorrect
- + {showStudioAsText ? ( studio.name ) : ( diff --git a/ui/v2.5/src/components/Shared/HoverScrubber.tsx b/ui/v2.5/src/components/Shared/HoverScrubber.tsx index 7c07e8adc..17c0ed79e 100644 --- a/ui/v2.5/src/components/Shared/HoverScrubber.tsx +++ b/ui/v2.5/src/components/Shared/HoverScrubber.tsx @@ -9,6 +9,7 @@ interface IHoverScrubber { activeIndex: number | undefined; setActiveIndex: (index: number | undefined) => void; onClick?: (index: number) => void; + disabled?: boolean; } export const HoverScrubber: React.FC = ({ @@ -16,6 +17,7 @@ export const HoverScrubber: React.FC = ({ activeIndex, setActiveIndex, onClick, + disabled, }) => { function getActiveIndex( e: @@ -69,6 +71,11 @@ export const HoverScrubber: React.FC = ({ | React.TouchEvent ) { if (!onClick) return; + if (disabled) { + // allow propagation up so that selection still works + e.preventDefault(); + return; + } const relatedTarget = e.currentTarget;