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.
This commit is contained in:
Fringg
2026-02-23 15:44:16 +03:00
parent 2b2ead837c
commit 91d567f9cc
5 changed files with 37 additions and 23 deletions

View File

@@ -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.",

View File

@@ -2402,6 +2402,7 @@
"notVerified": "تایید نشده",
"verificationRequired": "لطفاً ایمیل خود را تایید کنید تا از ورود با ایمیل استفاده کنید.",
"resendVerification": "ارسال مجدد ایمیل تایید",
"resendIn": "ارسال مجدد در {{seconds}} ثانیه",
"verificationResent": "ایمیل تایید مجدداً ارسال شد!",
"canLoginWithEmail": "اکنون می‌توانید با ایمیل و رمز عبور وارد شوید.",
"emailAlreadyRegistered": "این ایمیل قبلاً به حساب دیگری متصل شده است. اگر این ایمیل شماست، با آن وارد شوید یا با پشتیبانی تماس بگیرید.",

View File

@@ -3454,6 +3454,7 @@
"notVerified": "Не подтверждён",
"verificationRequired": "Подтвердите email для использования входа по почте.",
"resendVerification": "Отправить письмо повторно",
"resendIn": "Повторить через {{seconds}} сек.",
"verificationResent": "Письмо отправлено повторно!",
"canLoginWithEmail": "Теперь вы можете входить с помощью email и пароля.",
"emailAlreadyRegistered": "Этот email уже привязан к другому аккаунту. Если это ваш email, войдите через него или обратитесь в поддержку.",

View File

@@ -2401,6 +2401,7 @@
"notVerified": "未验证",
"verificationRequired": "请验证邮箱以使用邮箱登录。",
"resendVerification": "重新发送验证邮件",
"resendIn": "{{seconds}} 秒后重新发送",
"verificationResent": "验证邮件已重新发送!",
"canLoginWithEmail": "现在您可以使用邮箱和密码登录。",
"emailAlreadyRegistered": "此邮箱已绑定到其他账户。如果这是您的邮箱,请使用邮箱登录或联系客服。",

View File

@@ -77,6 +77,7 @@ export default function Profile() {
const [changeCode, setChangeCode] = useState('');
const [changeError, setChangeError] = useState<string | null>(null);
const [resendCooldown, setResendCooldown] = useState(0);
const [verificationResendCooldown, setVerificationResendCooldown] = useState(0);
const newEmailInputRef = useRef<HTMLInputElement>(null);
const codeInputRef = useRef<HTMLInputElement>(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() {
<Button
onClick={() => resendVerificationMutation.mutate()}
loading={resendVerificationMutation.isPending}
disabled={verificationResendCooldown > 0}
>
{t('profile.resendVerification')}
{verificationResendCooldown > 0
? t('profile.resendIn', { seconds: verificationResendCooldown })
: t('profile.resendVerification')}
</Button>
{(user.auth_type === 'telegram' || user.auth_type === 'email') && (
<button
onClick={() => setChangeEmailStep('email')}
className="text-sm text-accent-400 transition-colors hover:text-accent-300"
>
{t('profile.changeEmail.button')}
</button>
)}
<button
onClick={() => setChangeEmailStep('email')}
className="text-sm text-accent-400 transition-colors hover:text-accent-300"
>
{t('profile.changeEmail.button')}
</button>
</div>
</div>
)}
{user.email_verified &&
(user.auth_type === 'telegram' || user.auth_type === 'email') && (
<div className="flex items-center justify-between">
<p className="text-sm text-dark-400">{t('profile.canLoginWithEmail')}</p>
<button
onClick={() => setChangeEmailStep('email')}
className="flex items-center gap-2 text-sm text-accent-400 transition-colors hover:text-accent-300"
>
<PencilIcon />
<span>{t('profile.changeEmail.button')}</span>
</button>
</div>
)}
{user.email_verified && (
<div className="flex items-center justify-between">
<p className="text-sm text-dark-400">{t('profile.canLoginWithEmail')}</p>
<button
onClick={() => setChangeEmailStep('email')}
className="flex items-center gap-2 text-sm text-accent-400 transition-colors hover:text-accent-300"
>
<PencilIcon />
<span>{t('profile.changeEmail.button')}</span>
</button>
</div>
)}
{/* Inline email change flow */}
<AnimatePresence>