mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-18 17:14:26 +03:00
Add ability to view attempts
This commit is contained in:
parent
f84713487f
commit
fd07fa5124
@ -14,7 +14,7 @@ import { toast } from "sonner";
|
|||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
|
|
||||||
export default function FaceLibrary() {
|
export default function FaceLibrary() {
|
||||||
const [page, setPage] = useState<string>();
|
const [page, setPage] = useState<string>("attempts");
|
||||||
const [pageToggle, setPageToggle] = useOptimisticState(page, setPage, 100);
|
const [pageToggle, setPageToggle] = useOptimisticState(page, setPage, 100);
|
||||||
const tabsRef = useRef<HTMLDivElement | null>(null);
|
const tabsRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
@ -23,7 +23,8 @@ export default function FaceLibrary() {
|
|||||||
const { data: faceData, mutate: refreshFaces } = useSWR("faces");
|
const { data: faceData, mutate: refreshFaces } = useSWR("faces");
|
||||||
|
|
||||||
const faces = useMemo<string[]>(
|
const faces = useMemo<string[]>(
|
||||||
() => (faceData ? Object.keys(faceData) : []),
|
() =>
|
||||||
|
faceData ? Object.keys(faceData).filter((face) => face != "debug") : [],
|
||||||
[faceData],
|
[faceData],
|
||||||
);
|
);
|
||||||
const faceImages = useMemo<string[]>(
|
const faceImages = useMemo<string[]>(
|
||||||
@ -31,6 +32,11 @@ export default function FaceLibrary() {
|
|||||||
[pageToggle, faceData],
|
[pageToggle, faceData],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const faceAttempts = useMemo<string[]>(
|
||||||
|
() => faceData?.["debug"] || [],
|
||||||
|
[faceData],
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!pageToggle && faces) {
|
if (!pageToggle && faces) {
|
||||||
setPageToggle(faces[0]);
|
setPageToggle(faces[0]);
|
||||||
@ -105,10 +111,24 @@ export default function FaceLibrary() {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
{faceAttempts.length > 0 && (
|
||||||
|
<>
|
||||||
|
<ToggleGroupItem
|
||||||
|
value="attempts"
|
||||||
|
className={`flex scroll-mx-10 items-center justify-between gap-2 ${pageToggle == "attempts" ? "" : "*:text-muted-foreground"}`}
|
||||||
|
data-nav-item="attempts"
|
||||||
|
aria-label="Select attempts"
|
||||||
|
>
|
||||||
|
<div>Attempts</div>
|
||||||
|
</ToggleGroupItem>
|
||||||
|
<div>|</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
{Object.values(faces).map((item) => (
|
{Object.values(faces).map((item) => (
|
||||||
<ToggleGroupItem
|
<ToggleGroupItem
|
||||||
key={item}
|
key={item}
|
||||||
className={`flex scroll-mx-10 items-center justify-between gap-2 ${page == "UI settings" ? "last:mr-20" : ""} ${pageToggle == item ? "" : "*:text-muted-foreground"}`}
|
className={`flex scroll-mx-10 items-center justify-between gap-2 ${pageToggle == item ? "" : "*:text-muted-foreground"}`}
|
||||||
value={item}
|
value={item}
|
||||||
data-nav-item={item}
|
data-nav-item={item}
|
||||||
aria-label={`Select ${item}`}
|
aria-label={`Select ${item}`}
|
||||||
@ -121,20 +141,100 @@ export default function FaceLibrary() {
|
|||||||
</div>
|
</div>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
</div>
|
</div>
|
||||||
{pageToggle && (
|
{pageToggle &&
|
||||||
<div className="scrollbar-container flex flex-wrap gap-2 overflow-y-scroll">
|
(pageToggle == "attempts" ? (
|
||||||
{faceImages.map((image: string) => (
|
<AttemptsGrid attemptImages={faceAttempts} />
|
||||||
<FaceImage key={image} name={pageToggle} image={image} />
|
) : (
|
||||||
))}
|
<FaceGrid
|
||||||
<Button
|
faceImages={faceImages}
|
||||||
key="upload"
|
pageToggle={pageToggle}
|
||||||
className="size-40"
|
setUpload={setUpload}
|
||||||
onClick={() => setUpload(true)}
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
type AttemptsGridProps = {
|
||||||
|
attemptImages: string[];
|
||||||
|
};
|
||||||
|
function AttemptsGrid({ attemptImages }: AttemptsGridProps) {
|
||||||
|
return (
|
||||||
|
<div className="scrollbar-container flex flex-wrap gap-2 overflow-y-scroll">
|
||||||
|
{attemptImages.map((image: string) => (
|
||||||
|
<FaceImage key={image} name={"something"} image={image} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
type FaceAttemptProps = {
|
||||||
|
name: string;
|
||||||
|
image: string;
|
||||||
|
};
|
||||||
|
function FaceAttempt({ name, image }: FaceAttemptProps) {
|
||||||
|
const [hovered, setHovered] = useState(false);
|
||||||
|
|
||||||
|
const onDelete = useCallback(() => {
|
||||||
|
axios
|
||||||
|
.post(`/faces/${name}/delete`, { ids: [image] })
|
||||||
|
.then((resp) => {
|
||||||
|
if (resp.status == 200) {
|
||||||
|
toast.error(`Successfully deleted face.`, { position: "top-center" });
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
if (error.response?.data?.message) {
|
||||||
|
toast.error(`Failed to delete: ${error.response.data.message}`, {
|
||||||
|
position: "top-center",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
toast.error(`Failed to delete: ${error.message}`, {
|
||||||
|
position: "top-center",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [name, image]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="relative h-40"
|
||||||
|
onMouseEnter={isDesktop ? () => setHovered(true) : undefined}
|
||||||
|
onMouseLeave={isDesktop ? () => setHovered(false) : undefined}
|
||||||
|
onClick={isDesktop ? undefined : () => setHovered(!hovered)}
|
||||||
|
>
|
||||||
|
{hovered && (
|
||||||
|
<div className="absolute right-1 top-1">
|
||||||
|
<Chip
|
||||||
|
className="cursor-pointer rounded-md bg-gray-500 bg-gradient-to-br from-gray-400 to-gray-500"
|
||||||
|
onClick={() => onDelete()}
|
||||||
>
|
>
|
||||||
<LuImagePlus className="size-10" />
|
<LuTrash className="size-4 fill-destructive text-destructive" />
|
||||||
</Button>
|
</Chip>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
<img
|
||||||
|
className="h-40 rounded-md"
|
||||||
|
src={`${baseUrl}clips/faces/${name}/${image}`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
type FaceGridProps = {
|
||||||
|
faceImages: string[];
|
||||||
|
pageToggle: string;
|
||||||
|
setUpload: (upload: boolean) => void;
|
||||||
|
};
|
||||||
|
function FaceGrid({ faceImages, pageToggle, setUpload }: FaceGridProps) {
|
||||||
|
return (
|
||||||
|
<div className="scrollbar-container flex flex-wrap gap-2 overflow-y-scroll">
|
||||||
|
{faceImages.map((image: string) => (
|
||||||
|
<FaceImage key={image} name={pageToggle} image={image} />
|
||||||
|
))}
|
||||||
|
<Button key="upload" className="size-40" onClick={() => setUpload(true)}>
|
||||||
|
<LuImagePlus className="size-10" />
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user