refactor: migrate to @telegram-apps/sdk-react v3, remove all window.Telegram.WebApp usage

Полный переход с нативного window.Telegram.WebApp API на @telegram-apps/sdk-react v3.
Bridge SDK оборачивал receiveEvent, из-за чего popup_closed не доходил до нативного
обработчика — это было основной причиной бага с двойным открытием попапа.

Изменения:

- Удалён @telegram-apps/react-router-integration (привязан к SDK v1)
- Инициализация SDK v3 в main.tsx: init(), restoreInitData(), mount всех компонентов
- AppWithNavigator: убран TelegramRouter (initNavigator + useIntegration), все платформы
  на BrowserRouter, добавлен TelegramBackButton через SDK v3
- useTelegramSDK: детекция через retrieveLaunchParams(), реактивные значения через
  useSignal() (fullscreen, viewport, safe area insets), initData через retrieveRawInitData()
- TelegramAdapter: полная перезапись — попапы через showPopup() (Promise-based, без
  ручного onEvent/offEvent/timeout), кнопки через setMainButtonParams/showBackButton,
  haptic через hapticFeedbackImpactOccurred, тема через themeParamsState с конвертацией
  camelCase→snake_case, cloud storage через getCloudStorageItem/setCloudStorageItem
- api/client.ts: initData через retrieveRawInitData() вместо window.Telegram.WebApp.initData
- AppHeader: фото пользователя через initDataUser() сигнал
- ConnectionModal: openLink через SDK вместо window.Telegram.WebApp.openLink
- ColorPicker: детекция Telegram через isInTelegramWebApp()
- Удалён интерфейс TelegramWebApp (368 строк) и Window augmentation из vite-env.d.ts
- Убрано поле telegram из PlatformContext в types.ts
- Обновлён manualChunks в vite.config.ts
This commit is contained in:
c0mrade
2026-02-04 11:06:59 +03:00
parent 2d00a5c21f
commit 023c3ef6b3
17 changed files with 497 additions and 873 deletions

View File

@@ -1,102 +1,76 @@
import { useEffect, useMemo } from 'react';
import { BrowserRouter, Router } from 'react-router-dom';
import { useIntegration } from '@telegram-apps/react-router-integration';
import { initNavigator } from '@telegram-apps/sdk';
import { useEffect } from 'react';
import { BrowserRouter, useLocation, useNavigate } from 'react-router-dom';
import {
showBackButton,
hideBackButton,
onBackButtonClick,
offBackButtonClick,
} from '@telegram-apps/sdk-react';
import App from './App';
import { PlatformProvider } from './platform/PlatformProvider';
import { ThemeColorsProvider } from './providers/ThemeColorsProvider';
import { WebSocketProvider } from './providers/WebSocketProvider';
import { ToastProvider } from './components/Toast';
import { TooltipProvider } from './components/primitives/Tooltip';
import { isInTelegramWebApp } from './hooks/useTelegramSDK';
/**
* Check if running inside Telegram Mini App
* Uses multiple checks to reliably detect Telegram environment
* Manages Telegram BackButton visibility based on navigation location.
* Shows back button on non-root routes, hides on root.
*/
function isTelegramMiniApp(): boolean {
if (typeof window === 'undefined') return false;
const webApp = window.Telegram?.WebApp;
if (!webApp) return false;
// Check 1: initDataUnsafe should have user data in real Telegram
const hasUserData = webApp.initDataUnsafe?.user?.id !== undefined;
// Check 2: Platform should not be 'unknown' (which is default in browser)
const validPlatform = webApp.platform !== 'unknown' && webApp.platform !== '';
// Check 3: Version should be present (SDK loads in Telegram only)
const hasVersion = webApp.version !== undefined && webApp.version !== '';
return hasUserData || (validPlatform && hasVersion);
}
/**
* Component wrapper for Telegram navigator setup.
* Integrates Telegram Mini Apps navigator with React Router to provide
* automatic BackButton management based on navigation history.
* Falls back to BrowserRouter when not in Telegram Mini App.
*/
/**
* Navigator-based router for Telegram Mini App
*/
function TelegramRouter({ children }: { children: React.ReactNode }) {
const navigator = useMemo(() => initNavigator('app-navigation-state', { hashMode: null }), []);
const [location, reactNavigator] = useIntegration(navigator);
function TelegramBackButton() {
const location = useLocation();
const navigate = useNavigate();
useEffect(() => {
const isRoot = location.pathname === '/' || location.pathname === '';
try {
navigator.attach();
return () => {
try {
navigator.detach();
} catch (err) {
console.warn('Failed to detach navigator:', err);
}
};
} catch (err) {
console.warn('Failed to attach navigator:', err);
if (isRoot) {
hideBackButton();
} else {
showBackButton();
}
} catch {
// Not in Telegram or back button not mounted
}
}, [navigator]);
}, [location]);
return (
<Router location={location} navigator={reactNavigator}>
{children}
</Router>
);
useEffect(() => {
const handler = () => navigate(-1);
try {
onBackButtonClick(handler);
} catch {
// Not in Telegram
}
return () => {
try {
offBackButtonClick(handler);
} catch {
// Not in Telegram
}
};
}, [navigate]);
return null;
}
export function AppWithNavigator() {
const isTelegram = useMemo(() => {
const result = isTelegramMiniApp();
console.log('[AppWithNavigator] Platform detection:', {
isTelegram: result,
platform: window.Telegram?.WebApp?.platform,
hasUser: window.Telegram?.WebApp?.initDataUnsafe?.user?.id !== undefined,
version: window.Telegram?.WebApp?.version,
});
return result;
}, []);
const isTelegram = isInTelegramWebApp();
// Common app content
const appContent = (
<PlatformProvider>
<ThemeColorsProvider>
<TooltipProvider>
<ToastProvider>
<WebSocketProvider>
<App />
</WebSocketProvider>
</ToastProvider>
</TooltipProvider>
</ThemeColorsProvider>
</PlatformProvider>
return (
<BrowserRouter>
{isTelegram && <TelegramBackButton />}
<PlatformProvider>
<ThemeColorsProvider>
<TooltipProvider>
<ToastProvider>
<WebSocketProvider>
<App />
</WebSocketProvider>
</ToastProvider>
</TooltipProvider>
</ThemeColorsProvider>
</PlatformProvider>
</BrowserRouter>
);
// Use Telegram navigator in Mini App, BrowserRouter elsewhere
if (isTelegram) {
return <TelegramRouter>{appContent}</TelegramRouter>;
}
return <BrowserRouter>{appContent}</BrowserRouter>;
}