mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-28 09:33:46 +00:00
feat: migrate Telegram Login Widget to v23 with admin-configurable settings
- Upgrade telegram-widget.js from v22 to v23 (TelegramLoginButton + ConnectedAccounts) - TelegramLoginButton fetches widget config from API instead of hardcoded values - Add TelegramWidgetConfig type and API method with fallback to env defaults - Remove botUsername prop — now sourced from backend API - Add useEffect cleanup to prevent orphaned iframes on unmount - Add TELEGRAM_WIDGET category to admin interface settings menu - Add i18n translations for widget settings (ru, en, zh, fa)
This commit is contained in:
@@ -23,6 +23,14 @@ export interface EmailAuthEnabled {
|
|||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TelegramWidgetConfig {
|
||||||
|
bot_username: string;
|
||||||
|
size: 'large' | 'medium' | 'small';
|
||||||
|
radius: number;
|
||||||
|
userpic: boolean;
|
||||||
|
request_access: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface AnalyticsCounters {
|
export interface AnalyticsCounters {
|
||||||
yandex_metrika_id: string;
|
yandex_metrika_id: string;
|
||||||
google_ads_id: string;
|
google_ads_id: string;
|
||||||
@@ -251,6 +259,24 @@ export const brandingApi = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Get Telegram widget config (public, no auth required)
|
||||||
|
getTelegramWidgetConfig: async (): Promise<TelegramWidgetConfig> => {
|
||||||
|
try {
|
||||||
|
const response = await apiClient.get<TelegramWidgetConfig>(
|
||||||
|
'/cabinet/branding/telegram-widget',
|
||||||
|
);
|
||||||
|
return response.data;
|
||||||
|
} catch {
|
||||||
|
return {
|
||||||
|
bot_username: import.meta.env.VITE_TELEGRAM_BOT_USERNAME || '',
|
||||||
|
size: 'large',
|
||||||
|
radius: 8,
|
||||||
|
userpic: true,
|
||||||
|
request_access: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// Update analytics counters (admin only)
|
// Update analytics counters (admin only)
|
||||||
updateAnalyticsCounters: async (data: Partial<AnalyticsCounters>): Promise<AnalyticsCounters> => {
|
updateAnalyticsCounters: async (data: Partial<AnalyticsCounters>): Promise<AnalyticsCounters> => {
|
||||||
const response = await apiClient.patch<AnalyticsCounters>('/cabinet/branding/analytics', data);
|
const response = await apiClient.patch<AnalyticsCounters>('/cabinet/branding/analytics', data);
|
||||||
|
|||||||
@@ -1,42 +1,58 @@
|
|||||||
import { useEffect, useRef } from 'react';
|
import { useEffect, useRef } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import { brandingApi, type TelegramWidgetConfig } from '../api/branding';
|
||||||
|
|
||||||
interface TelegramLoginButtonProps {
|
interface TelegramLoginButtonProps {
|
||||||
botUsername: string;
|
|
||||||
referralCode?: string;
|
referralCode?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function TelegramLoginButton({
|
export default function TelegramLoginButton({ referralCode }: TelegramLoginButtonProps) {
|
||||||
botUsername,
|
|
||||||
referralCode,
|
|
||||||
}: TelegramLoginButtonProps) {
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const { data: widgetConfig } = useQuery<TelegramWidgetConfig>({
|
||||||
|
queryKey: ['telegram-widget-config'],
|
||||||
|
queryFn: brandingApi.getTelegramWidgetConfig,
|
||||||
|
staleTime: 60000,
|
||||||
|
});
|
||||||
|
|
||||||
|
const botUsername =
|
||||||
|
widgetConfig?.bot_username || import.meta.env.VITE_TELEGRAM_BOT_USERNAME || '';
|
||||||
|
|
||||||
// Load widget script
|
// Load widget script
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!containerRef.current || !botUsername) return;
|
if (!containerRef.current || !botUsername || !widgetConfig) return;
|
||||||
|
|
||||||
// Clear previous widget using safe DOM API
|
const container = containerRef.current;
|
||||||
while (containerRef.current.firstChild) {
|
|
||||||
containerRef.current.removeChild(containerRef.current.firstChild);
|
// Clear previous widget
|
||||||
|
while (container.firstChild) {
|
||||||
|
container.removeChild(container.firstChild);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get current URL for redirect
|
|
||||||
const redirectUrl = `${window.location.origin}/auth/telegram/callback`;
|
const redirectUrl = `${window.location.origin}/auth/telegram/callback`;
|
||||||
|
|
||||||
// Create script element for Telegram Login Widget
|
|
||||||
const script = document.createElement('script');
|
const script = document.createElement('script');
|
||||||
script.src = 'https://telegram.org/js/telegram-widget.js?22';
|
script.src = 'https://telegram.org/js/telegram-widget.js?23';
|
||||||
script.setAttribute('data-telegram-login', botUsername);
|
script.setAttribute('data-telegram-login', botUsername);
|
||||||
script.setAttribute('data-size', 'large');
|
script.setAttribute('data-size', widgetConfig.size);
|
||||||
script.setAttribute('data-radius', '8');
|
script.setAttribute('data-radius', String(widgetConfig.radius));
|
||||||
|
script.setAttribute('data-userpic', String(widgetConfig.userpic));
|
||||||
script.setAttribute('data-auth-url', redirectUrl);
|
script.setAttribute('data-auth-url', redirectUrl);
|
||||||
script.setAttribute('data-request-access', 'write');
|
if (widgetConfig.request_access) {
|
||||||
|
script.setAttribute('data-request-access', 'write');
|
||||||
|
}
|
||||||
script.async = true;
|
script.async = true;
|
||||||
|
|
||||||
containerRef.current.appendChild(script);
|
container.appendChild(script);
|
||||||
}, [botUsername]);
|
|
||||||
|
return () => {
|
||||||
|
while (container.firstChild) {
|
||||||
|
container.removeChild(container.firstChild);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [botUsername, widgetConfig]);
|
||||||
|
|
||||||
if (!botUsername || botUsername === 'your_bot') {
|
if (!botUsername || botUsername === 'your_bot') {
|
||||||
return (
|
return (
|
||||||
@@ -48,10 +64,8 @@ export default function TelegramLoginButton({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center space-y-4">
|
<div className="flex flex-col items-center space-y-4">
|
||||||
{/* Telegram Widget will be inserted here */}
|
|
||||||
<div ref={containerRef} className="flex justify-center" />
|
<div ref={containerRef} className="flex justify-center" />
|
||||||
|
|
||||||
{/* Fallback link for mobile */}
|
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<p className="mb-2 text-xs text-gray-500">{t('auth.orOpenInApp')}</p>
|
<p className="mb-2 text-xs text-gray-500">{t('auth.orOpenInApp')}</p>
|
||||||
<a
|
<a
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ export const MENU_SECTIONS: MenuSection[] = [
|
|||||||
'INTERFACE_SUBSCRIPTION',
|
'INTERFACE_SUBSCRIPTION',
|
||||||
'CONNECT_BUTTON',
|
'CONNECT_BUTTON',
|
||||||
'MINIAPP',
|
'MINIAPP',
|
||||||
|
'TELEGRAM_WIDGET',
|
||||||
'HAPP',
|
'HAPP',
|
||||||
'SKIP',
|
'SKIP',
|
||||||
'ADDITIONAL',
|
'ADDITIONAL',
|
||||||
|
|||||||
@@ -1681,7 +1681,16 @@
|
|||||||
"projectName": "Project name",
|
"projectName": "Project name",
|
||||||
"quickPresets": "Quick presets",
|
"quickPresets": "Quick presets",
|
||||||
"resetAllColors": "Reset all colors",
|
"resetAllColors": "Reset all colors",
|
||||||
"statusColors": "Status colors"
|
"statusColors": "Status colors",
|
||||||
|
"categories": {
|
||||||
|
"TELEGRAM_WIDGET": "Telegram Login Widget"
|
||||||
|
},
|
||||||
|
"settingNames": {
|
||||||
|
"Telegram Widget Size": "Widget Size",
|
||||||
|
"Telegram Widget Radius": "Corner Radius",
|
||||||
|
"Telegram Widget Userpic": "Show User Photo",
|
||||||
|
"Telegram Widget Request Access": "Request Access"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"theme": {
|
"theme": {
|
||||||
"accentColor": "Accent color",
|
"accentColor": "Accent color",
|
||||||
|
|||||||
@@ -1341,7 +1341,16 @@
|
|||||||
"projectName": "نام پروژه",
|
"projectName": "نام پروژه",
|
||||||
"quickPresets": "پیشتنظیمهای سریع",
|
"quickPresets": "پیشتنظیمهای سریع",
|
||||||
"resetAllColors": "بازنشانی همه رنگها",
|
"resetAllColors": "بازنشانی همه رنگها",
|
||||||
"statusColors": "رنگهای وضعیت"
|
"statusColors": "رنگهای وضعیت",
|
||||||
|
"categories": {
|
||||||
|
"TELEGRAM_WIDGET": "Telegram Login Widget"
|
||||||
|
},
|
||||||
|
"settingNames": {
|
||||||
|
"Telegram Widget Size": "اندازه ویجت",
|
||||||
|
"Telegram Widget Radius": "شعاع گوشه",
|
||||||
|
"Telegram Widget Userpic": "نمایش تصویر کاربر",
|
||||||
|
"Telegram Widget Request Access": "درخواست دسترسی"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"color": "رنگ دکمه",
|
"color": "رنگ دکمه",
|
||||||
|
|||||||
@@ -1762,6 +1762,10 @@
|
|||||||
"Subscription Show Devices": "Показывать устройства",
|
"Subscription Show Devices": "Показывать устройства",
|
||||||
"Telegram Stars Enabled": "Stars включены",
|
"Telegram Stars Enabled": "Stars включены",
|
||||||
"Telegram Stars Rate Rub": "Курс Stars (₽)",
|
"Telegram Stars Rate Rub": "Курс Stars (₽)",
|
||||||
|
"Telegram Widget Size": "Размер виджета",
|
||||||
|
"Telegram Widget Radius": "Скругление углов",
|
||||||
|
"Telegram Widget Userpic": "Показывать аватар",
|
||||||
|
"Telegram Widget Request Access": "Запрос доступа",
|
||||||
"Tribute Api Key": "API ключ",
|
"Tribute Api Key": "API ключ",
|
||||||
"Tribute Donate Link": "Ссылка доната",
|
"Tribute Donate Link": "Ссылка доната",
|
||||||
"Tribute Enabled": "Tribute включен",
|
"Tribute Enabled": "Tribute включен",
|
||||||
@@ -2164,6 +2168,7 @@
|
|||||||
"PAL24": "PAL24",
|
"PAL24": "PAL24",
|
||||||
"WATA": "Wata",
|
"WATA": "Wata",
|
||||||
"TELEGRAM": "Telegram",
|
"TELEGRAM": "Telegram",
|
||||||
|
"TELEGRAM_WIDGET": "Telegram Login Widget",
|
||||||
"SUBSCRIPTIONS_CORE": "Основные",
|
"SUBSCRIPTIONS_CORE": "Основные",
|
||||||
"SIMPLE_SUBSCRIPTION": "Простая подписка",
|
"SIMPLE_SUBSCRIPTION": "Простая подписка",
|
||||||
"PERIODS": "Периоды",
|
"PERIODS": "Периоды",
|
||||||
|
|||||||
@@ -1379,7 +1379,16 @@
|
|||||||
"projectName": "项目名称",
|
"projectName": "项目名称",
|
||||||
"quickPresets": "快速预设",
|
"quickPresets": "快速预设",
|
||||||
"resetAllColors": "重置所有颜色",
|
"resetAllColors": "重置所有颜色",
|
||||||
"statusColors": "状态颜色"
|
"statusColors": "状态颜色",
|
||||||
|
"categories": {
|
||||||
|
"TELEGRAM_WIDGET": "Telegram Login Widget"
|
||||||
|
},
|
||||||
|
"settingNames": {
|
||||||
|
"Telegram Widget Size": "小部件大小",
|
||||||
|
"Telegram Widget Radius": "圆角半径",
|
||||||
|
"Telegram Widget Userpic": "显示头像",
|
||||||
|
"Telegram Widget Request Access": "请求访问权限"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"buttons": {
|
"buttons": {
|
||||||
"color": "按钮颜色",
|
"color": "按钮颜色",
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ function TelegramLinkWidget() {
|
|||||||
const redirectUrl = `${window.location.origin}/auth/link/telegram/callback?csrf_state=${encodeURIComponent(csrfState)}`;
|
const redirectUrl = `${window.location.origin}/auth/link/telegram/callback?csrf_state=${encodeURIComponent(csrfState)}`;
|
||||||
|
|
||||||
const script = document.createElement('script');
|
const script = document.createElement('script');
|
||||||
script.src = 'https://telegram.org/js/telegram-widget.js?22';
|
script.src = 'https://telegram.org/js/telegram-widget.js?23';
|
||||||
script.setAttribute('data-telegram-login', botUsername);
|
script.setAttribute('data-telegram-login', botUsername);
|
||||||
script.setAttribute('data-size', 'small');
|
script.setAttribute('data-size', 'small');
|
||||||
script.setAttribute('data-radius', '8');
|
script.setAttribute('data-radius', '8');
|
||||||
|
|||||||
@@ -485,10 +485,7 @@ export default function Login() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<TelegramLoginButton
|
<TelegramLoginButton referralCode={referralCode || undefined} />
|
||||||
botUsername={botUsername}
|
|
||||||
referralCode={referralCode || undefined}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user