Implement swipe gesture instead of autoplay for mobile (#10110)

* Implement swipe gesture instead of autoplay

* Cleanup
This commit is contained in:
Nicolas Mowen 2024-02-27 20:39:06 -07:00 committed by GitHub
parent fd24007618
commit 3fed6a0ab3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 21 additions and 26 deletions

9
web/package-lock.json generated
View File

@ -46,6 +46,7 @@
"react-hook-form": "^7.48.2", "react-hook-form": "^7.48.2",
"react-icons": "^4.12.0", "react-icons": "^4.12.0",
"react-router-dom": "^6.20.1", "react-router-dom": "^6.20.1",
"react-swipeable": "^7.0.1",
"react-tracked": "^1.7.11", "react-tracked": "^1.7.11",
"react-transition-group": "^4.4.5", "react-transition-group": "^4.4.5",
"react-use-websocket": "^4.5.0", "react-use-websocket": "^4.5.0",
@ -6735,6 +6736,14 @@
} }
} }
}, },
"node_modules/react-swipeable": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/react-swipeable/-/react-swipeable-7.0.1.tgz",
"integrity": "sha512-RKB17JdQzvECfnVj9yDZsiYn3vH0eyva/ZbrCZXZR0qp66PBRhtg4F9yJcJTWYT5Adadi+x4NoG53BxKHwIYLQ==",
"peerDependencies": {
"react": "^16.8.3 || ^17 || ^18"
}
},
"node_modules/react-tracked": { "node_modules/react-tracked": {
"version": "1.7.11", "version": "1.7.11",
"resolved": "https://registry.npmjs.org/react-tracked/-/react-tracked-1.7.11.tgz", "resolved": "https://registry.npmjs.org/react-tracked/-/react-tracked-1.7.11.tgz",

View File

@ -51,6 +51,7 @@
"react-hook-form": "^7.48.2", "react-hook-form": "^7.48.2",
"react-icons": "^4.12.0", "react-icons": "^4.12.0",
"react-router-dom": "^6.20.1", "react-router-dom": "^6.20.1",
"react-swipeable": "^7.0.1",
"react-tracked": "^1.7.11", "react-tracked": "^1.7.11",
"react-transition-group": "^4.4.5", "react-transition-group": "^4.4.5",
"react-use-websocket": "^4.5.0", "react-use-websocket": "^4.5.0",

View File

@ -21,6 +21,7 @@ import axios from "axios";
import { useFormattedTimestamp } from "@/hooks/use-date-utils"; import { useFormattedTimestamp } from "@/hooks/use-date-utils";
import useImageLoaded from "@/hooks/use-image-loaded"; import useImageLoaded from "@/hooks/use-image-loaded";
import { Skeleton } from "../ui/skeleton"; import { Skeleton } from "../ui/skeleton";
import { useSwipeable } from "react-swipeable";
type PreviewPlayerProps = { type PreviewPlayerProps = {
review: ReviewSegment; review: ReviewSegment;
@ -41,7 +42,6 @@ type Preview = {
export default function PreviewThumbnailPlayer({ export default function PreviewThumbnailPlayer({
review, review,
relevantPreview, relevantPreview,
autoPlayback = false,
setReviewed, setReviewed,
onClick, onClick,
}: PreviewPlayerProps) { }: PreviewPlayerProps) {
@ -61,6 +61,13 @@ export default function PreviewThumbnailPlayer({
} }
}, [review, onClick]); }, [review, onClick]);
const swipeHandlers = useSwipeable({
onSwipedLeft: () => setPlayback(false),
onSwipedRight: () => setPlayback(true),
preventScrollOnSwipe: true,
});
const handleSetReviewed = useCallback(() => { const handleSetReviewed = useCallback(() => {
if (setReviewed) { if (setReviewed) {
setReviewed(review.id); setReviewed(review.id);
@ -69,27 +76,7 @@ export default function PreviewThumbnailPlayer({
// playback // playback
const playingBack = useMemo(() => playback, [playback, autoPlayback]); const playingBack = useMemo(() => playback, [playback]);
useEffect(() => {
if (!autoPlayback) {
setPlayback(false);
if (hoverTimeout) {
clearTimeout(hoverTimeout);
}
return;
}
const timeout = setTimeout(() => {
setPlayback(true);
setHoverTimeout(null);
}, 500);
return () => {
clearTimeout(timeout);
};
}, [autoPlayback]);
const onPlayback = useCallback( const onPlayback = useCallback(
(isHovered: Boolean) => { (isHovered: Boolean) => {
@ -127,6 +114,7 @@ export default function PreviewThumbnailPlayer({
onMouseEnter={isMobile ? undefined : () => onPlayback(true)} onMouseEnter={isMobile ? undefined : () => onPlayback(true)}
onMouseLeave={isMobile ? undefined : () => onPlayback(false)} onMouseLeave={isMobile ? undefined : () => onPlayback(false)}
onClick={handleOnClick} onClick={handleOnClick}
{...swipeHandlers}
> >
{playingBack && ( {playingBack && (
<div className="absolute left-0 top-0 right-0 bottom-0 animate-in fade-in"> <div className="absolute left-0 top-0 right-0 bottom-0 animate-in fade-in">

View File

@ -9,7 +9,7 @@ import { useEventUtils } from "@/hooks/use-event-utils";
import { FrigateConfig } from "@/types/frigateConfig"; import { FrigateConfig } from "@/types/frigateConfig";
import { ReviewFilter, ReviewSegment, ReviewSeverity } from "@/types/review"; import { ReviewFilter, ReviewSegment, ReviewSeverity } from "@/types/review";
import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { isDesktop, isMobile } from "react-device-detect"; import { isDesktop } from "react-device-detect";
import { LuFolderCheck } from "react-icons/lu"; import { LuFolderCheck } from "react-icons/lu";
import { MdCircle } from "react-icons/md"; import { MdCircle } from "react-icons/md";
import useSWR from "swr"; import useSWR from "swr";
@ -283,9 +283,6 @@ export default function EventView({
relevantPreview={relevantPreview} relevantPreview={relevantPreview}
setReviewed={markItemAsReviewed} setReviewed={markItemAsReviewed}
onClick={onSelectReview} onClick={onSelectReview}
autoPlayback={
isMobile && minimapBounds.end == value.start_time
}
/> />
</div> </div>
{lastRow && !reachedEnd && <ActivityIndicator />} {lastRow && !reachedEnd && <ActivityIndicator />}