mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-14 15:15:22 +03:00
Add object lifecycle to explore dialog
This commit is contained in:
parent
d2b8d0007b
commit
3e9707d507
@ -13,7 +13,7 @@ import {
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { ObjectLifecycleSequence } from "@/types/timeline";
|
import { ObjectLifecycleSequence } from "@/types/timeline";
|
||||||
import Heading from "@/components/ui/heading";
|
import Heading from "@/components/ui/heading";
|
||||||
import { ReviewDetailPaneType, ReviewSegment } from "@/types/review";
|
import { ReviewDetailPaneType } from "@/types/review";
|
||||||
import { FrigateConfig } from "@/types/frigateConfig";
|
import { FrigateConfig } from "@/types/frigateConfig";
|
||||||
import { formatUnixTimestampToDateTime } from "@/utils/dateUtil";
|
import { formatUnixTimestampToDateTime } from "@/utils/dateUtil";
|
||||||
import { getIconForLabel } from "@/utils/iconUtil";
|
import { getIconForLabel } from "@/utils/iconUtil";
|
||||||
@ -47,14 +47,16 @@ import { AnnotationSettingsPane } from "./AnnotationSettingsPane";
|
|||||||
import { TooltipPortal } from "@radix-ui/react-tooltip";
|
import { TooltipPortal } from "@radix-ui/react-tooltip";
|
||||||
|
|
||||||
type ObjectLifecycleProps = {
|
type ObjectLifecycleProps = {
|
||||||
review: ReviewSegment;
|
className?: string;
|
||||||
event: Event;
|
event: Event;
|
||||||
|
showBack?: boolean;
|
||||||
setPane: React.Dispatch<React.SetStateAction<ReviewDetailPaneType>>;
|
setPane: React.Dispatch<React.SetStateAction<ReviewDetailPaneType>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function ObjectLifecycle({
|
export default function ObjectLifecycle({
|
||||||
review,
|
className,
|
||||||
event,
|
event,
|
||||||
|
showBack = true,
|
||||||
setPane,
|
setPane,
|
||||||
}: ObjectLifecycleProps) {
|
}: ObjectLifecycleProps) {
|
||||||
const { data: eventSequence } = useSWR<ObjectLifecycleSequence[]>([
|
const { data: eventSequence } = useSWR<ObjectLifecycleSequence[]>([
|
||||||
@ -78,13 +80,13 @@ export default function ObjectLifecycle({
|
|||||||
const getZoneColor = useCallback(
|
const getZoneColor = useCallback(
|
||||||
(zoneName: string) => {
|
(zoneName: string) => {
|
||||||
const zoneColor =
|
const zoneColor =
|
||||||
config?.cameras?.[review.camera]?.zones?.[zoneName]?.color;
|
config?.cameras?.[event.camera]?.zones?.[zoneName]?.color;
|
||||||
if (zoneColor) {
|
if (zoneColor) {
|
||||||
const reversed = [...zoneColor].reverse();
|
const reversed = [...zoneColor].reverse();
|
||||||
return reversed;
|
return reversed;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[config, review],
|
[config, event],
|
||||||
);
|
);
|
||||||
|
|
||||||
const getZonePolygon = useCallback(
|
const getZonePolygon = useCallback(
|
||||||
@ -93,7 +95,7 @@ export default function ObjectLifecycle({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const zonePoints =
|
const zonePoints =
|
||||||
config?.cameras[review.camera].zones[zoneName].coordinates;
|
config?.cameras[event.camera].zones[zoneName].coordinates;
|
||||||
const imgElement = imgRef.current;
|
const imgElement = imgRef.current;
|
||||||
const imgRect = imgElement.getBoundingClientRect();
|
const imgRect = imgElement.getBoundingClientRect();
|
||||||
|
|
||||||
@ -110,7 +112,7 @@ export default function ObjectLifecycle({
|
|||||||
}, [] as number[])
|
}, [] as number[])
|
||||||
.join(",");
|
.join(",");
|
||||||
},
|
},
|
||||||
[config, imgRef, review],
|
[config, imgRef, event],
|
||||||
);
|
);
|
||||||
|
|
||||||
const [boxStyle, setBoxStyle] = useState<React.CSSProperties | null>(null);
|
const [boxStyle, setBoxStyle] = useState<React.CSSProperties | null>(null);
|
||||||
@ -224,7 +226,8 @@ export default function ObjectLifecycle({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div className={className}>
|
||||||
|
{showBack && (
|
||||||
<div className={cn("flex items-center gap-2")}>
|
<div className={cn("flex items-center gap-2")}>
|
||||||
<Button
|
<Button
|
||||||
className="flex items-center gap-2.5 rounded-lg"
|
className="flex items-center gap-2.5 rounded-lg"
|
||||||
@ -235,6 +238,7 @@ export default function ObjectLifecycle({
|
|||||||
{isDesktop && <div className="text-primary">Back</div>}
|
{isDesktop && <div className="text-primary">Back</div>}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="relative mx-auto">
|
<div className="relative mx-auto">
|
||||||
<ImageLoadingIndicator
|
<ImageLoadingIndicator
|
||||||
@ -513,7 +517,7 @@ export default function ObjectLifecycle({
|
|||||||
<CarouselNext />
|
<CarouselNext />
|
||||||
</Carousel>
|
</Carousel>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -214,11 +214,7 @@ export default function ReviewDetailDialog({
|
|||||||
|
|
||||||
{pane == "details" && selectedEvent && (
|
{pane == "details" && selectedEvent && (
|
||||||
<div className="scrollbar-container overflow-x-none mt-0 flex size-full flex-col gap-2 overflow-y-auto overflow-x-hidden">
|
<div className="scrollbar-container overflow-x-none mt-0 flex size-full flex-col gap-2 overflow-y-auto overflow-x-hidden">
|
||||||
<ObjectLifecycle
|
<ObjectLifecycle event={selectedEvent} setPane={setPane} />
|
||||||
review={review}
|
|
||||||
event={selectedEvent}
|
|
||||||
setPane={setPane}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Content>
|
</Content>
|
||||||
|
|||||||
@ -36,8 +36,15 @@ import ActivityIndicator from "@/components/indicators/activity-indicator";
|
|||||||
import { ASPECT_VERTICAL_LAYOUT, ASPECT_WIDE_LAYOUT } from "@/types/record";
|
import { ASPECT_VERTICAL_LAYOUT, ASPECT_WIDE_LAYOUT } from "@/types/record";
|
||||||
import { FaRegListAlt, FaVideo } from "react-icons/fa";
|
import { FaRegListAlt, FaVideo } from "react-icons/fa";
|
||||||
import FrigatePlusIcon from "@/components/icons/FrigatePlusIcon";
|
import FrigatePlusIcon from "@/components/icons/FrigatePlusIcon";
|
||||||
|
import { FaRotate } from "react-icons/fa6";
|
||||||
|
import ObjectLifecycle from "./ObjectLifecycle";
|
||||||
|
|
||||||
const SEARCH_TABS = ["details", "frigate+", "video"] as const;
|
const SEARCH_TABS = [
|
||||||
|
"details",
|
||||||
|
"frigate+",
|
||||||
|
"video",
|
||||||
|
"object lifecycle",
|
||||||
|
] as const;
|
||||||
type SearchTab = (typeof SEARCH_TABS)[number];
|
type SearchTab = (typeof SEARCH_TABS)[number];
|
||||||
|
|
||||||
type SearchDetailDialogProps = {
|
type SearchDetailDialogProps = {
|
||||||
@ -104,7 +111,7 @@ export default function SearchDetailDialog({
|
|||||||
<Content
|
<Content
|
||||||
className={
|
className={
|
||||||
isDesktop
|
isDesktop
|
||||||
? "sm:max-w-xl md:max-w-3xl lg:max-w-4xl xl:max-w-7xl"
|
? "sm:max-w-xl md:max-w-xl lg:max-w-2xl xl:max-w-5xl"
|
||||||
: "max-h-[75dvh] overflow-hidden px-2 pb-4"
|
: "max-h-[75dvh] overflow-hidden px-2 pb-4"
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@ -138,6 +145,9 @@ export default function SearchDetailDialog({
|
|||||||
{item == "details" && <FaRegListAlt className="size-4" />}
|
{item == "details" && <FaRegListAlt className="size-4" />}
|
||||||
{item == "frigate+" && <FrigatePlusIcon className="size-4" />}
|
{item == "frigate+" && <FrigatePlusIcon className="size-4" />}
|
||||||
{item == "video" && <FaVideo className="size-4" />}
|
{item == "video" && <FaVideo className="size-4" />}
|
||||||
|
{item == "object lifecycle" && (
|
||||||
|
<FaRotate className="size-4" />
|
||||||
|
)}
|
||||||
<div className="capitalize">{item}</div>
|
<div className="capitalize">{item}</div>
|
||||||
</ToggleGroupItem>
|
</ToggleGroupItem>
|
||||||
))}
|
))}
|
||||||
@ -164,6 +174,14 @@ export default function SearchDetailDialog({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{page == "video" && <VideoTab search={search} config={config} />}
|
{page == "video" && <VideoTab search={search} config={config} />}
|
||||||
|
{page == "object lifecycle" && (
|
||||||
|
<ObjectLifecycle
|
||||||
|
className="w-full"
|
||||||
|
event={search as unknown as Event}
|
||||||
|
showBack={false}
|
||||||
|
setPane={() => {}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Content>
|
</Content>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user