mirror of
				https://github.com/blakeblackshear/frigate.git
				synced 2025-10-27 10:52:11 +01:00 
			
		
		
		
	Simplify preview refreshing with custom hook (#11293)
This commit is contained in:
		
							parent
							
								
									db8c820677
								
							
						
					
					
						commit
						3ed89ec042
					
				@ -7,12 +7,12 @@ import { REVIEW_PADDING, ReviewSegment } from "@/types/review";
 | 
				
			|||||||
import { useNavigate } from "react-router-dom";
 | 
					import { useNavigate } from "react-router-dom";
 | 
				
			||||||
import { RecordingStartingPoint } from "@/types/record";
 | 
					import { RecordingStartingPoint } from "@/types/record";
 | 
				
			||||||
import axios from "axios";
 | 
					import axios from "axios";
 | 
				
			||||||
import { Preview } from "@/types/preview";
 | 
					 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  InProgressPreview,
 | 
					  InProgressPreview,
 | 
				
			||||||
  VideoPreview,
 | 
					  VideoPreview,
 | 
				
			||||||
} from "../player/PreviewThumbnailPlayer";
 | 
					} from "../player/PreviewThumbnailPlayer";
 | 
				
			||||||
import { isCurrentHour } from "@/utils/dateUtil";
 | 
					import { isCurrentHour } from "@/utils/dateUtil";
 | 
				
			||||||
 | 
					import { useCameraPreviews } from "@/hooks/use-camera-previews";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type AnimatedEventCardProps = {
 | 
					type AnimatedEventCardProps = {
 | 
				
			||||||
  event: ReviewSegment;
 | 
					  event: ReviewSegment;
 | 
				
			||||||
@ -24,10 +24,15 @@ export function AnimatedEventCard({ event }: AnimatedEventCardProps) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  // preview
 | 
					  // preview
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const { data: previews } = useSWR<Preview[]>(
 | 
					  const previews = useCameraPreviews(
 | 
				
			||||||
    currentHour
 | 
					    {
 | 
				
			||||||
      ? null
 | 
					      after: Math.round(event.start_time),
 | 
				
			||||||
      : `/preview/${event.camera}/start/${Math.round(event.start_time)}/end/${Math.round(event.end_time || event.start_time + 20)}`,
 | 
					      before: Math.round(event.end_time || event.start_time + 20),
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      camera: event.camera,
 | 
				
			||||||
 | 
					      fetchPreviews: !currentHour,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // interaction
 | 
					  // interaction
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										57
									
								
								web/src/hooks/use-camera-previews.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								web/src/hooks/use-camera-previews.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,57 @@
 | 
				
			|||||||
 | 
					import { Preview } from "@/types/preview";
 | 
				
			||||||
 | 
					import { TimeRange } from "@/types/timeline";
 | 
				
			||||||
 | 
					import { useEffect, useState } from "react";
 | 
				
			||||||
 | 
					import useSWR from "swr";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type OptionalCameraPreviewProps = {
 | 
				
			||||||
 | 
					  camera?: string;
 | 
				
			||||||
 | 
					  autoRefresh?: boolean;
 | 
				
			||||||
 | 
					  fetchPreviews?: boolean;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function useCameraPreviews(
 | 
				
			||||||
 | 
					  initialTimeRange: TimeRange,
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    camera = "all",
 | 
				
			||||||
 | 
					    autoRefresh = true,
 | 
				
			||||||
 | 
					    fetchPreviews = true,
 | 
				
			||||||
 | 
					  }: OptionalCameraPreviewProps,
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					  const [timeRange, setTimeRange] = useState(initialTimeRange);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    setTimeRange(initialTimeRange);
 | 
				
			||||||
 | 
					  }, [initialTimeRange]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const { data: allPreviews } = useSWR<Preview[]>(
 | 
				
			||||||
 | 
					    fetchPreviews
 | 
				
			||||||
 | 
					      ? `preview/${camera}/start/${timeRange.after}/end/${timeRange.before}`
 | 
				
			||||||
 | 
					      : null,
 | 
				
			||||||
 | 
					    { revalidateOnFocus: false, revalidateOnReconnect: false },
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Set a timeout to update previews on the hour
 | 
				
			||||||
 | 
					  useEffect(() => {
 | 
				
			||||||
 | 
					    if (!autoRefresh || !fetchPreviews || !allPreviews) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const callback = () => {
 | 
				
			||||||
 | 
					      const nextPreviewStart = new Date(
 | 
				
			||||||
 | 
					        allPreviews[allPreviews.length - 1].end * 1000,
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					      nextPreviewStart.setHours(nextPreviewStart.getHours() + 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (Date.now() > nextPreviewStart.getTime()) {
 | 
				
			||||||
 | 
					        setTimeRange({ after: timeRange.after, before: Date.now() / 1000 });
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    document.addEventListener("focusin", callback);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return () => {
 | 
				
			||||||
 | 
					      document.removeEventListener("focusin", callback);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }, [allPreviews, autoRefresh, fetchPreviews, timeRange]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return allPreviews;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,9 +1,9 @@
 | 
				
			|||||||
import ActivityIndicator from "@/components/indicators/activity-indicator";
 | 
					import ActivityIndicator from "@/components/indicators/activity-indicator";
 | 
				
			||||||
import useApiFilter from "@/hooks/use-api-filter";
 | 
					import useApiFilter from "@/hooks/use-api-filter";
 | 
				
			||||||
 | 
					import { useCameraPreviews } from "@/hooks/use-camera-previews";
 | 
				
			||||||
import { useTimezone } from "@/hooks/use-date-utils";
 | 
					import { useTimezone } from "@/hooks/use-date-utils";
 | 
				
			||||||
import { useOverlayState, useSearchEffect } from "@/hooks/use-overlay-state";
 | 
					import { useOverlayState, useSearchEffect } from "@/hooks/use-overlay-state";
 | 
				
			||||||
import { FrigateConfig } from "@/types/frigateConfig";
 | 
					import { FrigateConfig } from "@/types/frigateConfig";
 | 
				
			||||||
import { Preview } from "@/types/preview";
 | 
					 | 
				
			||||||
import { RecordingStartingPoint } from "@/types/record";
 | 
					import { RecordingStartingPoint } from "@/types/record";
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  ReviewFilter,
 | 
					  ReviewFilter,
 | 
				
			||||||
@ -161,7 +161,6 @@ export default function Events() {
 | 
				
			|||||||
  }, [updateSummary]);
 | 
					  }, [updateSummary]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // preview videos
 | 
					  // preview videos
 | 
				
			||||||
  const [previewKey, setPreviewKey] = useState(0);
 | 
					 | 
				
			||||||
  const previewTimes = useMemo(() => {
 | 
					  const previewTimes = useMemo(() => {
 | 
				
			||||||
    if (!reviews || reviews.length == 0) {
 | 
					    if (!reviews || reviews.length == 0) {
 | 
				
			||||||
      return undefined;
 | 
					      return undefined;
 | 
				
			||||||
@ -170,50 +169,22 @@ export default function Events() {
 | 
				
			|||||||
    const startDate = new Date();
 | 
					    const startDate = new Date();
 | 
				
			||||||
    startDate.setMinutes(0, 0, 0);
 | 
					    startDate.setMinutes(0, 0, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let endDate;
 | 
					    const endDate = new Date(reviews.at(-1)?.end_time || 0);
 | 
				
			||||||
    if (previewKey == 0) {
 | 
					    endDate.setHours(0, 0, 0, 0);
 | 
				
			||||||
      endDate = new Date(reviews.at(-1)?.end_time || 0);
 | 
					 | 
				
			||||||
      endDate.setHours(0, 0, 0, 0);
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      endDate = new Date();
 | 
					 | 
				
			||||||
      endDate.setMilliseconds(0);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
      start: startDate.getTime() / 1000,
 | 
					      after: startDate.getTime() / 1000,
 | 
				
			||||||
      end: endDate.getTime() / 1000,
 | 
					      before: endDate.getTime() / 1000,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
  }, [reviews, previewKey]);
 | 
					  }, [reviews]);
 | 
				
			||||||
  const { data: allPreviews } = useSWR<Preview[]>(
 | 
					
 | 
				
			||||||
    previewTimes
 | 
					  const allPreviews = useCameraPreviews(
 | 
				
			||||||
      ? `preview/all/start/${previewTimes.start}/end/${previewTimes.end}`
 | 
					    previewTimes ?? { after: 0, before: 0 },
 | 
				
			||||||
      : null,
 | 
					    {
 | 
				
			||||||
    { revalidateOnFocus: false, revalidateOnReconnect: false },
 | 
					      fetchPreviews: previewTimes != undefined,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Set a timeout to update previews on the hour
 | 
					 | 
				
			||||||
  useEffect(() => {
 | 
					 | 
				
			||||||
    if (!allPreviews || allPreviews.length == 0) {
 | 
					 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const callback = () => {
 | 
					 | 
				
			||||||
      const nextPreviewStart = new Date(
 | 
					 | 
				
			||||||
        allPreviews[allPreviews.length - 1].end * 1000,
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
      nextPreviewStart.setHours(nextPreviewStart.getHours() + 1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      if (Date.now() > nextPreviewStart.getTime()) {
 | 
					 | 
				
			||||||
        setPreviewKey(10 * Math.random());
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    document.addEventListener("focusin", callback);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return () => {
 | 
					 | 
				
			||||||
      document.removeEventListener("focusin", callback);
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
  }, [allPreviews]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // review status
 | 
					  // review status
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const markAllItemsAsReviewed = useCallback(
 | 
					  const markAllItemsAsReviewed = useCallback(
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user