diff --git a/src/components/TelegramLoginButton.tsx b/src/components/TelegramLoginButton.tsx index 15dc748..9cc0c59 100644 --- a/src/components/TelegramLoginButton.tsx +++ b/src/components/TelegramLoginButton.tsx @@ -26,6 +26,10 @@ export default function TelegramLoginButton({ referralCode }: TelegramLoginButto const [oidcError, setOidcError] = useState(''); const [scriptLoaded, setScriptLoaded] = useState(false); const [scriptFailed, setScriptFailed] = useState(false); + // Lets the user opt into deep-link auth manually, without waiting for the + // Telegram widget script to fail. See #. + const [manualDeepLink, setManualDeepLink] = useState(false); + const showDeepLinkUI = scriptFailed || manualDeepLink; const loginWithTelegramOIDC = useAuthStore((s) => s.loginWithTelegramOIDC); // Deep link auth state @@ -168,7 +172,12 @@ export default function TelegramLoginButton({ referralCode }: TelegramLoginButto const loginWithTelegramWidget = useAuthStore((s) => s.loginWithTelegramWidget); useEffect(() => { - if (isOIDC || !containerRef.current || !botUsername || !widgetConfig) return; + // showDeepLinkUI обязан быть в зависимостях: пока он true, контейнер + // виджета размонтирован, а при возврате «Назад к виджету» сам по себе + // эффект не перезапустится — на legacy-пути scriptLoaded не меняется + // никогда, поэтому ни одна из остальных зависимостей не дрогнет, и + // пользователь получил бы пустое место вместо виджета. + if (showDeepLinkUI || isOIDC || !containerRef.current || !botUsername || !widgetConfig) return; const container = containerRef.current; while (container.firstChild) { @@ -229,7 +238,15 @@ export default function TelegramLoginButton({ referralCode }: TelegramLoginButto container.removeChild(container.firstChild); } }; - }, [isOIDC, botUsername, widgetConfig, loginWithTelegramWidget, navigate, handleScriptFailed]); + }, [ + showDeepLinkUI, + isOIDC, + botUsername, + widgetConfig, + loginWithTelegramWidget, + navigate, + handleScriptFailed, + ]); // Deep link auth: request token and start polling with recursive setTimeout const startDeepLinkAuth = useCallback(async () => { @@ -336,9 +353,10 @@ export default function TelegramLoginButton({ referralCode }: TelegramLoginButto } }, [botUsername, loginWithDeepLink, navigate, t]); - // Auto-start deep link auth when script fails (with cancellation for Strict Mode) + // Auto-start deep link auth when script fails OR the user opts in manually + // (with cancellation for Strict Mode) useEffect(() => { - if (scriptFailed && !deepLinkToken && !deepLinkPolling) { + if (showDeepLinkUI && !deepLinkToken && !deepLinkPolling) { let cancelled = false; const start = async () => { if (!cancelled) await startDeepLinkAuth(); @@ -348,7 +366,7 @@ export default function TelegramLoginButton({ referralCode }: TelegramLoginButto cancelled = true; }; } - }, [scriptFailed, deepLinkToken, deepLinkPolling, startDeepLinkAuth]); + }, [showDeepLinkUI, deepLinkToken, deepLinkPolling, startDeepLinkAuth]); // Resume polling immediately when user returns to the page (e.g. after confirming in Telegram) // Browsers throttle setTimeout in background tabs, so polling may have stalled. @@ -431,8 +449,9 @@ export default function TelegramLoginButton({ referralCode }: TelegramLoginButto ); } - // Deep link fallback UI - if (scriptFailed) { + // Deep link UI — shown either as an automatic fallback (widget script + // failed to load) or because the user explicitly chose this method. + if (showDeepLinkUI) { const resolvedBotUsername = deepLinkBotUsername || botUsername; const deepLinkUrl = deepLinkToken ? `https://t.me/${resolvedBotUsername}?start=webauth_${deepLinkToken}` @@ -443,7 +462,7 @@ export default function TelegramLoginButton({ referralCode }: TelegramLoginButto
{/* Info message */}

- {t('auth.telegramWidgetBlocked')} + {t(scriptFailed ? 'auth.telegramWidgetBlocked' : 'auth.deepLinkIntro')}

{deepLinkToken && deepLinkUrl ? ( @@ -517,6 +536,27 @@ export default function TelegramLoginButton({ referralCode }: TelegramLoginButto {t('common.loading')}
)} + + {/* Only offer a way back if the widget actually works — if the + script failed there is nothing to go back to. */} + {!scriptFailed && ( + + )} ); } @@ -551,24 +591,41 @@ export default function TelegramLoginButton({ referralCode }: TelegramLoginButto
)} -
-

{t('auth.orOpenInApp')}

+ {/* Referral deep link — only relevant for not-yet-registered users who + arrived via a referral link; the bot itself handles registering + them with the code attached. Hidden otherwise to avoid a third, + visually-identical "Telegram" entry point next to the two auth + methods below. */} + {referralCode && ( - - - - @{botUsername} + {t('auth.orOpenInApp')} @{botUsername} + )} + +
+
+ {t('common.or')} +
+ + {/* Manual opt-in: same deep-link flow used as the anti-block fallback, + offered here as an explicit equal alternative to the widget for + users who'd rather confirm in the bot than type a phone number. */} +
); } diff --git a/src/locales/en.json b/src/locales/en.json index 1c6898a..4520e2d 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -207,6 +207,9 @@ "orOpenInApp": "Or open the bot in the app", "loginFailed": "Login Failed", "telegramWidgetBlocked": "Telegram login widget is unavailable. Use the bot to sign in:", + "deepLinkIntro": "Confirm sign-in right in the bot — no phone number needed:", + "loginWithBot": "Login via bot", + "backToWidget": "Back to widget login", "openBotToLogin": "Open bot to sign in", "waitingForConfirmation": "Waiting for confirmation...", "deepLinkExpired": "Link expired. Please try again.", diff --git a/src/locales/fa.json b/src/locales/fa.json index fe8a782..f135511 100644 --- a/src/locales/fa.json +++ b/src/locales/fa.json @@ -199,6 +199,9 @@ "orOpenInApp": "یا ربات را در برنامه باز کنید", "loginFailed": "ورود ناموفق", "telegramWidgetBlocked": "ویجت ورود تلگرام در دسترس نیست. از طریق ربات وارد شوید:", + "deepLinkIntro": "ورود را مستقیماً در ربات تأیید کنید — بدون نیاز به شماره تلفن:", + "loginWithBot": "ورود از طریق ربات", + "backToWidget": "بازگشت به ورود با ویجت", "openBotToLogin": "باز کردن ربات برای ورود", "waitingForConfirmation": "در انتظار تایید...", "deepLinkExpired": "لینک منقضی شده است. لطفا دوباره تلاش کنید.", diff --git a/src/locales/ru.json b/src/locales/ru.json index 5b358d8..a54c5ad 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -210,6 +210,9 @@ "orOpenInApp": "Или откройте бота в приложении", "loginFailed": "Ошибка входа", "telegramWidgetBlocked": "Виджет входа через Telegram недоступен. Войдите через бота:", + "deepLinkIntro": "Подтвердите вход прямо в боте — без ввода номера телефона:", + "loginWithBot": "Войти через бота", + "backToWidget": "Назад к входу через виджет", "openBotToLogin": "Открыть бота для входа", "waitingForConfirmation": "Ожидание подтверждения...", "deepLinkExpired": "Ссылка истекла. Попробуйте снова.", diff --git a/src/locales/zh.json b/src/locales/zh.json index 87b3dce..754fb98 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -199,6 +199,9 @@ "orOpenInApp": "或在应用中打开机器人", "loginFailed": "登录失败", "telegramWidgetBlocked": "Telegram登录小部件不可用。请使用机器人登录:", + "deepLinkIntro": "直接在机器人中确认登录 — 无需手机号:", + "loginWithBot": "通过机器人登录", + "backToWidget": "返回小部件登录", "openBotToLogin": "打开机器人登录", "waitingForConfirmation": "等待确认...", "deepLinkExpired": "链接已过期,请重试。",