From 3d950252b70b59a6a0f49976aeb87686e850d0e9 Mon Sep 17 00:00:00 2001 From: Fringg Date: Tue, 17 Mar 2026 21:46:12 +0300 Subject: [PATCH] fix: deep link auth timer cleanup and reliability - Clear previous interval/timeout before starting new deep link auth - Store expire timeout in ref for cleanup on unmount - Use server expires_in instead of hardcoded 5 minutes - Show error message when token expires (not just clear state) - Add consumeCampaignSlug/consumeReferralCode to loginWithDeepLink --- src/components/TelegramLoginButton.tsx | 40 +++++++++++++++++++++----- src/store/auth.ts | 2 ++ 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/components/TelegramLoginButton.tsx b/src/components/TelegramLoginButton.tsx index 4f385db..1e872d7 100644 --- a/src/components/TelegramLoginButton.tsx +++ b/src/components/TelegramLoginButton.tsx @@ -29,6 +29,7 @@ export default function TelegramLoginButton({ referralCode }: TelegramLoginButto const [deepLinkPolling, setDeepLinkPolling] = useState(false); const [deepLinkError, setDeepLinkError] = useState(''); const pollIntervalRef = useRef>(null); + const expireTimeoutRef = useRef>(null); const loginWithDeepLink = useAuthStore((s) => s.loginWithDeepLink); @@ -54,12 +55,15 @@ export default function TelegramLoginButton({ referralCode }: TelegramLoginButto }; }, []); - // Cleanup polling on unmount + // Cleanup polling and expire timeout on unmount useEffect(() => { return () => { if (pollIntervalRef.current) { clearInterval(pollIntervalRef.current); } + if (expireTimeoutRef.current) { + clearTimeout(expireTimeoutRef.current); + } }; }, []); @@ -223,8 +227,20 @@ export default function TelegramLoginButton({ referralCode }: TelegramLoginButto // Deep link auth: request token and start polling const startDeepLinkAuth = useCallback(async () => { setDeepLinkError(''); + + // Clear any previous timers + if (pollIntervalRef.current) { + clearInterval(pollIntervalRef.current); + pollIntervalRef.current = null; + } + if (expireTimeoutRef.current) { + clearTimeout(expireTimeoutRef.current); + expireTimeoutRef.current = null; + } + try { - const { token, bot_username } = await authApi.requestDeepLinkToken(); + const response = await authApi.requestDeepLinkToken(); + const { token, bot_username, expires_in } = response; setDeepLinkToken(token); setDeepLinkBotUsername(bot_username || botUsername); setDeepLinkPolling(true); @@ -239,6 +255,10 @@ export default function TelegramLoginButton({ referralCode }: TelegramLoginButto await loginWithDeepLink(token); // Success - auth store is updated, navigate clearInterval(intervalId); + if (expireTimeoutRef.current) { + clearTimeout(expireTimeoutRef.current); + expireTimeoutRef.current = null; + } if (mountedRef.current) { setDeepLinkPolling(false); navigate('/'); @@ -270,17 +290,23 @@ export default function TelegramLoginButton({ referralCode }: TelegramLoginButto pollIntervalRef.current = intervalId; - // Auto-expire after token TTL - setTimeout( + // Auto-expire after server-provided TTL + const expireId = setTimeout( () => { - clearInterval(intervalId); + if (pollIntervalRef.current) { + clearInterval(pollIntervalRef.current); + pollIntervalRef.current = null; + } if (mountedRef.current && !useAuthStore.getState().isAuthenticated) { setDeepLinkPolling(false); setDeepLinkToken(null); + setDeepLinkError(t('auth.deepLinkExpired')); } }, - 5 * 60 * 1000, - ); // 5 minutes + (expires_in || 300) * 1000, + ); + + expireTimeoutRef.current = expireId; } catch { setDeepLinkError(t('common.error')); } diff --git a/src/store/auth.ts b/src/store/auth.ts index 2ff82a8..dbfaffc 100644 --- a/src/store/auth.ts +++ b/src/store/auth.ts @@ -320,6 +320,8 @@ export const useAuthStore = create()( }, loginWithDeepLink: async (token) => { + consumeCampaignSlug(); + consumeReferralCode(); const response = await authApi.pollDeepLinkToken(token); tokenStorage.setTokens(response.access_token, response.refresh_token); set({