diff --git a/web/src/components/overlay/ObjectTrackOverlay.tsx b/web/src/components/overlay/ObjectTrackOverlay.tsx index 092199dc9..17526bb09 100644 --- a/web/src/components/overlay/ObjectTrackOverlay.tsx +++ b/web/src/components/overlay/ObjectTrackOverlay.tsx @@ -2,7 +2,7 @@ import { useMemo, useCallback } from "react"; import { ObjectLifecycleSequence, LifecycleClassType } from "@/types/timeline"; import { FrigateConfig } from "@/types/frigateConfig"; import useSWR from "swr"; -import { useActivityStream } from "@/contexts/ActivityStreamContext"; +import { useDetailStream } from "@/context/detail-stream-context"; import { Tooltip, TooltipContent, @@ -35,7 +35,7 @@ export default function ObjectTrackOverlay({ }: ObjectTrackOverlayProps) { const { t } = useTranslation("views/events"); const { data: config } = useSWR("config"); - const { annotationOffset } = useActivityStream(); + const { annotationOffset } = useDetailStream(); const effectiveCurrentTime = currentTime - annotationOffset / 1000; diff --git a/web/src/components/overlay/detail/AnnotationOffsetSlider.tsx b/web/src/components/overlay/detail/AnnotationOffsetSlider.tsx index f1ff4cded..03aad4d60 100644 --- a/web/src/components/overlay/detail/AnnotationOffsetSlider.tsx +++ b/web/src/components/overlay/detail/AnnotationOffsetSlider.tsx @@ -1,7 +1,7 @@ import { useCallback, useState } from "react"; import { Slider } from "@/components/ui/slider"; import { Button } from "@/components/ui/button"; -import { useActivityStream } from "@/contexts/ActivityStreamContext"; +import { useDetailStream } from "@/context/detail-stream-context"; import axios from "axios"; import { useSWRConfig } from "swr"; import { toast } from "sonner"; @@ -12,7 +12,7 @@ type Props = { }; export default function AnnotationOffsetSlider({ className }: Props) { - const { annotationOffset, setAnnotationOffset, camera } = useActivityStream(); + const { annotationOffset, setAnnotationOffset, camera } = useDetailStream(); const { mutate } = useSWRConfig(); const { t } = useTranslation(["views/explore"]); const [isSaving, setIsSaving] = useState(false); diff --git a/web/src/components/player/HlsVideoPlayer.tsx b/web/src/components/player/HlsVideoPlayer.tsx index 78347220a..881e702ff 100644 --- a/web/src/components/player/HlsVideoPlayer.tsx +++ b/web/src/components/player/HlsVideoPlayer.tsx @@ -20,7 +20,7 @@ import { cn } from "@/lib/utils"; import { ASPECT_VERTICAL_LAYOUT, RecordingPlayerError } from "@/types/record"; import { useTranslation } from "react-i18next"; import ObjectTrackOverlay from "@/components/overlay/ObjectTrackOverlay"; -import { useActivityStream } from "@/contexts/ActivityStreamContext"; +import { useDetailStream } from "@/context/detail-stream-context"; // Android native hls does not seek correctly const USE_NATIVE_HLS = !isAndroid; @@ -82,8 +82,8 @@ export default function HlsVideoPlayer({ selectedObjectTimeline, currentTime, camera, - isActivityMode, - } = useActivityStream(); + isDetailMode, + } = useDetailStream(); // playback @@ -313,7 +313,7 @@ export default function HlsVideoPlayer({ height: isMobile ? "100%" : undefined, }} > - {isActivityMode && + {isDetailMode && selectedObjectId && camera && currentTime && diff --git a/web/src/components/timeline/ActivityStream.tsx b/web/src/components/timeline/DetailStream.tsx similarity index 98% rename from web/src/components/timeline/ActivityStream.tsx rename to web/src/components/timeline/DetailStream.tsx index cf7eda3cc..470ccf910 100644 --- a/web/src/components/timeline/ActivityStream.tsx +++ b/web/src/components/timeline/DetailStream.tsx @@ -2,7 +2,7 @@ import { useEffect, useRef, useState } from "react"; import { ObjectLifecycleSequence } from "@/types/timeline"; import { LifecycleIcon } from "@/components/overlay/detail/ObjectLifecycle"; import { getLifecycleItemDescription } from "@/utils/lifecycleUtil"; -import { useActivityStream } from "@/contexts/ActivityStreamContext"; +import { useDetailStream } from "@/context/detail-stream-context"; import scrollIntoView from "scroll-into-view-if-needed"; import useUserInteraction from "@/hooks/use-user-interaction"; import { formatUnixTimestampToDateTime } from "@/utils/dateUtil"; @@ -25,20 +25,20 @@ import EventMenu from "@/components/timeline/EventMenu"; import { FrigatePlusDialog } from "@/components/overlay/dialog/FrigatePlusDialog"; import { cn } from "@/lib/utils"; -type ActivityStreamProps = { +type DetailStreamProps = { reviewItems?: ReviewSegment[]; currentTime: number; onSeek: (timestamp: number) => void; }; -export default function ActivityStream({ +export default function DetailStream({ reviewItems, currentTime, onSeek, -}: ActivityStreamProps) { +}: DetailStreamProps) { const { data: config } = useSWR("config"); const { t } = useTranslation("views/events"); - const { annotationOffset } = useActivityStream(); + const { annotationOffset } = useDetailStream(); const scrollRef = useRef(null); const [activeReviewId, setActiveReviewId] = useState( @@ -330,7 +330,7 @@ function EventCollapsible({ const { t } = useTranslation("views/events"); const { data: config } = useSWR("config"); - const { selectedObjectId, setSelectedObjectId } = useActivityStream(); + const { selectedObjectId, setSelectedObjectId } = useDetailStream(); const formattedStart = config ? formatUnixTimestampToDateTime(event.start_time ?? 0, { diff --git a/web/src/contexts/ActivityStreamContext.tsx b/web/src/context/detail-stream-context.tsx similarity index 71% rename from web/src/contexts/ActivityStreamContext.tsx rename to web/src/context/detail-stream-context.tsx index 910211229..8558da848 100644 --- a/web/src/contexts/ActivityStreamContext.tsx +++ b/web/src/context/detail-stream-context.tsx @@ -3,7 +3,7 @@ import { FrigateConfig } from "@/types/frigateConfig"; import useSWR from "swr"; import { ObjectLifecycleSequence } from "@/types/timeline"; -interface ActivityStreamContextType { +interface DetailStreamContextType { selectedObjectId: string | undefined; selectedObjectTimeline?: ObjectLifecycleSequence[]; currentTime: number; @@ -11,26 +11,26 @@ interface ActivityStreamContextType { annotationOffset: number; // milliseconds setAnnotationOffset: (ms: number) => void; setSelectedObjectId: (id: string | undefined) => void; - isActivityMode: boolean; + isDetailMode: boolean; } -const ActivityStreamContext = createContext< - ActivityStreamContextType | undefined ->(undefined); +const DetailStreamContext = createContext( + undefined, +); -interface ActivityStreamProviderProps { +interface DetailStreamProviderProps { children: React.ReactNode; - isActivityMode: boolean; + isDetailMode: boolean; currentTime: number; camera: string; } -export function ActivityStreamProvider({ +export function DetailStreamProvider({ children, - isActivityMode, + isDetailMode, currentTime, camera, -}: ActivityStreamProviderProps) { +}: DetailStreamProviderProps) { const [selectedObjectId, setSelectedObjectId] = useState< string | undefined >(); @@ -52,7 +52,7 @@ export function ActivityStreamProvider({ setAnnotationOffset(cfgOffset); }, [config, camera]); - const value: ActivityStreamContextType = { + const value: DetailStreamContextType = { selectedObjectId, selectedObjectTimeline, currentTime, @@ -60,22 +60,22 @@ export function ActivityStreamProvider({ annotationOffset, setAnnotationOffset, setSelectedObjectId, - isActivityMode, + isDetailMode, }; return ( - + {children} - + ); } // eslint-disable-next-line react-refresh/only-export-components -export function useActivityStream() { - const context = useContext(ActivityStreamContext); +export function useDetailStream() { + const context = useContext(DetailStreamContext); if (context === undefined) { throw new Error( - "useActivityStream must be used within an ActivityStreamProvider", + "useDetailStream must be used within an DetailStreamProvider", ); } return context; diff --git a/web/src/types/timeline.ts b/web/src/types/timeline.ts index aa6762111..3782f8ae5 100644 --- a/web/src/types/timeline.ts +++ b/web/src/types/timeline.ts @@ -29,7 +29,7 @@ export type ObjectLifecycleSequence = { export type TimeRange = { before: number; after: number }; -export type TimelineType = "timeline" | "events" | "activity"; +export type TimelineType = "timeline" | "events" | "detail"; export type TimelineScrubMode = "auto" | "drag" | "hover" | "compat"; diff --git a/web/src/views/recording/RecordingView.tsx b/web/src/views/recording/RecordingView.tsx index 13ecccac2..3176dfff5 100644 --- a/web/src/views/recording/RecordingView.tsx +++ b/web/src/views/recording/RecordingView.tsx @@ -7,7 +7,7 @@ import PreviewPlayer, { import { DynamicVideoController } from "@/components/player/dynamic/DynamicVideoController"; import DynamicVideoPlayer from "@/components/player/dynamic/DynamicVideoPlayer"; import MotionReviewTimeline from "@/components/timeline/MotionReviewTimeline"; -import ActivityStream from "@/components/timeline/ActivityStream"; +import DetailStream from "@/components/timeline/DetailStream"; import { Button } from "@/components/ui/button"; import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"; import { useOverlayState } from "@/hooks/use-overlay-state"; @@ -67,7 +67,7 @@ import { } from "@/components/ui/tooltip"; import { CameraNameLabel } from "@/components/camera/CameraNameLabel"; import { useAllowedCameras } from "@/hooks/use-allowed-cameras"; -import { ActivityStreamProvider } from "@/contexts/ActivityStreamContext"; +import { DetailStreamProvider } from "@/context/detail-stream-context"; import { GenAISummaryDialog } from "@/components/overlay/chip/GenAISummaryChip"; const DATA_REFRESH_TIME = 600000; // 10 minutes @@ -524,8 +524,8 @@ export function RecordingView({ ); return ( - @@ -644,11 +644,11 @@ export function RecordingView({
{t("events.label")}
-
Activity
+
Detail
) : ( @@ -688,7 +688,7 @@ export function RecordingView({ className={cn( "flex flex-1 flex-wrap", isDesktop - ? timelineType === "activity" + ? timelineType === "detail" ? "w-full" : "w-[80%]" : "", @@ -697,7 +697,7 @@ export function RecordingView({
{isDesktop && effectiveCameras.length > 1 && - timelineType !== "activity" && ( + timelineType !== "detail" && (
-
+ ); } @@ -967,8 +967,8 @@ function Timeline({ className={cn( "relative", isDesktop - ? `${timelineType == "timeline" ? "w-[100px]" : timelineType == "activity" ? "w-[30%]" : "w-60"} no-scrollbar overflow-y-auto` - : `overflow-hidden portrait:flex-grow ${timelineType == "timeline" ? "landscape:w-[100px]" : timelineType == "activity" ? "flex-1" : "landscape:w-[175px]"} ` + ? `${timelineType == "timeline" ? "w-[100px]" : timelineType == "detail" ? "w-[30%]" : "w-60"} no-scrollbar overflow-y-auto` + : `overflow-hidden portrait:flex-grow ${timelineType == "timeline" ? "landscape:w-[100px]" : timelineType == "detail" ? "flex-1" : "landscape:w-[175px]"} ` } relative`} > {isMobile && ( @@ -1004,8 +1004,8 @@ function Timeline({ ) : ( ) - ) : timelineType == "activity" ? ( - manuallySetCurrentTime(timestamp, true)} reviewItems={mainCameraReviewItems}