mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-04-12 18:17:36 +03:00
rename activity to detail
This commit is contained in:
parent
64aa709888
commit
ec5234f0ae
@ -2,7 +2,7 @@ import { useMemo, useCallback } from "react";
|
|||||||
import { ObjectLifecycleSequence, LifecycleClassType } from "@/types/timeline";
|
import { ObjectLifecycleSequence, LifecycleClassType } from "@/types/timeline";
|
||||||
import { FrigateConfig } from "@/types/frigateConfig";
|
import { FrigateConfig } from "@/types/frigateConfig";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import { useActivityStream } from "@/contexts/ActivityStreamContext";
|
import { useDetailStream } from "@/context/detail-stream-context";
|
||||||
import {
|
import {
|
||||||
Tooltip,
|
Tooltip,
|
||||||
TooltipContent,
|
TooltipContent,
|
||||||
@ -35,7 +35,7 @@ export default function ObjectTrackOverlay({
|
|||||||
}: ObjectTrackOverlayProps) {
|
}: ObjectTrackOverlayProps) {
|
||||||
const { t } = useTranslation("views/events");
|
const { t } = useTranslation("views/events");
|
||||||
const { data: config } = useSWR<FrigateConfig>("config");
|
const { data: config } = useSWR<FrigateConfig>("config");
|
||||||
const { annotationOffset } = useActivityStream();
|
const { annotationOffset } = useDetailStream();
|
||||||
|
|
||||||
const effectiveCurrentTime = currentTime - annotationOffset / 1000;
|
const effectiveCurrentTime = currentTime - annotationOffset / 1000;
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import { Slider } from "@/components/ui/slider";
|
import { Slider } from "@/components/ui/slider";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { useActivityStream } from "@/contexts/ActivityStreamContext";
|
import { useDetailStream } from "@/context/detail-stream-context";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { useSWRConfig } from "swr";
|
import { useSWRConfig } from "swr";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
@ -12,7 +12,7 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default function AnnotationOffsetSlider({ className }: Props) {
|
export default function AnnotationOffsetSlider({ className }: Props) {
|
||||||
const { annotationOffset, setAnnotationOffset, camera } = useActivityStream();
|
const { annotationOffset, setAnnotationOffset, camera } = useDetailStream();
|
||||||
const { mutate } = useSWRConfig();
|
const { mutate } = useSWRConfig();
|
||||||
const { t } = useTranslation(["views/explore"]);
|
const { t } = useTranslation(["views/explore"]);
|
||||||
const [isSaving, setIsSaving] = useState(false);
|
const [isSaving, setIsSaving] = useState(false);
|
||||||
|
|||||||
@ -20,7 +20,7 @@ import { cn } from "@/lib/utils";
|
|||||||
import { ASPECT_VERTICAL_LAYOUT, RecordingPlayerError } from "@/types/record";
|
import { ASPECT_VERTICAL_LAYOUT, RecordingPlayerError } from "@/types/record";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import ObjectTrackOverlay from "@/components/overlay/ObjectTrackOverlay";
|
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
|
// Android native hls does not seek correctly
|
||||||
const USE_NATIVE_HLS = !isAndroid;
|
const USE_NATIVE_HLS = !isAndroid;
|
||||||
@ -82,8 +82,8 @@ export default function HlsVideoPlayer({
|
|||||||
selectedObjectTimeline,
|
selectedObjectTimeline,
|
||||||
currentTime,
|
currentTime,
|
||||||
camera,
|
camera,
|
||||||
isActivityMode,
|
isDetailMode,
|
||||||
} = useActivityStream();
|
} = useDetailStream();
|
||||||
|
|
||||||
// playback
|
// playback
|
||||||
|
|
||||||
@ -313,7 +313,7 @@ export default function HlsVideoPlayer({
|
|||||||
height: isMobile ? "100%" : undefined,
|
height: isMobile ? "100%" : undefined,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isActivityMode &&
|
{isDetailMode &&
|
||||||
selectedObjectId &&
|
selectedObjectId &&
|
||||||
camera &&
|
camera &&
|
||||||
currentTime &&
|
currentTime &&
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { useEffect, useRef, useState } from "react";
|
|||||||
import { ObjectLifecycleSequence } from "@/types/timeline";
|
import { ObjectLifecycleSequence } from "@/types/timeline";
|
||||||
import { LifecycleIcon } from "@/components/overlay/detail/ObjectLifecycle";
|
import { LifecycleIcon } from "@/components/overlay/detail/ObjectLifecycle";
|
||||||
import { getLifecycleItemDescription } from "@/utils/lifecycleUtil";
|
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 scrollIntoView from "scroll-into-view-if-needed";
|
||||||
import useUserInteraction from "@/hooks/use-user-interaction";
|
import useUserInteraction from "@/hooks/use-user-interaction";
|
||||||
import { formatUnixTimestampToDateTime } from "@/utils/dateUtil";
|
import { formatUnixTimestampToDateTime } from "@/utils/dateUtil";
|
||||||
@ -25,20 +25,20 @@ import EventMenu from "@/components/timeline/EventMenu";
|
|||||||
import { FrigatePlusDialog } from "@/components/overlay/dialog/FrigatePlusDialog";
|
import { FrigatePlusDialog } from "@/components/overlay/dialog/FrigatePlusDialog";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
type ActivityStreamProps = {
|
type DetailStreamProps = {
|
||||||
reviewItems?: ReviewSegment[];
|
reviewItems?: ReviewSegment[];
|
||||||
currentTime: number;
|
currentTime: number;
|
||||||
onSeek: (timestamp: number) => void;
|
onSeek: (timestamp: number) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function ActivityStream({
|
export default function DetailStream({
|
||||||
reviewItems,
|
reviewItems,
|
||||||
currentTime,
|
currentTime,
|
||||||
onSeek,
|
onSeek,
|
||||||
}: ActivityStreamProps) {
|
}: DetailStreamProps) {
|
||||||
const { data: config } = useSWR<FrigateConfig>("config");
|
const { data: config } = useSWR<FrigateConfig>("config");
|
||||||
const { t } = useTranslation("views/events");
|
const { t } = useTranslation("views/events");
|
||||||
const { annotationOffset } = useActivityStream();
|
const { annotationOffset } = useDetailStream();
|
||||||
const scrollRef = useRef<HTMLDivElement>(null);
|
const scrollRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const [activeReviewId, setActiveReviewId] = useState<string | undefined>(
|
const [activeReviewId, setActiveReviewId] = useState<string | undefined>(
|
||||||
@ -330,7 +330,7 @@ function EventCollapsible({
|
|||||||
const { t } = useTranslation("views/events");
|
const { t } = useTranslation("views/events");
|
||||||
const { data: config } = useSWR<FrigateConfig>("config");
|
const { data: config } = useSWR<FrigateConfig>("config");
|
||||||
|
|
||||||
const { selectedObjectId, setSelectedObjectId } = useActivityStream();
|
const { selectedObjectId, setSelectedObjectId } = useDetailStream();
|
||||||
|
|
||||||
const formattedStart = config
|
const formattedStart = config
|
||||||
? formatUnixTimestampToDateTime(event.start_time ?? 0, {
|
? formatUnixTimestampToDateTime(event.start_time ?? 0, {
|
||||||
@ -3,7 +3,7 @@ import { FrigateConfig } from "@/types/frigateConfig";
|
|||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import { ObjectLifecycleSequence } from "@/types/timeline";
|
import { ObjectLifecycleSequence } from "@/types/timeline";
|
||||||
|
|
||||||
interface ActivityStreamContextType {
|
interface DetailStreamContextType {
|
||||||
selectedObjectId: string | undefined;
|
selectedObjectId: string | undefined;
|
||||||
selectedObjectTimeline?: ObjectLifecycleSequence[];
|
selectedObjectTimeline?: ObjectLifecycleSequence[];
|
||||||
currentTime: number;
|
currentTime: number;
|
||||||
@ -11,26 +11,26 @@ interface ActivityStreamContextType {
|
|||||||
annotationOffset: number; // milliseconds
|
annotationOffset: number; // milliseconds
|
||||||
setAnnotationOffset: (ms: number) => void;
|
setAnnotationOffset: (ms: number) => void;
|
||||||
setSelectedObjectId: (id: string | undefined) => void;
|
setSelectedObjectId: (id: string | undefined) => void;
|
||||||
isActivityMode: boolean;
|
isDetailMode: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ActivityStreamContext = createContext<
|
const DetailStreamContext = createContext<DetailStreamContextType | undefined>(
|
||||||
ActivityStreamContextType | undefined
|
undefined,
|
||||||
>(undefined);
|
);
|
||||||
|
|
||||||
interface ActivityStreamProviderProps {
|
interface DetailStreamProviderProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
isActivityMode: boolean;
|
isDetailMode: boolean;
|
||||||
currentTime: number;
|
currentTime: number;
|
||||||
camera: string;
|
camera: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ActivityStreamProvider({
|
export function DetailStreamProvider({
|
||||||
children,
|
children,
|
||||||
isActivityMode,
|
isDetailMode,
|
||||||
currentTime,
|
currentTime,
|
||||||
camera,
|
camera,
|
||||||
}: ActivityStreamProviderProps) {
|
}: DetailStreamProviderProps) {
|
||||||
const [selectedObjectId, setSelectedObjectId] = useState<
|
const [selectedObjectId, setSelectedObjectId] = useState<
|
||||||
string | undefined
|
string | undefined
|
||||||
>();
|
>();
|
||||||
@ -52,7 +52,7 @@ export function ActivityStreamProvider({
|
|||||||
setAnnotationOffset(cfgOffset);
|
setAnnotationOffset(cfgOffset);
|
||||||
}, [config, camera]);
|
}, [config, camera]);
|
||||||
|
|
||||||
const value: ActivityStreamContextType = {
|
const value: DetailStreamContextType = {
|
||||||
selectedObjectId,
|
selectedObjectId,
|
||||||
selectedObjectTimeline,
|
selectedObjectTimeline,
|
||||||
currentTime,
|
currentTime,
|
||||||
@ -60,22 +60,22 @@ export function ActivityStreamProvider({
|
|||||||
annotationOffset,
|
annotationOffset,
|
||||||
setAnnotationOffset,
|
setAnnotationOffset,
|
||||||
setSelectedObjectId,
|
setSelectedObjectId,
|
||||||
isActivityMode,
|
isDetailMode,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ActivityStreamContext.Provider value={value}>
|
<DetailStreamContext.Provider value={value}>
|
||||||
{children}
|
{children}
|
||||||
</ActivityStreamContext.Provider>
|
</DetailStreamContext.Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line react-refresh/only-export-components
|
// eslint-disable-next-line react-refresh/only-export-components
|
||||||
export function useActivityStream() {
|
export function useDetailStream() {
|
||||||
const context = useContext(ActivityStreamContext);
|
const context = useContext(DetailStreamContext);
|
||||||
if (context === undefined) {
|
if (context === undefined) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"useActivityStream must be used within an ActivityStreamProvider",
|
"useDetailStream must be used within an DetailStreamProvider",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return context;
|
return context;
|
||||||
@ -29,7 +29,7 @@ export type ObjectLifecycleSequence = {
|
|||||||
|
|
||||||
export type TimeRange = { before: number; after: number };
|
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";
|
export type TimelineScrubMode = "auto" | "drag" | "hover" | "compat";
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import PreviewPlayer, {
|
|||||||
import { DynamicVideoController } from "@/components/player/dynamic/DynamicVideoController";
|
import { DynamicVideoController } from "@/components/player/dynamic/DynamicVideoController";
|
||||||
import DynamicVideoPlayer from "@/components/player/dynamic/DynamicVideoPlayer";
|
import DynamicVideoPlayer from "@/components/player/dynamic/DynamicVideoPlayer";
|
||||||
import MotionReviewTimeline from "@/components/timeline/MotionReviewTimeline";
|
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 { Button } from "@/components/ui/button";
|
||||||
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
|
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
|
||||||
import { useOverlayState } from "@/hooks/use-overlay-state";
|
import { useOverlayState } from "@/hooks/use-overlay-state";
|
||||||
@ -67,7 +67,7 @@ import {
|
|||||||
} from "@/components/ui/tooltip";
|
} from "@/components/ui/tooltip";
|
||||||
import { CameraNameLabel } from "@/components/camera/CameraNameLabel";
|
import { CameraNameLabel } from "@/components/camera/CameraNameLabel";
|
||||||
import { useAllowedCameras } from "@/hooks/use-allowed-cameras";
|
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";
|
import { GenAISummaryDialog } from "@/components/overlay/chip/GenAISummaryChip";
|
||||||
|
|
||||||
const DATA_REFRESH_TIME = 600000; // 10 minutes
|
const DATA_REFRESH_TIME = 600000; // 10 minutes
|
||||||
@ -524,8 +524,8 @@ export function RecordingView({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ActivityStreamProvider
|
<DetailStreamProvider
|
||||||
isActivityMode={timelineType === "activity"}
|
isDetailMode={timelineType === "detail"}
|
||||||
currentTime={currentTime}
|
currentTime={currentTime}
|
||||||
camera={mainCamera}
|
camera={mainCamera}
|
||||||
>
|
>
|
||||||
@ -644,11 +644,11 @@ export function RecordingView({
|
|||||||
<div className="">{t("events.label")}</div>
|
<div className="">{t("events.label")}</div>
|
||||||
</ToggleGroupItem>
|
</ToggleGroupItem>
|
||||||
<ToggleGroupItem
|
<ToggleGroupItem
|
||||||
className={`${timelineType == "activity" ? "" : "text-muted-foreground"}`}
|
className={`${timelineType == "detail" ? "" : "text-muted-foreground"}`}
|
||||||
value="activity"
|
value="detail"
|
||||||
aria-label="Activity Stream"
|
aria-label="Detail Stream"
|
||||||
>
|
>
|
||||||
<div className="">Activity</div>
|
<div className="">Detail</div>
|
||||||
</ToggleGroupItem>
|
</ToggleGroupItem>
|
||||||
</ToggleGroup>
|
</ToggleGroup>
|
||||||
) : (
|
) : (
|
||||||
@ -688,7 +688,7 @@ export function RecordingView({
|
|||||||
className={cn(
|
className={cn(
|
||||||
"flex flex-1 flex-wrap",
|
"flex flex-1 flex-wrap",
|
||||||
isDesktop
|
isDesktop
|
||||||
? timelineType === "activity"
|
? timelineType === "detail"
|
||||||
? "w-full"
|
? "w-full"
|
||||||
: "w-[80%]"
|
: "w-[80%]"
|
||||||
: "",
|
: "",
|
||||||
@ -697,7 +697,7 @@ export function RecordingView({
|
|||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex size-full items-center",
|
"flex size-full items-center",
|
||||||
timelineType === "activity"
|
timelineType === "detail"
|
||||||
? "flex-col"
|
? "flex-col"
|
||||||
: mainCameraAspect == "tall"
|
: mainCameraAspect == "tall"
|
||||||
? "flex-row justify-evenly"
|
? "flex-row justify-evenly"
|
||||||
@ -776,7 +776,7 @@ export function RecordingView({
|
|||||||
</div>
|
</div>
|
||||||
{isDesktop &&
|
{isDesktop &&
|
||||||
effectiveCameras.length > 1 &&
|
effectiveCameras.length > 1 &&
|
||||||
timelineType !== "activity" && (
|
timelineType !== "detail" && (
|
||||||
<div
|
<div
|
||||||
ref={previewRowRef}
|
ref={previewRowRef}
|
||||||
className={cn(
|
className={cn(
|
||||||
@ -852,7 +852,7 @@ export function RecordingView({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ActivityStreamProvider>
|
</DetailStreamProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -967,8 +967,8 @@ function Timeline({
|
|||||||
className={cn(
|
className={cn(
|
||||||
"relative",
|
"relative",
|
||||||
isDesktop
|
isDesktop
|
||||||
? `${timelineType == "timeline" ? "w-[100px]" : timelineType == "activity" ? "w-[30%]" : "w-60"} no-scrollbar overflow-y-auto`
|
? `${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 == "activity" ? "flex-1" : "landscape:w-[175px]"} `
|
: `overflow-hidden portrait:flex-grow ${timelineType == "timeline" ? "landscape:w-[100px]" : timelineType == "detail" ? "flex-1" : "landscape:w-[175px]"} `
|
||||||
} relative`}
|
} relative`}
|
||||||
>
|
>
|
||||||
{isMobile && (
|
{isMobile && (
|
||||||
@ -1004,8 +1004,8 @@ function Timeline({
|
|||||||
) : (
|
) : (
|
||||||
<Skeleton className="size-full" />
|
<Skeleton className="size-full" />
|
||||||
)
|
)
|
||||||
) : timelineType == "activity" ? (
|
) : timelineType == "detail" ? (
|
||||||
<ActivityStream
|
<DetailStream
|
||||||
currentTime={currentTime}
|
currentTime={currentTime}
|
||||||
onSeek={(timestamp) => manuallySetCurrentTime(timestamp, true)}
|
onSeek={(timestamp) => manuallySetCurrentTime(timestamp, true)}
|
||||||
reviewItems={mainCameraReviewItems}
|
reviewItems={mainCameraReviewItems}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user