mirror of
https://github.com/documenso/documenso.git
synced 2025-11-13 08:13:56 +10:00
fix: prevent accidental signatures (#1515)
 
This commit is contained in:
@ -61,6 +61,7 @@ export const AddSignatureFormPartial = ({
|
||||
}: AddSignatureFormProps) => {
|
||||
const { currentStep, totalSteps } = useStep();
|
||||
const [validateUninsertedFields, setValidateUninsertedFields] = useState(false);
|
||||
const [isSignatureValid, setIsSignatureValid] = useState(false);
|
||||
|
||||
// Refined schema which takes into account whether to allow an empty name or signature.
|
||||
const refinedSchema = ZAddSignatureFormSchema.superRefine((val, ctx) => {
|
||||
@ -336,9 +337,17 @@ export const AddSignatureFormPartial = ({
|
||||
className="h-44 w-full"
|
||||
defaultValue={field.value}
|
||||
onBlur={field.onBlur}
|
||||
onValidityChange={(isValid) => {
|
||||
setIsSignatureValid(isValid);
|
||||
if (!isValid) {
|
||||
field.onChange(null);
|
||||
}
|
||||
}}
|
||||
onChange={(value) => {
|
||||
onFormValueChange(FieldType.SIGNATURE);
|
||||
field.onChange(value);
|
||||
if (isSignatureValid) {
|
||||
onFormValueChange(FieldType.SIGNATURE);
|
||||
field.onChange(value);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</CardContent>
|
||||
|
||||
@ -97,6 +97,8 @@ export type SignaturePadProps = Omit<HTMLAttributes<HTMLCanvasElement>, 'onChang
|
||||
disabled?: boolean;
|
||||
allowTypedSignature?: boolean;
|
||||
defaultValue?: string;
|
||||
onValidityChange?: (isValid: boolean) => void;
|
||||
minCoverageThreshold?: number;
|
||||
};
|
||||
|
||||
export const SignaturePad = ({
|
||||
@ -106,6 +108,8 @@ export const SignaturePad = ({
|
||||
onChange,
|
||||
disabled = false,
|
||||
allowTypedSignature,
|
||||
onValidityChange,
|
||||
minCoverageThreshold = 0.01,
|
||||
...props
|
||||
}: SignaturePadProps) => {
|
||||
const $el = useRef<HTMLCanvasElement>(null);
|
||||
@ -134,6 +138,29 @@ export const SignaturePad = ({
|
||||
} satisfies StrokeOptions;
|
||||
}, []);
|
||||
|
||||
const checkSignatureValidity = () => {
|
||||
if ($el.current) {
|
||||
const ctx = $el.current.getContext('2d');
|
||||
|
||||
if (ctx) {
|
||||
const imageData = ctx.getImageData(0, 0, $el.current.width, $el.current.height);
|
||||
const data = imageData.data;
|
||||
let filledPixels = 0;
|
||||
const totalPixels = data.length / 4;
|
||||
|
||||
for (let i = 0; i < data.length; i += 4) {
|
||||
if (data[i + 3] > 0) filledPixels++;
|
||||
}
|
||||
|
||||
const filledPercentage = filledPixels / totalPixels;
|
||||
const isValid = filledPercentage > minCoverageThreshold;
|
||||
onValidityChange?.(isValid);
|
||||
|
||||
return isValid;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseDown = (event: MouseEvent | PointerEvent | TouchEvent) => {
|
||||
if (event.cancelable) {
|
||||
event.preventDefault();
|
||||
@ -218,7 +245,6 @@ export const SignaturePad = ({
|
||||
|
||||
if (ctx) {
|
||||
ctx.restore();
|
||||
|
||||
ctx.imageSmoothingEnabled = true;
|
||||
ctx.imageSmoothingQuality = 'high';
|
||||
ctx.fillStyle = selectedColor;
|
||||
@ -230,7 +256,11 @@ export const SignaturePad = ({
|
||||
ctx.fill(pathData);
|
||||
});
|
||||
|
||||
onChange?.($el.current.toDataURL());
|
||||
const isValidSignature = checkSignatureValidity();
|
||||
|
||||
if (isValidSignature) {
|
||||
onChange?.($el.current.toDataURL());
|
||||
}
|
||||
ctx.save();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user