import { useState } from "react"; import { Navigate, useNavigate } from "react-router-dom"; import { ApiError } from "../api/client"; import { Spinner } from "../components/Spinner"; import { TelegramLoginButton } from "../components/TelegramLoginButton"; import { useAuthConfig, useCreateDevUser, useDevUsers, useMe, useStubLogin, useTelegramLogin, useTelegramRegister, } from "../hooks/auth"; // Данные виджета Telegram, отложенные до выбора ника (когда тег занят). interface PendingTelegram { payload: Record; suggested: string; } export function LoginPage() { const { data: me, isLoading } = useMe(); const { data: config } = useAuthConfig(); const navigate = useNavigate(); const [error, setError] = useState(null); const [pendingTg, setPendingTg] = useState(null); const methods = config?.methods ?? []; const hasTelegram = methods.includes("telegram"); // Stub-вход показываем только в dev-сборке И если бэкенд его отдаёт (в проде // import.meta.env.DEV === false → блок вырезается из бандла). const showStub = import.meta.env.DEV && methods.includes("stub"); const telegram = useTelegramLogin(); if (isLoading) return ; if (me) return ; const onTelegram = async (user: Record) => { setError(null); try { await telegram.mutateAsync(user); navigate("/", { replace: true }); } catch (e) { // Тег занят/некорректен → просим выбрать ник и регистрируем тем же payload. if (e instanceof ApiError && e.code === "TELEGRAM_NICKNAME_REQUIRED") { const suggested = (e.details as { suggested?: string } | undefined)?.suggested ?? ""; setPendingTg({ payload: user, suggested }); return; } setError(e instanceof ApiError ? e.message : "Не удалось войти через Telegram"); } }; return (

Forbidden Stars

Учёт партий

{pendingTg ? ( { setPendingTg(null); setError(null); }} onDone={() => navigate("/", { replace: true })} /> ) : ( hasTelegram && (

Вход через Telegram

{config?.telegram_bot_username ? ( onTelegram(u as unknown as Record)} /> ) : (

Telegram не настроен — укажите TELEGRAM_BOT_USERNAME и TELEGRAM_BOT_TOKEN в .env (бот от @BotFather + /setdomain на ваш домен).

)}
) )} {showStub && !pendingTg && } {error &&

{error}

}
); } // Выбор ника при регистрации через Telegram, когда тег занят/некорректен. // Повторяет регистрацию тем же payload виджета (подпись на сервере проверяется заново). function TelegramNicknamePicker({ pending, onCancel, onDone, }: { pending: PendingTelegram; onCancel: () => void; onDone: () => void; }) { const register = useTelegramRegister(); const [nickname, setNickname] = useState(pending.suggested); const [error, setError] = useState( pending.suggested ? `Ник «${pending.suggested}» уже занят — выберите другой.` : null, ); const submit = async () => { const nick = nickname.trim(); if (!nick) { setError("Введите никнейм."); return; } setError(null); try { await register.mutateAsync({ ...pending.payload, nickname: nick }); onDone(); } catch (e) { setError(e instanceof ApiError ? e.message : "Не удалось зарегистрироваться"); } }; return (

Выберите никнейм

Ваш ник в Telegram уже занят другим игроком. Укажите другой никнейм для регистрации.

setNickname(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") submit(); }} />
{error &&

{error}

}
); } // DEV-вход по нику. Вынесен в отдельный компонент; импортируется только в dev-ветке // LoginPage, поэтому в прод-сборке (import.meta.env.DEV=false) удаляется тришейкингом. function DevLogin({ onError }: { onError: (msg: string | null) => void }) { const { data: devUsers } = useDevUsers(true); const stubLogin = useStubLogin(); const createUser = useCreateDevUser(); const navigate = useNavigate(); const [nickname, setNickname] = useState(""); const doLogin = async (nick: string) => { onError(null); try { await stubLogin.mutateAsync(nick); navigate("/", { replace: true }); } catch (e) { onError(e instanceof ApiError ? e.message : "Не удалось войти"); } }; const onCreate = async () => { const nick = nickname.trim(); if (!nick) return; try { await createUser.mutateAsync(nick); await doLogin(nick); } catch (e) { onError(e instanceof ApiError ? e.message : "Ошибка"); } }; return (

Тестовый вход (dev)

setNickname(e.target.value)} />
{devUsers && devUsers.length > 0 && ( <>
Или выберите существующего:
{devUsers.map((u) => ( ))}
)}
); }