-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Reset selected index on new searches * Remove right click for similarity search * Fix sub label icon * add card footer * Add Frigate+ dialog * Move buttons and menu to thumbnail footer * Add similarity search * Show object score * Implement download buttons * remove confidence score * conditionally show submenu items * Implement delete * fix icon color * Add object lifecycle button * fix score * delete confirmation * small tweaks * consistent icons --------- Co-authored-by: Nicolas Mowen <[email protected]>
- Loading branch information
1 parent
0eccb6a
commit 644069f
Showing
7 changed files
with
283 additions
and
96 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
import { useCallback, useState } from "react"; | ||
import TimeAgo from "../dynamic/TimeAgo"; | ||
import useSWR from "swr"; | ||
import { FrigateConfig } from "@/types/frigateConfig"; | ||
import { useFormattedTimestamp } from "@/hooks/use-date-utils"; | ||
import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip"; | ||
import ActivityIndicator from "../indicators/activity-indicator"; | ||
import { SearchResult } from "@/types/search"; | ||
import { | ||
DropdownMenu, | ||
DropdownMenuContent, | ||
DropdownMenuItem, | ||
DropdownMenuLabel, | ||
DropdownMenuSeparator, | ||
DropdownMenuTrigger, | ||
} from "@/components/ui/dropdown-menu"; | ||
import { | ||
AlertDialog, | ||
AlertDialogAction, | ||
AlertDialogCancel, | ||
AlertDialogContent, | ||
AlertDialogDescription, | ||
AlertDialogFooter, | ||
AlertDialogHeader, | ||
AlertDialogTitle, | ||
} from "../ui/alert-dialog"; | ||
import { LuCamera, LuDownload, LuMoreVertical, LuTrash2 } from "react-icons/lu"; | ||
import FrigatePlusIcon from "@/components/icons/FrigatePlusIcon"; | ||
import { FrigatePlusDialog } from "../overlay/dialog/FrigatePlusDialog"; | ||
import { Event } from "@/types/event"; | ||
import { FaArrowsRotate } from "react-icons/fa6"; | ||
import { baseUrl } from "@/api/baseUrl"; | ||
import axios from "axios"; | ||
import { toast } from "sonner"; | ||
import { MdImageSearch } from "react-icons/md"; | ||
|
||
type SearchThumbnailProps = { | ||
searchResult: SearchResult; | ||
findSimilar: () => void; | ||
refreshResults: () => void; | ||
showObjectLifecycle: () => void; | ||
}; | ||
|
||
export default function SearchThumbnailFooter({ | ||
searchResult, | ||
findSimilar, | ||
refreshResults, | ||
showObjectLifecycle, | ||
}: SearchThumbnailProps) { | ||
const { data: config } = useSWR<FrigateConfig>("config"); | ||
|
||
// interactions | ||
|
||
const [showFrigatePlus, setShowFrigatePlus] = useState(false); | ||
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); | ||
|
||
const handleDelete = useCallback(() => { | ||
axios | ||
.delete(`events/${searchResult.id}`) | ||
.then((resp) => { | ||
if (resp.status == 200) { | ||
toast.success("Tracked object deleted successfully.", { | ||
position: "top-center", | ||
}); | ||
refreshResults(); | ||
} | ||
}) | ||
.catch(() => { | ||
toast.error("Failed to delete tracked object.", { | ||
position: "top-center", | ||
}); | ||
}); | ||
}, [searchResult, refreshResults]); | ||
|
||
// date | ||
|
||
const formattedDate = useFormattedTimestamp( | ||
searchResult.start_time, | ||
config?.ui.time_format == "24hour" ? "%b %-d, %H:%M" : "%b %-d, %I:%M %p", | ||
config?.ui.timezone, | ||
); | ||
|
||
return ( | ||
<> | ||
<AlertDialog | ||
open={deleteDialogOpen} | ||
onOpenChange={() => setDeleteDialogOpen(!deleteDialogOpen)} | ||
> | ||
<AlertDialogContent> | ||
<AlertDialogHeader> | ||
<AlertDialogTitle>Confirm Delete</AlertDialogTitle> | ||
</AlertDialogHeader> | ||
<AlertDialogDescription> | ||
Are you sure you want to delete this tracked object? | ||
</AlertDialogDescription> | ||
<AlertDialogFooter> | ||
<AlertDialogCancel>Cancel</AlertDialogCancel> | ||
<AlertDialogAction | ||
className="bg-destructive" | ||
onClick={handleDelete} | ||
> | ||
Delete | ||
</AlertDialogAction> | ||
</AlertDialogFooter> | ||
</AlertDialogContent> | ||
</AlertDialog> | ||
<FrigatePlusDialog | ||
upload={ | ||
showFrigatePlus ? (searchResult as unknown as Event) : undefined | ||
} | ||
onClose={() => setShowFrigatePlus(false)} | ||
onEventUploaded={() => {}} | ||
/> | ||
|
||
<div className="flex flex-col items-start"> | ||
{searchResult.end_time ? ( | ||
<TimeAgo time={searchResult.start_time * 1000} dense /> | ||
) : ( | ||
<div> | ||
<ActivityIndicator size={24} /> | ||
</div> | ||
)} | ||
{formattedDate} | ||
</div> | ||
<div className="flex flex-row items-center justify-end gap-8 md:gap-4"> | ||
{config?.plus?.enabled && | ||
searchResult.has_snapshot && | ||
searchResult.end_time && ( | ||
<Tooltip> | ||
<TooltipTrigger> | ||
<FrigatePlusIcon | ||
className="size-5 cursor-pointer text-primary" | ||
onClick={() => setShowFrigatePlus(true)} | ||
/> | ||
</TooltipTrigger> | ||
<TooltipContent>Submit to Frigate+</TooltipContent> | ||
</Tooltip> | ||
)} | ||
|
||
{config?.semantic_search?.enabled && ( | ||
<Tooltip> | ||
<TooltipTrigger> | ||
<MdImageSearch | ||
className="size-5 cursor-pointer text-primary" | ||
onClick={findSimilar} | ||
/> | ||
</TooltipTrigger> | ||
<TooltipContent>Find similar</TooltipContent> | ||
</Tooltip> | ||
)} | ||
|
||
<DropdownMenu> | ||
<DropdownMenuTrigger> | ||
<LuMoreVertical className="size-5 cursor-pointer text-primary" /> | ||
</DropdownMenuTrigger> | ||
<DropdownMenuContent align={"end"}> | ||
<DropdownMenuLabel className="mt-0.5"> | ||
Tracked Object Actions | ||
</DropdownMenuLabel> | ||
<DropdownMenuSeparator className="mt-1" /> | ||
{searchResult.has_clip && ( | ||
<DropdownMenuItem> | ||
<a | ||
className="justify_start flex items-center" | ||
href={`${baseUrl}api/events/${searchResult.id}/clip.mp4`} | ||
download={`${searchResult.camera}_${searchResult.label}.mp4`} | ||
> | ||
<LuDownload className="mr-2 size-4" /> | ||
<span>Download video</span> | ||
</a> | ||
</DropdownMenuItem> | ||
)} | ||
{searchResult.has_snapshot && ( | ||
<DropdownMenuItem> | ||
<a | ||
className="justify_start flex items-center" | ||
href={`${baseUrl}api/events/${searchResult.id}/snapshot.jpg`} | ||
download={`${searchResult.camera}_${searchResult.label}.jpg`} | ||
> | ||
<LuCamera className="mr-2 size-4" /> | ||
<span>Download snapshot</span> | ||
</a> | ||
</DropdownMenuItem> | ||
)} | ||
<DropdownMenuItem onClick={showObjectLifecycle}> | ||
<FaArrowsRotate className="mr-2 size-4" /> | ||
<span>View object lifecycle</span> | ||
</DropdownMenuItem> | ||
<DropdownMenuItem onClick={() => setDeleteDialogOpen(true)}> | ||
<LuTrash2 className="mr-2 size-4" /> | ||
<span>Delete</span> | ||
</DropdownMenuItem> | ||
</DropdownMenuContent> | ||
</DropdownMenu> | ||
</div> | ||
</> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.