mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-03-07 02:18:07 +01:00
Review card refactor (#20813)
* Use the review card in event timeline popover * Show review title in review card
This commit is contained in:
@@ -38,6 +38,7 @@ import { Button, buttonVariants } from "../ui/button";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { LuCircle } from "react-icons/lu";
|
||||
import { MdAutoAwesome } from "react-icons/md";
|
||||
|
||||
type ReviewCardProps = {
|
||||
event: ReviewSegment;
|
||||
@@ -164,29 +165,33 @@ export default function ReviewCard({
|
||||
<div className="flex items-center justify-between">
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div className="flex items-center justify-evenly gap-1">
|
||||
<>
|
||||
<LuCircle
|
||||
className={cn(
|
||||
"size-2",
|
||||
event.severity == "alert"
|
||||
? "fill-severity_alert text-severity_alert"
|
||||
: "fill-severity_detection text-severity_detection",
|
||||
)}
|
||||
/>
|
||||
{event.data.objects.map((object) => {
|
||||
return getIconForLabel(
|
||||
object,
|
||||
"size-3 text-primary dark:text-white",
|
||||
);
|
||||
})}
|
||||
{event.data.audio.map((audio) => {
|
||||
return getIconForLabel(
|
||||
audio,
|
||||
"size-3 text-primary dark:text-white",
|
||||
);
|
||||
})}
|
||||
</>
|
||||
<div className="flex items-center gap-2">
|
||||
<LuCircle
|
||||
className={cn(
|
||||
"size-2",
|
||||
event.severity == "alert"
|
||||
? "fill-severity_alert text-severity_alert"
|
||||
: "fill-severity_detection text-severity_detection",
|
||||
)}
|
||||
/>
|
||||
<div className="flex items-center gap-1">
|
||||
{event.data.objects.map((object, idx) => (
|
||||
<div
|
||||
key={`${object}-${idx}`}
|
||||
className="rounded-full bg-muted-foreground p-1"
|
||||
>
|
||||
{getIconForLabel(object, "size-3 text-white")}
|
||||
</div>
|
||||
))}
|
||||
{event.data.audio.map((audio, idx) => (
|
||||
<div
|
||||
key={`${audio}-${idx}`}
|
||||
className="rounded-full bg-muted-foreground p-1"
|
||||
>
|
||||
{getIconForLabel(audio, "size-3 text-white")}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="font-extra-light text-xs">{formattedDate}</div>
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
@@ -213,6 +218,14 @@ export default function ReviewCard({
|
||||
dense
|
||||
/>
|
||||
</div>
|
||||
{event.data.metadata?.title && (
|
||||
<div className="flex items-center gap-1.5 rounded bg-secondary/50">
|
||||
<MdAutoAwesome className="size-3 shrink-0 text-primary" />
|
||||
<span className="truncate text-xs text-primary">
|
||||
{event.data.metadata.title}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
LuChevronRight,
|
||||
LuSettings,
|
||||
} from "react-icons/lu";
|
||||
import { MdAutoAwesome } from "react-icons/md";
|
||||
import { getTranslatedLabel } from "@/utils/i18n";
|
||||
import EventMenu from "@/components/timeline/EventMenu";
|
||||
import { FrigatePlusDialog } from "@/components/overlay/dialog/FrigatePlusDialog";
|
||||
@@ -410,8 +411,9 @@ function ReviewGroup({
|
||||
</div>
|
||||
<div className="flex flex-col gap-0.5">
|
||||
{review.data.metadata?.title && (
|
||||
<div className="mb-1 text-sm text-primary-variant">
|
||||
{review.data.metadata.title}
|
||||
<div className="mb-1 flex items-center gap-1 text-sm text-primary-variant">
|
||||
<MdAutoAwesome className="size-3 shrink-0" />
|
||||
<span className="truncate">{review.data.metadata.title}</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-row items-center gap-1.5">
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { useApiHost } from "@/api";
|
||||
import { useTimelineUtils } from "@/hooks/use-timeline-utils";
|
||||
import { useEventSegmentUtils } from "@/hooks/use-event-segment-utils";
|
||||
import { ReviewSegment, ReviewSeverity } from "@/types/review";
|
||||
@@ -18,6 +17,7 @@ import { HoverCardPortal } from "@radix-ui/react-hover-card";
|
||||
import scrollIntoView from "scroll-into-view-if-needed";
|
||||
import { MinimapBounds, Tick, Timestamp } from "./segment-metadata";
|
||||
import useTapUtils from "@/hooks/use-tap-utils";
|
||||
import ReviewCard from "../card/ReviewCard";
|
||||
|
||||
type EventSegmentProps = {
|
||||
events: ReviewSegment[];
|
||||
@@ -54,7 +54,7 @@ export function EventSegment({
|
||||
displaySeverityType,
|
||||
shouldShowRoundedCorners,
|
||||
getEventStart,
|
||||
getEventThumbnail,
|
||||
getEvent,
|
||||
} = useEventSegmentUtils(segmentDuration, events, severityType);
|
||||
|
||||
const { alignStartDateToTimeline, alignEndDateToTimeline } = useTimelineUtils(
|
||||
@@ -87,13 +87,11 @@ export function EventSegment({
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [getEventStart, segmentTime]);
|
||||
|
||||
const apiHost = useApiHost();
|
||||
|
||||
const { handleTouchStart } = useTapUtils();
|
||||
|
||||
const eventThumbnail = useMemo(() => {
|
||||
return getEventThumbnail(segmentTime);
|
||||
}, [getEventThumbnail, segmentTime]);
|
||||
const segmentEvent = useMemo(() => {
|
||||
return getEvent(segmentTime);
|
||||
}, [getEvent, segmentTime]);
|
||||
|
||||
const timestamp = useMemo(() => new Date(segmentTime * 1000), [segmentTime]);
|
||||
const segmentKey = useMemo(
|
||||
@@ -252,10 +250,7 @@ export function EventSegment({
|
||||
className="w-[250px] rounded-lg p-2 md:rounded-2xl"
|
||||
side="left"
|
||||
>
|
||||
<img
|
||||
className="rounded-lg"
|
||||
src={`${apiHost}${eventThumbnail.replace("/media/frigate/", "")}`}
|
||||
/>
|
||||
{segmentEvent && <ReviewCard event={segmentEvent} />}
|
||||
</HoverCardContent>
|
||||
</HoverCardPortal>
|
||||
</HoverCard>
|
||||
|
||||
Reference in New Issue
Block a user