UI Improvements and Tweaks (#13689)

* Improve image loading by not loading when off screen

* Add share menu to export

* Add share button and tidy up review detail lists

* Fix missing key

* Use query args for review filter

* Add object lifecycle to explore dialog

* Adjust sizing

* Simplify share button

* Always show snapshot but hide buttons for frigate+ if not applicable

* Handle case when user switches to element missing the previously selected tab

* Handle cases where share is not available

* Fix logic
This commit is contained in:
Nicolas Mowen
2024-09-12 08:46:29 -06:00
committed by GitHub
parent b4acf4f341
commit d84e3cacca
15 changed files with 172 additions and 70 deletions

View File

@@ -13,7 +13,7 @@ import {
import { Button } from "@/components/ui/button";
import { ObjectLifecycleSequence } from "@/types/timeline";
import Heading from "@/components/ui/heading";
import { ReviewDetailPaneType, ReviewSegment } from "@/types/review";
import { ReviewDetailPaneType } from "@/types/review";
import { FrigateConfig } from "@/types/frigateConfig";
import { formatUnixTimestampToDateTime } from "@/utils/dateUtil";
import { getIconForLabel } from "@/utils/iconUtil";
@@ -47,14 +47,16 @@ import { AnnotationSettingsPane } from "./AnnotationSettingsPane";
import { TooltipPortal } from "@radix-ui/react-tooltip";
type ObjectLifecycleProps = {
review: ReviewSegment;
className?: string;
event: Event;
fullscreen?: boolean;
setPane: React.Dispatch<React.SetStateAction<ReviewDetailPaneType>>;
};
export default function ObjectLifecycle({
review,
className,
event,
fullscreen = false,
setPane,
}: ObjectLifecycleProps) {
const { data: eventSequence } = useSWR<ObjectLifecycleSequence[]>([
@@ -78,13 +80,13 @@ export default function ObjectLifecycle({
const getZoneColor = useCallback(
(zoneName: string) => {
const zoneColor =
config?.cameras?.[review.camera]?.zones?.[zoneName]?.color;
config?.cameras?.[event.camera]?.zones?.[zoneName]?.color;
if (zoneColor) {
const reversed = [...zoneColor].reverse();
return reversed;
}
},
[config, review],
[config, event],
);
const getZonePolygon = useCallback(
@@ -93,7 +95,7 @@ export default function ObjectLifecycle({
return;
}
const zonePoints =
config?.cameras[review.camera].zones[zoneName].coordinates;
config?.cameras[event.camera].zones[zoneName].coordinates;
const imgElement = imgRef.current;
const imgRect = imgElement.getBoundingClientRect();
@@ -110,7 +112,7 @@ export default function ObjectLifecycle({
}, [] as number[])
.join(",");
},
[config, imgRef, review],
[config, imgRef, event],
);
const [boxStyle, setBoxStyle] = useState<React.CSSProperties | null>(null);
@@ -224,17 +226,19 @@ export default function ObjectLifecycle({
}
return (
<>
<div className={cn("flex items-center gap-2")}>
<Button
className="flex items-center gap-2.5 rounded-lg"
size="sm"
onClick={() => setPane("overview")}
>
<IoMdArrowRoundBack className="size-5 text-secondary-foreground" />
{isDesktop && <div className="text-primary">Back</div>}
</Button>
</div>
<div className={className}>
{!fullscreen && (
<div className={cn("flex items-center gap-2")}>
<Button
className="flex items-center gap-2.5 rounded-lg"
size="sm"
onClick={() => setPane("overview")}
>
<IoMdArrowRoundBack className="size-5 text-secondary-foreground" />
{isDesktop && <div className="text-primary">Back</div>}
</Button>
</div>
)}
<div className="relative mx-auto">
<ImageLoadingIndicator
@@ -347,7 +351,10 @@ export default function ObjectLifecycle({
)}
<div className="relative flex flex-col items-center justify-center">
<Carousel className="m-0 w-full" setApi={setMainApi}>
<Carousel
className={cn("m-0 w-full", fullscreen && isDesktop && "w-[75%]")}
setApi={setMainApi}
>
<CarouselContent>
{eventSequence.map((item, index) => (
<CarouselItem key={index}>
@@ -455,7 +462,7 @@ export default function ObjectLifecycle({
</CarouselContent>
</Carousel>
</div>
<div className="relative flex flex-col items-center justify-center">
<div className="relative mt-4 flex flex-col items-center justify-center">
<Carousel
opts={{
align: "center",
@@ -474,7 +481,10 @@ export default function ObjectLifecycle({
{eventSequence.map((item, index) => (
<CarouselItem
key={index}
className={cn("basis-1/4 cursor-pointer pl-1 md:basis-[10%]")}
className={cn(
"basis-1/4 cursor-pointer pl-1 md:basis-[10%]",
fullscreen && "md:basis-16",
)}
onClick={() => handleThumbnailClick(index)}
>
<div className="p-1">
@@ -513,7 +523,7 @@ export default function ObjectLifecycle({
<CarouselNext />
</Carousel>
</div>
</>
</div>
);
}