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

@@ -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>