From 91d567f9cc48dea7d605b55c6014174806b8d9ab Mon Sep 17 00:00:00 2001 From: Fringg Date: Mon, 23 Feb 2026 15:44:16 +0300 Subject: [PATCH] fix: add resend email cooldown and allow email change for all auth types Add 60-second cooldown timer on resend verification email button to prevent spam. Remove auth_type restriction on change email button so OAuth users can also change their email. --- src/locales/en.json | 1 + src/locales/fa.json | 1 + src/locales/ru.json | 1 + src/locales/zh.json | 1 + src/pages/Profile.tsx | 56 +++++++++++++++++++++++++------------------ 5 files changed, 37 insertions(+), 23 deletions(-) diff --git a/src/locales/en.json b/src/locales/en.json index ea37eb5..4360b98 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -2902,6 +2902,7 @@ "notVerified": "Not verified", "verificationRequired": "Please verify your email to use email login.", "resendVerification": "Resend Verification Email", + "resendIn": "Resend in {{seconds}} sec.", "verificationResent": "Verification email resent!", "canLoginWithEmail": "You can now log in using your email and password.", "emailAlreadyRegistered": "This email is already linked to another account. If this is your email, log in with it or contact support.", diff --git a/src/locales/fa.json b/src/locales/fa.json index 20cc322..68f682c 100644 --- a/src/locales/fa.json +++ b/src/locales/fa.json @@ -2402,6 +2402,7 @@ "notVerified": "تایید نشده", "verificationRequired": "لطفاً ایمیل خود را تایید کنید تا از ورود با ایمیل استفاده کنید.", "resendVerification": "ارسال مجدد ایمیل تایید", + "resendIn": "ارسال مجدد در {{seconds}} ثانیه", "verificationResent": "ایمیل تایید مجدداً ارسال شد!", "canLoginWithEmail": "اکنون می‌توانید با ایمیل و رمز عبور وارد شوید.", "emailAlreadyRegistered": "این ایمیل قبلاً به حساب دیگری متصل شده است. اگر این ایمیل شماست، با آن وارد شوید یا با پشتیبانی تماس بگیرید.", diff --git a/src/locales/ru.json b/src/locales/ru.json index 29147d4..e14a4e9 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -3454,6 +3454,7 @@ "notVerified": "Не подтверждён", "verificationRequired": "Подтвердите email для использования входа по почте.", "resendVerification": "Отправить письмо повторно", + "resendIn": "Повторить через {{seconds}} сек.", "verificationResent": "Письмо отправлено повторно!", "canLoginWithEmail": "Теперь вы можете входить с помощью email и пароля.", "emailAlreadyRegistered": "Этот email уже привязан к другому аккаунту. Если это ваш email, войдите через него или обратитесь в поддержку.", diff --git a/src/locales/zh.json b/src/locales/zh.json index 18dceb5..1f856e7 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -2401,6 +2401,7 @@ "notVerified": "未验证", "verificationRequired": "请验证邮箱以使用邮箱登录。", "resendVerification": "重新发送验证邮件", + "resendIn": "{{seconds}} 秒后重新发送", "verificationResent": "验证邮件已重新发送!", "canLoginWithEmail": "现在您可以使用邮箱和密码登录。", "emailAlreadyRegistered": "此邮箱已绑定到其他账户。如果这是您的邮箱,请使用邮箱登录或联系客服。", diff --git a/src/pages/Profile.tsx b/src/pages/Profile.tsx index b811f23..1437b26 100644 --- a/src/pages/Profile.tsx +++ b/src/pages/Profile.tsx @@ -77,6 +77,7 @@ export default function Profile() { const [changeCode, setChangeCode] = useState(''); const [changeError, setChangeError] = useState(null); const [resendCooldown, setResendCooldown] = useState(0); + const [verificationResendCooldown, setVerificationResendCooldown] = useState(0); const newEmailInputRef = useRef(null); const codeInputRef = useRef(null); @@ -171,6 +172,7 @@ export default function Profile() { onSuccess: () => { setSuccess(t('profile.verificationResent')); setError(null); + setVerificationResendCooldown(UI.RESEND_COOLDOWN_SEC); }, onError: (err: { response?: { data?: { detail?: string } } }) => { setError(err.response?.data?.detail || t('common.error')); @@ -228,7 +230,7 @@ export default function Profile() { }, }); - // Resend cooldown timer + // Resend cooldown timers useEffect(() => { if (resendCooldown <= 0) return; const timer = setInterval(() => { @@ -237,6 +239,14 @@ export default function Profile() { return () => clearInterval(timer); }, [resendCooldown]); + useEffect(() => { + if (verificationResendCooldown <= 0) return; + const timer = setInterval(() => { + setVerificationResendCooldown((prev) => Math.max(0, prev - 1)); + }, 1000); + return () => clearInterval(timer); + }, [verificationResendCooldown]); + // Auto-focus inputs on step change useEffect(() => { const timer = setTimeout(() => { @@ -454,34 +464,34 @@ export default function Profile() { - {(user.auth_type === 'telegram' || user.auth_type === 'email') && ( - - )} + )} - {user.email_verified && - (user.auth_type === 'telegram' || user.auth_type === 'email') && ( -
-

{t('profile.canLoginWithEmail')}

- -
- )} + {user.email_verified && ( +
+

{t('profile.canLoginWithEmail')}

+ +
+ )} {/* Inline email change flow */}