Вход через Telegram: код из бота вместо сломанного deep-link
Deep-link авторизация в Telegram-виджете не работала (не было отдельной кнопки для завершения флоу). Заменяет на код-флоу: бот выдаёт разовый код, пользователь вводит его в Login.tsx/LoginButton.tsx, useAuthStore дергает loginTelegramCode(). api.ts вместо startTelegramLogin/TelegramLoginSession получает getBotUsername() (кэширует bot_username из GET /api/v1/config) для кнопки "Открыть бота". Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { useAuthStore } from "@/store/useAuthStore";
|
||||
import { getBotUsername } from "@/lib/api";
|
||||
|
||||
// Кнопка входа по seed/Telegram на экране VPN. Панель — не полноэкранный
|
||||
// Dialog (его затемнение перекрывает весь вьюпорт, включая хедер, и блокирует
|
||||
@@ -20,27 +21,26 @@ export function LoginButton() {
|
||||
const { t } = useTranslation();
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [seedInput, setSeedInput] = useState("");
|
||||
const [codeInput, setCodeInput] = useState("");
|
||||
const [copied, setCopied] = useState(false);
|
||||
const [botUsername, setBotUsername] = useState<string | null>(null);
|
||||
|
||||
const seed = useAuthStore((s) => s.seed);
|
||||
const seedLoading = useAuthStore((s) => s.seedLoading);
|
||||
const seedError = useAuthStore((s) => s.seedError);
|
||||
const loadSeed = useAuthStore((s) => s.loadSeed);
|
||||
const loginSeed = useAuthStore((s) => s.loginSeed);
|
||||
const telegramDeepLink = useAuthStore((s) => s.telegramDeepLink);
|
||||
const telegramStatus = useAuthStore((s) => s.telegramStatus);
|
||||
const startTelegram = useAuthStore((s) => s.startTelegram);
|
||||
const cancelTelegram = useAuthStore((s) => s.cancelTelegram);
|
||||
const telegramCodeLoading = useAuthStore((s) => s.telegramCodeLoading);
|
||||
const telegramCodeError = useAuthStore((s) => s.telegramCodeError);
|
||||
const loginTelegramCode = useAuthStore((s) => s.loginTelegramCode);
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) loadSeed();
|
||||
}, [isOpen, loadSeed]);
|
||||
|
||||
useEffect(() => {
|
||||
if (telegramDeepLink) {
|
||||
open(telegramDeepLink).catch(() => {});
|
||||
}
|
||||
}, [telegramDeepLink]);
|
||||
getBotUsername().then(setBotUsername);
|
||||
}, []);
|
||||
|
||||
const handleCopy = async () => {
|
||||
if (!seed) return;
|
||||
@@ -55,11 +55,21 @@ export function LoginButton() {
|
||||
await loginSeed(seedInput.trim());
|
||||
};
|
||||
|
||||
const handleTelegramSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!codeInput.trim()) return;
|
||||
await loginTelegramCode(codeInput.trim());
|
||||
};
|
||||
|
||||
const handleOpenBot = () => {
|
||||
if (botUsername) open(`https://t.me/${botUsername}`).catch(() => {});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
onClick={() => setIsOpen(true)}
|
||||
className="absolute top-3 right-3 z-10 flex items-center gap-1.5 rounded-full border border-white/10 bg-background/30 px-3 py-1.5 text-xs font-medium text-foreground/80 backdrop-blur-md transition-colors hover:border-white/20 hover:text-foreground"
|
||||
className="absolute top-3 end-3 z-10 flex items-center gap-1.5 rounded-full border border-white/10 bg-background/30 px-3 py-1.5 text-xs font-medium text-foreground/80 backdrop-blur-md transition-colors hover:border-white/20 hover:text-foreground"
|
||||
>
|
||||
<IconUserCircle className="size-4" />
|
||||
{t("login_button")}
|
||||
@@ -124,33 +134,35 @@ export function LoginButton() {
|
||||
<TabsContent value="telegram" className="mt-4 flex flex-col gap-3">
|
||||
<p className="text-sm text-muted-foreground">{t("login_telegram_desc")}</p>
|
||||
|
||||
{telegramStatus === "idle" && (
|
||||
<Button type="button" onClick={startTelegram} className="gap-2">
|
||||
<IconBrandTelegram className="size-4" />
|
||||
{t("login_telegram_start")}
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
onClick={handleOpenBot}
|
||||
disabled={!botUsername}
|
||||
className="gap-2"
|
||||
>
|
||||
<IconBrandTelegram className="size-4" />
|
||||
{t("login_telegram_open_bot")}
|
||||
</Button>
|
||||
|
||||
<form onSubmit={handleTelegramSubmit} className="flex flex-col gap-2">
|
||||
<label className="text-xs font-bold uppercase tracking-widest text-muted-foreground">
|
||||
{t("login_telegram_code_label")}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={codeInput}
|
||||
onChange={(e) => setCodeInput(e.target.value)}
|
||||
placeholder={t("login_telegram_code_placeholder")}
|
||||
className="w-full rounded-xl border border-white/10 bg-white/5 p-3 text-sm text-foreground outline-none transition-colors focus:border-primary"
|
||||
/>
|
||||
{telegramCodeError && (
|
||||
<p className="text-xs text-destructive">{t("login_telegram_error")}</p>
|
||||
)}
|
||||
<Button type="submit" disabled={telegramCodeLoading}>
|
||||
{t("login_telegram_submit")}
|
||||
</Button>
|
||||
)}
|
||||
{telegramStatus === "waiting" && (
|
||||
<div className="flex flex-col gap-2">
|
||||
<p className="animate-pulse text-sm text-muted-foreground">
|
||||
{t("login_telegram_waiting")}
|
||||
</p>
|
||||
<Button type="button" variant="outline" onClick={cancelTelegram}>
|
||||
{t("login_telegram_cancel")}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
{telegramStatus === "success" && (
|
||||
<p className="text-sm font-medium text-primary">{t("login_telegram_success")}</p>
|
||||
)}
|
||||
{telegramStatus === "error" && (
|
||||
<div className="flex flex-col gap-2">
|
||||
<p className="text-sm text-destructive">{t("login_telegram_error")}</p>
|
||||
<Button type="button" onClick={startTelegram}>
|
||||
{t("login_telegram_start")}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</form>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user