From 4e1182449b81c5d8fdd8ba1ce0d9b263d43fa43a Mon Sep 17 00:00:00 2001 From: Egor Date: Sun, 18 Jan 2026 06:22:42 +0300 Subject: [PATCH] Add files via upload --- src/pages/Subscription.tsx | 9 ++++++--- src/pages/TelegramCallback.tsx | 14 +++++++++++--- src/pages/TelegramRedirect.tsx | 24 +++++++++++++++++++++++- 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/src/pages/Subscription.tsx b/src/pages/Subscription.tsx index 3c782c0..2d82d9d 100644 --- a/src/pages/Subscription.tsx +++ b/src/pages/Subscription.tsx @@ -301,18 +301,20 @@ export default function Subscription() { // Auto-scroll to switch tariff modal when it appears useEffect(() => { if (switchTariffId && switchModalRef.current) { - setTimeout(() => { + const timer = setTimeout(() => { switchModalRef.current?.scrollIntoView({ behavior: 'smooth', block: 'center' }) }, 100) + return () => clearTimeout(timer) } }, [switchTariffId]) // Auto-scroll to tariff purchase form when it appears useEffect(() => { if (showTariffPurchase && tariffPurchaseRef.current) { - setTimeout(() => { + const timer = setTimeout(() => { tariffPurchaseRef.current?.scrollIntoView({ behavior: 'smooth', block: 'center' }) }, 100) + return () => clearTimeout(timer) } }, [showTariffPurchase]) @@ -320,11 +322,12 @@ export default function Subscription() { useEffect(() => { const state = location.state as { scrollToExtend?: boolean } | null if (state?.scrollToExtend && tariffsCardRef.current) { - setTimeout(() => { + const timer = setTimeout(() => { tariffsCardRef.current?.scrollIntoView({ behavior: 'smooth', block: 'center' }) }, 300) // Clear the state to prevent re-scrolling on subsequent renders window.history.replaceState({}, document.title) + return () => clearTimeout(timer) } }, [location.state]) diff --git a/src/pages/TelegramCallback.tsx b/src/pages/TelegramCallback.tsx index 46905cb..5bb6a0f 100644 --- a/src/pages/TelegramCallback.tsx +++ b/src/pages/TelegramCallback.tsx @@ -32,19 +32,27 @@ export default function TelegramCallback() { return } + // Parse and validate numeric fields + const parsedId = parseInt(id, 10) + const parsedAuthDate = parseInt(authDate, 10) + + if (Number.isNaN(parsedId) || Number.isNaN(parsedAuthDate)) { + setError(t('auth.telegramRequired')) + return + } + try { await loginWithTelegramWidget({ - id: parseInt(id, 10), + id: parsedId, first_name: firstName, last_name: lastName || undefined, username: username || undefined, photo_url: photoUrl || undefined, - auth_date: parseInt(authDate, 10), + auth_date: parsedAuthDate, hash: hash, }) navigate('/') } catch (err: unknown) { - console.error('Telegram auth error:', err) const error = err as { response?: { data?: { detail?: string } } } setError(error.response?.data?.detail || t('common.error')) } diff --git a/src/pages/TelegramRedirect.tsx b/src/pages/TelegramRedirect.tsx index b01859b..3b52b1b 100644 --- a/src/pages/TelegramRedirect.tsx +++ b/src/pages/TelegramRedirect.tsx @@ -25,6 +25,9 @@ const getSafeRedirectUrl = (url: string | null): string => { return url } +const MAX_RETRY_ATTEMPTS = 3 +const RETRY_COUNT_KEY = 'telegram_redirect_retry_count' + export default function TelegramRedirect() { const { t } = useTranslation() const navigate = useNavigate() @@ -32,6 +35,10 @@ export default function TelegramRedirect() { const { loginWithTelegram, isAuthenticated, isLoading: authLoading } = useAuthStore() const [status, setStatus] = useState<'loading' | 'success' | 'error' | 'not-telegram'>('loading') const [errorMessage, setErrorMessage] = useState('') + const [retryCount, setRetryCount] = useState(() => { + const stored = sessionStorage.getItem(RETRY_COUNT_KEY) + return stored ? parseInt(stored, 10) : 0 + }) // Get branding for nice display const { data: branding } = useQuery({ @@ -96,13 +103,28 @@ export default function TelegramRedirect() { setTimeout(initTelegram, 300) }, [loginWithTelegram, navigate, isAuthenticated, authLoading, redirectTo, t]) - // Handle retry + // Handle retry with limit to prevent infinite loops const handleRetry = () => { + if (retryCount >= MAX_RETRY_ATTEMPTS) { + setErrorMessage('Превышено количество попыток. Попробуйте позже.') + sessionStorage.removeItem(RETRY_COUNT_KEY) + return + } + const newCount = retryCount + 1 + setRetryCount(newCount) + sessionStorage.setItem(RETRY_COUNT_KEY, String(newCount)) setStatus('loading') setErrorMessage('') window.location.reload() } + // Clear retry count on successful auth + useEffect(() => { + if (status === 'success') { + sessionStorage.removeItem(RETRY_COUNT_KEY) + } + }, [status]) + return (
{/* Background */}