mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-05-05 13:07:44 +03:00
implement event selection
This commit is contained in:
parent
c0bfae5b4a
commit
2110320860
@ -142,29 +142,33 @@ export default function FaceLibrary() {
|
|||||||
|
|
||||||
const [selectedFaces, setSelectedFaces] = useState<string[]>([]);
|
const [selectedFaces, setSelectedFaces] = useState<string[]>([]);
|
||||||
|
|
||||||
const onClickFace = useCallback(
|
const onClickFaces = useCallback(
|
||||||
(imageId: string, ctrl: boolean) => {
|
(images: string[], ctrl: boolean) => {
|
||||||
if (selectedFaces.length == 0 && !ctrl) {
|
if (selectedFaces.length == 0 && !ctrl) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const index = selectedFaces.indexOf(imageId);
|
let newSelectedFaces = [...selectedFaces];
|
||||||
|
|
||||||
if (index != -1) {
|
images.forEach((imageId) => {
|
||||||
if (selectedFaces.length == 1) {
|
const index = newSelectedFaces.indexOf(imageId);
|
||||||
setSelectedFaces([]);
|
|
||||||
|
if (index != -1) {
|
||||||
|
if (selectedFaces.length == 1) {
|
||||||
|
newSelectedFaces = [];
|
||||||
|
} else {
|
||||||
|
const copy = [
|
||||||
|
...newSelectedFaces.slice(0, index),
|
||||||
|
...newSelectedFaces.slice(index + 1),
|
||||||
|
];
|
||||||
|
newSelectedFaces = copy;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const copy = [
|
newSelectedFaces.push(imageId);
|
||||||
...selectedFaces.slice(0, index),
|
|
||||||
...selectedFaces.slice(index + 1),
|
|
||||||
];
|
|
||||||
setSelectedFaces(copy);
|
|
||||||
}
|
}
|
||||||
} else {
|
});
|
||||||
const copy = [...selectedFaces];
|
|
||||||
copy.push(imageId);
|
setSelectedFaces(newSelectedFaces);
|
||||||
setSelectedFaces(copy);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
[selectedFaces, setSelectedFaces],
|
[selectedFaces, setSelectedFaces],
|
||||||
);
|
);
|
||||||
@ -283,7 +287,7 @@ export default function FaceLibrary() {
|
|||||||
attemptImages={trainImages}
|
attemptImages={trainImages}
|
||||||
faceNames={faces}
|
faceNames={faces}
|
||||||
selectedFaces={selectedFaces}
|
selectedFaces={selectedFaces}
|
||||||
onClickFace={onClickFace}
|
onClickFaces={onClickFaces}
|
||||||
onRefresh={refreshFaces}
|
onRefresh={refreshFaces}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
@ -391,7 +395,7 @@ type TrainingGridProps = {
|
|||||||
attemptImages: string[];
|
attemptImages: string[];
|
||||||
faceNames: string[];
|
faceNames: string[];
|
||||||
selectedFaces: string[];
|
selectedFaces: string[];
|
||||||
onClickFace: (image: string, ctrl: boolean) => void;
|
onClickFaces: (images: string[], ctrl: boolean) => void;
|
||||||
onRefresh: () => void;
|
onRefresh: () => void;
|
||||||
};
|
};
|
||||||
function TrainingGrid({
|
function TrainingGrid({
|
||||||
@ -399,7 +403,7 @@ function TrainingGrid({
|
|||||||
attemptImages,
|
attemptImages,
|
||||||
faceNames,
|
faceNames,
|
||||||
selectedFaces,
|
selectedFaces,
|
||||||
onClickFace,
|
onClickFaces,
|
||||||
onRefresh,
|
onRefresh,
|
||||||
}: TrainingGridProps) {
|
}: TrainingGridProps) {
|
||||||
const { t } = useTranslation(["views/faceLibrary"]);
|
const { t } = useTranslation(["views/faceLibrary"]);
|
||||||
@ -523,7 +527,7 @@ function TrainingGrid({
|
|||||||
event={event}
|
event={event}
|
||||||
faceNames={faceNames}
|
faceNames={faceNames}
|
||||||
selectedFaces={selectedFaces}
|
selectedFaces={selectedFaces}
|
||||||
onClickFace={onClickFace}
|
onClickFaces={onClickFaces}
|
||||||
onSelectEvent={setSelectedEvent}
|
onSelectEvent={setSelectedEvent}
|
||||||
onRefresh={onRefresh}
|
onRefresh={onRefresh}
|
||||||
/>
|
/>
|
||||||
@ -540,7 +544,7 @@ type FaceAttemptGroupProps = {
|
|||||||
event?: Event;
|
event?: Event;
|
||||||
faceNames: string[];
|
faceNames: string[];
|
||||||
selectedFaces: string[];
|
selectedFaces: string[];
|
||||||
onClickFace: (image: string, ctrl: boolean) => void;
|
onClickFaces: (image: string[], ctrl: boolean) => void;
|
||||||
onSelectEvent: (event: Event) => void;
|
onSelectEvent: (event: Event) => void;
|
||||||
onRefresh: () => void;
|
onRefresh: () => void;
|
||||||
};
|
};
|
||||||
@ -550,7 +554,7 @@ function FaceAttemptGroup({
|
|||||||
event,
|
event,
|
||||||
faceNames,
|
faceNames,
|
||||||
selectedFaces,
|
selectedFaces,
|
||||||
onClickFace,
|
onClickFaces,
|
||||||
onSelectEvent,
|
onSelectEvent,
|
||||||
onRefresh,
|
onRefresh,
|
||||||
}: FaceAttemptGroupProps) {
|
}: FaceAttemptGroupProps) {
|
||||||
@ -564,15 +568,48 @@ function FaceAttemptGroup({
|
|||||||
[group, selectedFaces],
|
[group, selectedFaces],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// interaction
|
||||||
|
|
||||||
|
const handleClickEvent = useCallback(
|
||||||
|
(meta: boolean) => {
|
||||||
|
if (event && selectedFaces.length == 0 && !meta) {
|
||||||
|
onSelectEvent(event);
|
||||||
|
} else {
|
||||||
|
const anySelected =
|
||||||
|
group.find((face) => selectedFaces.includes(face.filename)) !=
|
||||||
|
undefined;
|
||||||
|
|
||||||
|
if (anySelected) {
|
||||||
|
// deselect all
|
||||||
|
const toDeselect: string[] = [];
|
||||||
|
group.forEach((face) => {
|
||||||
|
if (selectedFaces.includes(face.filename)) {
|
||||||
|
toDeselect.push(face.filename);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
onClickFaces(toDeselect, false);
|
||||||
|
} else {
|
||||||
|
// select all
|
||||||
|
onClickFaces(
|
||||||
|
group.map((face) => face.filename),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[event, group, selectedFaces, onClickFaces, onSelectEvent],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex flex-col gap-2 rounded-lg bg-card p-2 outline outline-[3px]",
|
"flex cursor-pointer flex-col gap-2 rounded-lg bg-card p-2 outline outline-[3px]",
|
||||||
isMobile && "w-full",
|
isMobile && "w-full",
|
||||||
allFacesSelected
|
allFacesSelected
|
||||||
? "shadow-selected outline-selected"
|
? "shadow-selected outline-selected"
|
||||||
: "outline-transparent duration-500",
|
: "outline-transparent duration-500",
|
||||||
)}
|
)}
|
||||||
|
onClick={(e) => handleClickEvent(e.metaKey)}
|
||||||
>
|
>
|
||||||
<div className="flex flex-row justify-between">
|
<div className="flex flex-row justify-between">
|
||||||
<div className="capitalize">
|
<div className="capitalize">
|
||||||
@ -623,7 +660,7 @@ function FaceAttemptGroup({
|
|||||||
}
|
}
|
||||||
onClick={(data, meta) => {
|
onClick={(data, meta) => {
|
||||||
if (meta || selectedFaces.length > 0) {
|
if (meta || selectedFaces.length > 0) {
|
||||||
onClickFace(data.filename, true);
|
onClickFaces([data.filename], true);
|
||||||
} else if (event) {
|
} else if (event) {
|
||||||
onSelectEvent(event);
|
onSelectEvent(event);
|
||||||
}
|
}
|
||||||
@ -749,7 +786,10 @@ function FaceAttempt({
|
|||||||
ref={imgRef}
|
ref={imgRef}
|
||||||
className={cn("size-44", isMobile && "w-full")}
|
className={cn("size-44", isMobile && "w-full")}
|
||||||
src={`${baseUrl}clips/faces/train/${data.filename}`}
|
src={`${baseUrl}clips/faces/train/${data.filename}`}
|
||||||
onClick={(e) => onClick(data, e.metaKey || e.ctrlKey)}
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
onClick(data, e.metaKey || e.ctrlKey);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<div className="absolute bottom-1 right-1 z-10 rounded-lg bg-black/50 px-2 py-1 text-xs text-white">
|
<div className="absolute bottom-1 right-1 z-10 rounded-lg bg-black/50 px-2 py-1 text-xs text-white">
|
||||||
<TimeAgo
|
<TimeAgo
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user