implement event selection

This commit is contained in:
Nicolas Mowen 2025-03-31 14:40:59 -06:00
parent c0bfae5b4a
commit 2110320860

View File

@ -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