60 lines
2.0 KiB
TypeScript
60 lines
2.0 KiB
TypeScript
import { useEffect, useRef } from "react";
|
|
|
|
interface TelegramUser {
|
|
id: number;
|
|
first_name?: string;
|
|
last_name?: string;
|
|
username?: string;
|
|
photo_url?: string;
|
|
auth_date: number;
|
|
hash: string;
|
|
}
|
|
|
|
declare global {
|
|
interface Window {
|
|
__fsTelegramAuth?: (user: TelegramUser) => void;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Официальный Telegram Login Widget. Требует бота (@BotFather) и /setdomain
|
|
* на текущий HTTPS-домен. botUsername — без «@».
|
|
*/
|
|
export function TelegramLoginButton({
|
|
botUsername,
|
|
onAuth,
|
|
}: {
|
|
botUsername: string;
|
|
onAuth: (user: TelegramUser) => void;
|
|
}) {
|
|
const ref = useRef<HTMLDivElement>(null);
|
|
// onAuth держим в ref, чтобы он НЕ был в зависимостях эффекта: иначе новая инлайн-
|
|
// функция на каждый рендер пересоздавала бы виджет (а cleanup сносит его innerHTML
|
|
// в момент отрисовки — в dev со StrictMode кнопка из-за этого не появлялась).
|
|
const onAuthRef = useRef(onAuth);
|
|
onAuthRef.current = onAuth;
|
|
|
|
useEffect(() => {
|
|
window.__fsTelegramAuth = (user) => onAuthRef.current(user);
|
|
const node = ref.current;
|
|
if (!node) return;
|
|
node.innerHTML = ""; // на случай повторного монтирования (StrictMode)
|
|
|
|
const script = document.createElement("script");
|
|
script.src = "https://telegram.org/js/telegram-widget.js?22";
|
|
script.async = true;
|
|
script.setAttribute("data-telegram-login", botUsername);
|
|
script.setAttribute("data-size", "large");
|
|
script.setAttribute("data-userpic", "false");
|
|
script.setAttribute("data-request-access", "write");
|
|
script.setAttribute("data-onauth", "__fsTelegramAuth(user)");
|
|
node.appendChild(script);
|
|
|
|
return () => {
|
|
node.innerHTML = "";
|
|
};
|
|
}, [botUsername]); // только смена бота пересоздаёт виджет
|
|
|
|
return <div ref={ref} />;
|
|
}
|