feat: more tests, refactored

This commit is contained in:
2026-05-24 21:05:21 +07:00
parent e7dd9420e7
commit c1f12c7201
33 changed files with 1403 additions and 334 deletions
+30 -33
View File
@@ -7,6 +7,11 @@ type FaceBlurPreview = {
effects: LayerEffect[];
};
type FaceSelectionState = {
key: string;
indices: number[];
};
type UseFaceBlurWorkflowOptions = {
selectedLayer: Layer | null;
faceDetectionsLayerId: string | null;
@@ -20,8 +25,7 @@ export function useFaceBlurWorkflow(options: UseFaceBlurWorkflowOptions) {
const [blurMethod, setBlurMethod] = React.useState<FaceBlurMethod>("gaussian");
const [blurAmount, setBlurAmount] = React.useState(14);
const [censorColor, setCensorColor] = React.useState("#111111");
const [selectedFaceIndices, setSelectedFaceIndices] = React.useState<number[]>([]);
const [faceBlurPreview, setFaceBlurPreview] = React.useState<FaceBlurPreview | null>(null);
const [faceSelection, setFaceSelection] = React.useState<FaceSelectionState | null>(null);
const hasDetectableSelection = Boolean(selectedLayer && selectedLayer.type === "raster" && faceDetectionsLayerId === selectedLayer.id);
const faceBlurEffect = React.useMemo(
@@ -29,6 +33,15 @@ export function useFaceBlurWorkflow(options: UseFaceBlurWorkflowOptions) {
[selectedLayer?.effects],
);
const selectionKey = `${selectedLayer?.id ?? "none"}:${faceDetectionsLayerId ?? "none"}:${faceDetections.length}:${faceBlurEffect ? "effect" : "empty"}`;
const defaultSelectedFaceIndices = React.useMemo(() => {
if (!hasDetectableSelection || faceBlurEffect) return [];
return faceDetections.map((_, index) => index);
}, [faceDetections, faceBlurEffect, hasDetectableSelection]);
const selectedFaceIndices = faceSelection?.key === selectionKey ? faceSelection.indices : defaultSelectedFaceIndices;
const buildBlurRegions = React.useCallback(
(indices: number[]) => {
if (!selectedLayer || selectedLayer.type !== "raster") return [];
@@ -56,14 +69,18 @@ export function useFaceBlurWorkflow(options: UseFaceBlurWorkflowOptions) {
);
const toggleFaceIndex = React.useCallback((index: number) => {
setSelectedFaceIndices((prev) => (prev.includes(index) ? prev.filter((item) => item !== index) : [...prev, index]));
}, []);
setFaceSelection((prev) => {
const current = prev?.key === selectionKey ? prev.indices : defaultSelectedFaceIndices;
const indices = current.includes(index) ? current.filter((item) => item !== index) : [...current, index];
return { key: selectionKey, indices };
});
}, [defaultSelectedFaceIndices, selectionKey]);
const clearBlur = React.useCallback(() => {
if (!selectedLayer) return;
removeLayerEffect(selectedLayer.id, "face-blur");
setFaceBlurPreview(null);
}, [selectedLayer, removeLayerEffect]);
setFaceSelection({ key: selectionKey, indices: [] });
}, [removeLayerEffect, selectedLayer, selectionKey]);
const blurFaces = React.useCallback(
(indices: number[]) => {
@@ -78,41 +95,21 @@ export function useFaceBlurWorkflow(options: UseFaceBlurWorkflowOptions) {
regions,
censorColor,
});
setSelectedFaceIndices([]);
setFaceBlurPreview(null);
setFaceSelection({ key: selectionKey, indices: [] });
},
[blurAmount, blurMethod, buildBlurRegions, censorColor, faceDetections.length, faceDetectionsLayerId, selectedLayer, setLayerEffect],
[blurAmount, blurMethod, buildBlurRegions, censorColor, faceDetections.length, faceDetectionsLayerId, selectedLayer, selectionKey, setLayerEffect],
);
React.useEffect(() => {
if (!hasDetectableSelection) {
setSelectedFaceIndices((prev) => (prev.length === 0 ? prev : []));
setFaceBlurPreview(null);
return;
}
if (faceBlurEffect) {
setSelectedFaceIndices((prev) => (prev.length === 0 ? prev : []));
return;
}
setSelectedFaceIndices((prev) => {
const next = faceDetections.map((_, i) => i);
if (prev.length === next.length && prev.every((value, index) => value === next[index])) return prev;
return next;
});
}, [faceDetections, hasDetectableSelection, faceBlurEffect]);
React.useEffect(() => {
const faceBlurPreview = React.useMemo<FaceBlurPreview | null>(() => {
if (!hasDetectableSelection || !selectedLayer || selectedLayer.type !== "raster") {
setFaceBlurPreview(null);
return;
return null;
}
if (selectedFaceIndices.length === 0) {
setFaceBlurPreview(null);
return;
return null;
}
setFaceBlurPreview({
return {
layerId: selectedLayer.id,
effects: [{
kind: "face-blur",
@@ -122,7 +119,7 @@ export function useFaceBlurWorkflow(options: UseFaceBlurWorkflowOptions) {
regions: buildBlurRegions(selectedFaceIndices),
censorColor,
}],
});
};
}, [blurAmount, blurMethod, buildBlurRegions, censorColor, hasDetectableSelection, selectedFaceIndices, selectedLayer]);
return {
+1 -14
View File
@@ -32,7 +32,6 @@ type SelectedImageLayer = {
type UseFaceDetectionOptions = {
tool: EditorToolId;
selectedLayerId: string | null;
selectedImageLayer: SelectedImageLayer | null;
activeLayerStillSelected: (layerId: string) => boolean;
};
@@ -40,7 +39,6 @@ type UseFaceDetectionOptions = {
export function useFaceDetection(options: UseFaceDetectionOptions) {
const {
tool,
selectedLayerId,
selectedImageLayer,
activeLayerStillSelected,
} = options;
@@ -63,6 +61,7 @@ export function useFaceDetection(options: UseFaceDetectionOptions) {
(status: "idle" | "detecting" | "unsupported" = "idle") => {
setFaceDetections((prev) => (prev.length === 0 ? prev : []));
setFaceDetectionsLayerId((prev) => (prev === null ? prev : null));
setFacePreviews((prev) => (prev.length === 0 ? prev : []));
setFaceStatus((prev) => (prev === status ? prev : status));
},
[],
@@ -115,18 +114,6 @@ export function useFaceDetection(options: UseFaceDetectionOptions) {
tool,
]);
React.useEffect(() => {
if (tool !== "face") {
setFaceDetections([]);
setFaceDetectionsLayerId(null);
setFacePreviews([]);
return;
}
setFaceDetections([]);
setFaceDetectionsLayerId(null);
setFacePreviews([]);
}, [tool, selectedLayerId]);
React.useEffect(() => {
let canceled = false;