diff --git a/apps/web/components/security/EnableTwoFactorModal.tsx b/apps/web/components/security/EnableTwoFactorModal.tsx index 9442b723c3..20b9a89026 100644 --- a/apps/web/components/security/EnableTwoFactorModal.tsx +++ b/apps/web/components/security/EnableTwoFactorModal.tsx @@ -1,8 +1,9 @@ import type { BaseSyntheticEvent } from "react"; -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { ErrorCode } from "@calcom/features/auth/lib/ErrorCode"; +import { useCallbackRef } from "@calcom/lib/hooks/useCallbackRef"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { Button, Dialog, DialogContent, Form } from "@calcom/ui"; @@ -127,6 +128,17 @@ const EnableTwoFactorModal = ({ onEnable, onCancel }: EnableTwoFactorModalProps) } } + const handleEnableRef = useCallbackRef(handleEnable); + + const totpCode = form.watch("totpCode"); + + // auto submit 2FA if all inputs have a value + useEffect(() => { + if (totpCode?.trim().length === 6) { + form.handleSubmit(handleEnableRef.current)(); + } + }, [form, handleEnableRef, totpCode]); + return ( diff --git a/apps/web/components/settings/EnableTwoFactorModal.tsx b/apps/web/components/settings/EnableTwoFactorModal.tsx index c6ad80254f..77babea82f 100644 --- a/apps/web/components/settings/EnableTwoFactorModal.tsx +++ b/apps/web/components/settings/EnableTwoFactorModal.tsx @@ -1,8 +1,9 @@ import type { BaseSyntheticEvent } from "react"; -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { ErrorCode } from "@calcom/features/auth/lib/ErrorCode"; +import { useCallbackRef } from "@calcom/lib/hooks/useCallbackRef"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { Button, Dialog, DialogContent, DialogFooter, Form, TextField } from "@calcom/ui"; @@ -129,6 +130,17 @@ const EnableTwoFactorModal = ({ onEnable, onCancel, open, onOpenChange }: Enable } } + const handleEnableRef = useCallbackRef(handleEnable); + + const totpCode = form.watch("totpCode"); + + // auto submit 2FA if all inputs have a value + useEffect(() => { + if (totpCode?.trim().length === 6) { + form.handleSubmit(handleEnableRef.current)(); + } + }, [form, handleEnableRef, totpCode]); + return ( diff --git a/apps/web/playwright/login.2fa.e2e.ts b/apps/web/playwright/login.2fa.e2e.ts index 2a869e5763..5c8f9db4be 100644 --- a/apps/web/playwright/login.2fa.e2e.ts +++ b/apps/web/playwright/login.2fa.e2e.ts @@ -102,7 +102,7 @@ test.describe("2FA Tests", async () => { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion await fillOtp({ page, secret: secret! }); - await page.click('[data-testid="enable-2fa"]'); + await expect(page.locator(`[data-testid=two-factor-switch][data-state="checked"]`)).toBeVisible(); return user; diff --git a/packages/lib/hooks/useCallbackRef.ts b/packages/lib/hooks/useCallbackRef.ts new file mode 100644 index 0000000000..87bfc7eeba --- /dev/null +++ b/packages/lib/hooks/useCallbackRef.ts @@ -0,0 +1,14 @@ +import { useRef } from "react"; + +import { useIsomorphicLayoutEffect } from "./useIsomorphicLayoutEffect"; + +export const useCallbackRef = (callback: C) => { + const callbackRef = useRef(callback); + useIsomorphicLayoutEffect(() => { + callbackRef.current = callback; + }); + + return callbackRef; +}; + +export default useCallbackRef;