Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/add cooldown varify email #4931

Merged
merged 3 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lang/ca.json
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,7 @@
"label.email_verified": "Correu electrònic verificat",
"label.email_verify": "Verifica el correu electrònic",
"label.email_already_verified": "El teu correu electrònic ha estat verificat. Ara pots desar la informació del teu perfil.",
"label.email_cooldown": "No has rebut el correu electrònic? Revisa la carpeta de correu brossa o <button>Torna a enviar el codi de verificació!</button> en <time>02:59</time>",
"label.email_used": "Aquesta adreça de correu electrònic s'utilitzarà per enviar-te comunicacions importants.",
"label.email_used_another": "Aquest correu electrònic ja ha estat verificat en un altre perfil!",
"label.email_sent_to": "Codi de verificació enviat a {email}",
Expand Down
1 change: 1 addition & 0 deletions lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,7 @@
"label.email_verified": "Email Verified",
"label.email_verify": "Verify Email",
"label.email_already_verified": "Your email has been verified. You can now save your profile information.",
"label.email_cooldown": "Didn't get the email? Check your spam folder or <button>Resend Verification Code!</button> in <time>02:59</time>",
"label.email_used": "This email address will be used to send you important communications.",
"label.email_used_another": "This email that has already been verified on another profile!",
"label.email_sent_to": "Verification code sent to {email}",
Expand Down
1 change: 1 addition & 0 deletions lang/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@
"label.email_verified": "Correo electrónico verificado",
"label.email_verify": "Verificar correo electrónico",
"label.email_already_verified": "Tu correo electrónico ha sido verificado. Ahora puedes guardar la información de tu perfil.",
"label.email_cooldown": "¿No recibiste el correo electrónico? Revisa tu carpeta de spam o <button>¡Reenviar código de verificación!</button> en <time>02:59</time>",
"label.email_used": "Esta dirección de correo electrónico se utilizará para enviarte comunicaciones importantes.",
"label.email_used_another": "¡Este correo electrónico ya ha sido verificado en otro perfil!",
"label.email_sent_to": "Código de verificación enviado a {email}",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"private": true,
"scripts": {
"build": "next build",
"dev": "next dev && node src/sitemap.ts",
"dev": "next dev",
"lint": "eslint . --ext .ts --ext .tsx",
"lint:fix": "eslint . --ext .ts --ext .tsx --fix",
"start": "next start",
Expand Down
68 changes: 67 additions & 1 deletion src/components/InputUserEmailVerify.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@ const InputUserEmailVerify = forwardRef<HTMLInputElement, InputType>(
);
const [isVerificationProcess, setIsVerificationProcess] =
useState(false);
const [isCooldown, setIsCooldown] = useState(false);
const [cooldownTime, setCooldownTime] = useState(0);

const [inputDescription, setInputDescription] = useState(
verified
? formatMessage({
Expand Down Expand Up @@ -199,6 +202,12 @@ const InputUserEmailVerify = forwardRef<HTMLInputElement, InputType>(
id: 'label.email_verify',
});

// Reset cooldown
const resetCoolDown = () => {
setIsCooldown(false);
setCooldownTime(0);
};

// Enable verification process "button" if email input value was empty and not verified yet
// and setup email if input value was changed and has more than 3 characters
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
Expand Down Expand Up @@ -227,6 +236,26 @@ const InputUserEmailVerify = forwardRef<HTMLInputElement, InputType>(
// or email is already exist on another user account
// If email isn't verified it will send email with verification code to user
const verificationEmailHandler = async () => {
// Prevent the button from being clicked during cooldown
if (isCooldown) {
return;
}

// Start cooldown timer
setIsCooldown(true);
setCooldownTime(180);

const intervalId = setInterval(() => {
setCooldownTime(prev => {
if (prev <= 1) {
resetCoolDown();
setDisableVerifyButton(false);
return 0;
}
return prev - 1;
});
}, 1000);
Comment on lines +248 to +257
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix potential memory leak in timer implementation

The current timer implementation using setInterval could lead to memory leaks because:

  1. The interval is not cleaned up if the component unmounts
  2. Multiple intervals could be created on re-renders

Wrap the timer logic in a useEffect hook to properly manage the interval:

+ useEffect(() => {
+   let intervalId: NodeJS.Timeout | undefined;
+   
+   if (isCooldown) {
+     intervalId = setInterval(() => {
+       setCooldownTime(prev => {
+         if (prev <= 1) {
+           resetCoolDown();
+           setDisableVerifyButton(false);
+           return 0;
+         }
+         return prev - 1;
+       });
+     }, 1000);
+   }
+   
+   return () => {
+     if (intervalId) {
+       clearInterval(intervalId);
+     }
+   };
+ }, [isCooldown]);

  const verificationEmailHandler = async () => {
    if (isCooldown) {
      return;
    }

    setIsCooldown(true);
    setCooldownTime(180);

-   const intervalId = setInterval(() => {
-     setCooldownTime(prev => {
-       if (prev <= 1) {
-         resetCoolDown();
-         setDisableVerifyButton(false);
-         return 0;
-       }
-       return prev - 1;
-     });
-   }, 1000);

    try {
      const { data } = await client.mutate({
        mutation: SEND_USER_EMAIL_CONFIRMATION_CODE_FLOW,
        variables: {
          email: email,
        },
      });

      // ... rest of the code

-     clearInterval(intervalId);
    } catch (error) {
      if (error instanceof Error) {
-       clearInterval(intervalId);
        resetCoolDown();
        showToastError(error.message);
      }
      console.log(error);
    }
  };

Also applies to: 294-294, 297-297


try {
const { data } = await client.mutate({
mutation: SEND_USER_EMAIL_CONFIRMATION_CODE_FLOW,
Expand Down Expand Up @@ -257,8 +286,16 @@ const InputUserEmailVerify = forwardRef<HTMLInputElement, InputType>(
}),
);
}

// Stop the timer when fetch ends
resetCoolDown();

// Clear interval when fetch is done
clearInterval(intervalId);
} catch (error) {
if (error instanceof Error) {
clearInterval(intervalId);
resetCoolDown();
showToastError(error.message);
}
console.log(error);
Expand Down Expand Up @@ -399,7 +436,36 @@ const InputUserEmailVerify = forwardRef<HTMLInputElement, InputType>(
size={InputSizeToLinkSize(size)}
$validationstatus={validationStatus}
>
{inputDescription}
{isCooldown && (
<InputCodeDesc>
<FormattedMessage
id='label.email_cooldown'
values={{
button: chunks => (
<button
type='button'
onClick={
verificationEmailHandler
}
disabled={isCooldown}
>
{chunks}
</button>
),
time: () => (
<b>
{Math.floor(cooldownTime / 60)}:
{(
'0' +
(cooldownTime % 60)
).slice(-2)}
</b>
),
}}
/>
</InputCodeDesc>
)}
{!isCooldown && inputDescription}
</InputDesc>
)}
{isVerificationProcess && (
Expand Down
Loading