mirror of
				https://github.com/blakeblackshear/frigate.git
				synced 2025-10-27 10:52:11 +01:00 
			
		
		
		
	Implement scroll lock for previews (#10180)
* Implement scroll lock * Fix seekbar not working
This commit is contained in:
		
							parent
							
								
									ebf34ce378
								
							
						
					
					
						commit
						a67e970fca
					
				@ -23,6 +23,7 @@ import { useSwipeable } from "react-swipeable";
 | 
			
		||||
type PreviewPlayerProps = {
 | 
			
		||||
  review: ReviewSegment;
 | 
			
		||||
  allPreviews?: Preview[];
 | 
			
		||||
  scrollLock?: boolean;
 | 
			
		||||
  onTimeUpdate?: React.Dispatch<React.SetStateAction<number | undefined>>;
 | 
			
		||||
  setReviewed: (reviewId: string) => void;
 | 
			
		||||
  onClick: (reviewId: string, ctrl: boolean) => void;
 | 
			
		||||
@ -39,6 +40,7 @@ type Preview = {
 | 
			
		||||
export default function PreviewThumbnailPlayer({
 | 
			
		||||
  review,
 | 
			
		||||
  allPreviews,
 | 
			
		||||
  scrollLock = false,
 | 
			
		||||
  setReviewed,
 | 
			
		||||
  onClick,
 | 
			
		||||
  onTimeUpdate,
 | 
			
		||||
@ -116,12 +118,16 @@ export default function PreviewThumbnailPlayer({
 | 
			
		||||
 | 
			
		||||
      return undefined;
 | 
			
		||||
    }
 | 
			
		||||
  }, [allPreviews]);
 | 
			
		||||
  }, [allPreviews, review]);
 | 
			
		||||
 | 
			
		||||
  const playingBack = useMemo(() => playback, [playback]);
 | 
			
		||||
 | 
			
		||||
  const onPlayback = useCallback(
 | 
			
		||||
    (isHovered: boolean) => {
 | 
			
		||||
      if (isHovered && scrollLock) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (isHovered) {
 | 
			
		||||
        setHoverTimeout(
 | 
			
		||||
          setTimeout(() => {
 | 
			
		||||
@ -144,7 +150,7 @@ export default function PreviewThumbnailPlayer({
 | 
			
		||||
 | 
			
		||||
    // we know that these deps are correct
 | 
			
		||||
    // eslint-disable-next-line react-hooks/exhaustive-deps
 | 
			
		||||
    [hoverTimeout, review],
 | 
			
		||||
    [hoverTimeout, scrollLock, review],
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  // date
 | 
			
		||||
@ -462,7 +468,7 @@ function VideoPreview({
 | 
			
		||||
        <source src={relevantPreview.src} type={relevantPreview.type} />
 | 
			
		||||
      </video>
 | 
			
		||||
      <Slider
 | 
			
		||||
        className="absolute inset-x-0 bottom-0"
 | 
			
		||||
        className="absolute inset-x-0 bottom-0 z-30"
 | 
			
		||||
        value={[progress]}
 | 
			
		||||
        onValueChange={onManualSeek}
 | 
			
		||||
        onValueCommit={onStopManualSeek}
 | 
			
		||||
@ -580,7 +586,7 @@ function InProgressPreview({
 | 
			
		||||
        onLoad={handleLoad}
 | 
			
		||||
      />
 | 
			
		||||
      <Slider
 | 
			
		||||
        className="absolute inset-x-0 bottom-0"
 | 
			
		||||
        className="absolute inset-x-0 bottom-0 z-30"
 | 
			
		||||
        value={[key]}
 | 
			
		||||
        onValueChange={onManualSeek}
 | 
			
		||||
        onValueCommit={onStopManualSeek}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										38
									
								
								web/src/hooks/use-mouse-listener.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								web/src/hooks/use-mouse-listener.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,38 @@
 | 
			
		||||
import { MutableRefObject, useCallback, useEffect, useState } from "react";
 | 
			
		||||
import { isMobile } from "react-device-detect";
 | 
			
		||||
 | 
			
		||||
export function useScrollLockout(ref: MutableRefObject<HTMLElement | null>) {
 | 
			
		||||
  const [scrollLock, setScrollLockout] = useState(false);
 | 
			
		||||
 | 
			
		||||
  const onScroll = useCallback(() => {
 | 
			
		||||
    if (!scrollLock) {
 | 
			
		||||
      setScrollLockout(true);
 | 
			
		||||
    }
 | 
			
		||||
  }, [scrollLock, setScrollLockout]);
 | 
			
		||||
  const onMouseMove = useCallback(() => {
 | 
			
		||||
    if (scrollLock) {
 | 
			
		||||
      setScrollLockout(false);
 | 
			
		||||
    }
 | 
			
		||||
  }, [scrollLock, setScrollLockout]);
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (isMobile) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!ref.current) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const content = ref.current;
 | 
			
		||||
    content.addEventListener("scroll", onScroll);
 | 
			
		||||
    content.addEventListener("mousemove", onMouseMove);
 | 
			
		||||
 | 
			
		||||
    return () => {
 | 
			
		||||
      content.removeEventListener("scroll", onScroll);
 | 
			
		||||
      content.removeEventListener("mousemove", onMouseMove);
 | 
			
		||||
    };
 | 
			
		||||
  }, [ref, onScroll, onMouseMove]);
 | 
			
		||||
 | 
			
		||||
  return scrollLock;
 | 
			
		||||
}
 | 
			
		||||
@ -7,6 +7,7 @@ import EventReviewTimeline from "@/components/timeline/EventReviewTimeline";
 | 
			
		||||
import ActivityIndicator from "@/components/ui/activity-indicator";
 | 
			
		||||
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
 | 
			
		||||
import { useEventUtils } from "@/hooks/use-event-utils";
 | 
			
		||||
import { useScrollLockout } from "@/hooks/use-mouse-listener";
 | 
			
		||||
import { FrigateConfig } from "@/types/frigateConfig";
 | 
			
		||||
import { Preview } from "@/types/preview";
 | 
			
		||||
import { ReviewFilter, ReviewSegment, ReviewSeverity } from "@/types/review";
 | 
			
		||||
@ -192,6 +193,7 @@ export default function EventView({
 | 
			
		||||
  // preview playback
 | 
			
		||||
 | 
			
		||||
  const [previewTime, setPreviewTime] = useState<number>();
 | 
			
		||||
  const scrollLock = useScrollLockout(contentRef);
 | 
			
		||||
 | 
			
		||||
  // review interaction
 | 
			
		||||
 | 
			
		||||
@ -220,7 +222,7 @@ export default function EventView({
 | 
			
		||||
        onOpenReview(reviewId);
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    [selectedReviews, setSelectedReviews],
 | 
			
		||||
    [selectedReviews, setSelectedReviews, onOpenReview],
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const exportReview = useCallback(
 | 
			
		||||
@ -236,7 +238,7 @@ export default function EventView({
 | 
			
		||||
        { playback: "realtime" },
 | 
			
		||||
      );
 | 
			
		||||
    },
 | 
			
		||||
    [selectedReviews],
 | 
			
		||||
    [currentItems],
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  if (!config) {
 | 
			
		||||
@ -342,6 +344,7 @@ export default function EventView({
 | 
			
		||||
                        review={value}
 | 
			
		||||
                        allPreviews={relevantPreviews}
 | 
			
		||||
                        setReviewed={markItemAsReviewed}
 | 
			
		||||
                        scrollLock={scrollLock}
 | 
			
		||||
                        onTimeUpdate={setPreviewTime}
 | 
			
		||||
                        onClick={onSelectReview}
 | 
			
		||||
                      />
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user