mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-07 03:35:26 +03:00
Handle autoplay for mobile
This commit is contained in:
parent
08e9120db5
commit
7e13501787
@ -22,11 +22,13 @@ import { formatUnixTimestampToDateTime } from "@/utils/dateUtil";
|
||||
type HistoryCardProps = {
|
||||
timeline: Card;
|
||||
relevantPreview?: Preview;
|
||||
shouldAutoPlay: boolean;
|
||||
};
|
||||
|
||||
export default function HistoryCard({
|
||||
relevantPreview,
|
||||
timeline,
|
||||
shouldAutoPlay,
|
||||
}: HistoryCardProps) {
|
||||
const { data: config } = useSWR<FrigateConfig>("config");
|
||||
|
||||
@ -41,6 +43,7 @@ export default function HistoryCard({
|
||||
relevantPreview={relevantPreview}
|
||||
startTs={Object.values(timeline.entries)[0].timestamp}
|
||||
eventId={Object.values(timeline.entries)[0].source_id}
|
||||
shouldAutoPlay={shouldAutoPlay}
|
||||
/>
|
||||
<div className="p-2">
|
||||
<div className="text-sm flex">
|
||||
|
||||
@ -11,6 +11,7 @@ type PreviewPlayerProps = {
|
||||
relevantPreview?: Preview;
|
||||
startTs: number;
|
||||
eventId: string;
|
||||
shouldAutoPlay: boolean;
|
||||
};
|
||||
|
||||
type Preview = {
|
||||
@ -26,12 +27,13 @@ export default function PreviewThumbnailPlayer({
|
||||
relevantPreview,
|
||||
startTs,
|
||||
eventId,
|
||||
shouldAutoPlay,
|
||||
}: PreviewPlayerProps) {
|
||||
const { data: config } = useSWR("config");
|
||||
const playerRef = useRef<Player | null>(null);
|
||||
const apiHost = useApiHost();
|
||||
|
||||
const onHover = useCallback(
|
||||
const onPlayback = useCallback(
|
||||
(isHovered: Boolean) => {
|
||||
if (!relevantPreview || !playerRef.current) {
|
||||
return;
|
||||
@ -47,6 +49,32 @@ export default function PreviewThumbnailPlayer({
|
||||
[relevantPreview, startTs]
|
||||
);
|
||||
|
||||
const observer = useRef<IntersectionObserver | null>();
|
||||
const inViewRef = useCallback(
|
||||
(node: HTMLElement | null) => {
|
||||
if (!shouldAutoPlay || observer.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
observer.current = new IntersectionObserver(
|
||||
(entries) => {
|
||||
if (entries[0].isIntersecting) {
|
||||
onPlayback(true);
|
||||
} else {
|
||||
onPlayback(false);
|
||||
}
|
||||
},
|
||||
{ threshold: 1.0 }
|
||||
);
|
||||
if (node) observer.current.observe(node);
|
||||
} catch (e) {
|
||||
// no op
|
||||
}
|
||||
},
|
||||
[observer, onPlayback]
|
||||
);
|
||||
|
||||
if (!relevantPreview) {
|
||||
if (isCurrentHour(startTs)) {
|
||||
return (
|
||||
@ -56,6 +84,7 @@ export default function PreviewThumbnailPlayer({
|
||||
>
|
||||
<img
|
||||
className={`${getPreviewWidth(camera, config)}`}
|
||||
loading="lazy"
|
||||
src={`${apiHost}api/preview/${camera}/${startTs}/thumbnail.jpg`}
|
||||
/>
|
||||
</AspectRatio>
|
||||
@ -69,6 +98,7 @@ export default function PreviewThumbnailPlayer({
|
||||
>
|
||||
<img
|
||||
className="w-[160px]"
|
||||
loading="lazy"
|
||||
src={`${apiHost}api/events/${eventId}/thumbnail.jpg`}
|
||||
/>
|
||||
</AspectRatio>
|
||||
@ -77,10 +107,11 @@ export default function PreviewThumbnailPlayer({
|
||||
|
||||
return (
|
||||
<AspectRatio
|
||||
ref={shouldAutoPlay ? inViewRef : null}
|
||||
ratio={16 / 9}
|
||||
className="bg-black flex justify-center items-center"
|
||||
onMouseEnter={() => onHover(true)}
|
||||
onMouseLeave={() => onHover(false)}
|
||||
onMouseEnter={() => onPlayback(true)}
|
||||
onMouseLeave={() => onPlayback(false)}
|
||||
>
|
||||
<div className={`${getPreviewWidth(camera, config)}`}>
|
||||
<VideoPlayer
|
||||
|
||||
@ -44,7 +44,7 @@ export default function VideoPlayer({ children, options, seekOptions = {forward:
|
||||
videoElement.classList.add('small-player');
|
||||
videoElement.classList.add('video-js');
|
||||
videoElement.classList.add('vjs-default-skin');
|
||||
videoRef.current.appendChild(videoElement);
|
||||
videoRef.current?.appendChild(videoElement);
|
||||
|
||||
const player = playerRef.current = videojs(videoElement, { ...defaultOptions, ...options }, () => {
|
||||
onReady && onReady(player);
|
||||
|
||||
@ -5,11 +5,10 @@ import { FrigateConfig } from "@/types/frigateConfig";
|
||||
import Heading from "@/components/ui/heading";
|
||||
import ActivityIndicator from "@/components/ui/activity-indicator";
|
||||
import HistoryCard from "@/components/card/HistoryCard";
|
||||
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
|
||||
import { formatUnixTimestampToDateTime } from "@/utils/dateUtil";
|
||||
import axios from "axios";
|
||||
|
||||
const API_LIMIT = 200;
|
||||
const API_LIMIT = 100;
|
||||
|
||||
function History() {
|
||||
const { data: config } = useSWR<FrigateConfig>("config");
|
||||
@ -26,11 +25,15 @@ function History() {
|
||||
const getKey = useCallback((index: number, prevData: HourlyTimeline) => {
|
||||
if (index > 0) {
|
||||
const lastDate = prevData.end;
|
||||
const pagedParams = { before: lastDate, timezone };
|
||||
const pagedParams = { before: lastDate, timezone, limit: API_LIMIT };
|
||||
return ["timeline/hourly", pagedParams];
|
||||
}
|
||||
|
||||
return ["timeline/hourly", { timezone }];
|
||||
return ["timeline/hourly", { timezone, limit: API_LIMIT }];
|
||||
}, []);
|
||||
|
||||
const shouldAutoPlay = useMemo(() => {
|
||||
return window.innerWidth < 480;
|
||||
}, []);
|
||||
|
||||
const {
|
||||
@ -207,6 +210,7 @@ function History() {
|
||||
<HistoryCard
|
||||
key={key}
|
||||
timeline={timeline}
|
||||
shouldAutoPlay={shouldAutoPlay}
|
||||
relevantPreview={Object.values(
|
||||
allPreviews || []
|
||||
).find(
|
||||
|
||||
Loading…
Reference in New Issue
Block a user