Implement scroll lock for previews (#10180)

* Implement scroll lock

* Fix seekbar not working
This commit is contained in:
Nicolas Mowen 2024-03-01 17:34:41 -07:00 committed by GitHub
parent ebf34ce378
commit a67e970fca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 53 additions and 6 deletions

View File

@ -23,6 +23,7 @@ import { useSwipeable } from "react-swipeable";
type PreviewPlayerProps = { type PreviewPlayerProps = {
review: ReviewSegment; review: ReviewSegment;
allPreviews?: Preview[]; allPreviews?: Preview[];
scrollLock?: boolean;
onTimeUpdate?: React.Dispatch<React.SetStateAction<number | undefined>>; onTimeUpdate?: React.Dispatch<React.SetStateAction<number | undefined>>;
setReviewed: (reviewId: string) => void; setReviewed: (reviewId: string) => void;
onClick: (reviewId: string, ctrl: boolean) => void; onClick: (reviewId: string, ctrl: boolean) => void;
@ -39,6 +40,7 @@ type Preview = {
export default function PreviewThumbnailPlayer({ export default function PreviewThumbnailPlayer({
review, review,
allPreviews, allPreviews,
scrollLock = false,
setReviewed, setReviewed,
onClick, onClick,
onTimeUpdate, onTimeUpdate,
@ -116,12 +118,16 @@ export default function PreviewThumbnailPlayer({
return undefined; return undefined;
} }
}, [allPreviews]); }, [allPreviews, review]);
const playingBack = useMemo(() => playback, [playback]); const playingBack = useMemo(() => playback, [playback]);
const onPlayback = useCallback( const onPlayback = useCallback(
(isHovered: boolean) => { (isHovered: boolean) => {
if (isHovered && scrollLock) {
return;
}
if (isHovered) { if (isHovered) {
setHoverTimeout( setHoverTimeout(
setTimeout(() => { setTimeout(() => {
@ -144,7 +150,7 @@ export default function PreviewThumbnailPlayer({
// we know that these deps are correct // we know that these deps are correct
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
[hoverTimeout, review], [hoverTimeout, scrollLock, review],
); );
// date // date
@ -462,7 +468,7 @@ function VideoPreview({
<source src={relevantPreview.src} type={relevantPreview.type} /> <source src={relevantPreview.src} type={relevantPreview.type} />
</video> </video>
<Slider <Slider
className="absolute inset-x-0 bottom-0" className="absolute inset-x-0 bottom-0 z-30"
value={[progress]} value={[progress]}
onValueChange={onManualSeek} onValueChange={onManualSeek}
onValueCommit={onStopManualSeek} onValueCommit={onStopManualSeek}
@ -580,7 +586,7 @@ function InProgressPreview({
onLoad={handleLoad} onLoad={handleLoad}
/> />
<Slider <Slider
className="absolute inset-x-0 bottom-0" className="absolute inset-x-0 bottom-0 z-30"
value={[key]} value={[key]}
onValueChange={onManualSeek} onValueChange={onManualSeek}
onValueCommit={onStopManualSeek} onValueCommit={onStopManualSeek}

View 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;
}

View File

@ -7,6 +7,7 @@ import EventReviewTimeline from "@/components/timeline/EventReviewTimeline";
import ActivityIndicator from "@/components/ui/activity-indicator"; import ActivityIndicator from "@/components/ui/activity-indicator";
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"; import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
import { useEventUtils } from "@/hooks/use-event-utils"; import { useEventUtils } from "@/hooks/use-event-utils";
import { useScrollLockout } from "@/hooks/use-mouse-listener";
import { FrigateConfig } from "@/types/frigateConfig"; import { FrigateConfig } from "@/types/frigateConfig";
import { Preview } from "@/types/preview"; import { Preview } from "@/types/preview";
import { ReviewFilter, ReviewSegment, ReviewSeverity } from "@/types/review"; import { ReviewFilter, ReviewSegment, ReviewSeverity } from "@/types/review";
@ -192,6 +193,7 @@ export default function EventView({
// preview playback // preview playback
const [previewTime, setPreviewTime] = useState<number>(); const [previewTime, setPreviewTime] = useState<number>();
const scrollLock = useScrollLockout(contentRef);
// review interaction // review interaction
@ -220,7 +222,7 @@ export default function EventView({
onOpenReview(reviewId); onOpenReview(reviewId);
} }
}, },
[selectedReviews, setSelectedReviews], [selectedReviews, setSelectedReviews, onOpenReview],
); );
const exportReview = useCallback( const exportReview = useCallback(
@ -236,7 +238,7 @@ export default function EventView({
{ playback: "realtime" }, { playback: "realtime" },
); );
}, },
[selectedReviews], [currentItems],
); );
if (!config) { if (!config) {
@ -342,6 +344,7 @@ export default function EventView({
review={value} review={value}
allPreviews={relevantPreviews} allPreviews={relevantPreviews}
setReviewed={markItemAsReviewed} setReviewed={markItemAsReviewed}
scrollLock={scrollLock}
onTimeUpdate={setPreviewTime} onTimeUpdate={setPreviewTime}
onClick={onSelectReview} onClick={onSelectReview}
/> />