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,5 +1,6 @@
import { useState, useRef, useEffect, useMemo, useCallback } from 'react';
import { createPortal } from 'react-dom';
import { isInTelegramWebApp } from '@/hooks/useTelegramSDK';
interface ColorPickerProps {
value: string;
@@ -9,11 +10,6 @@ interface ColorPickerProps {
disabled?: boolean;
}
// Check if running in Telegram WebApp
const isTelegramWebApp = (): boolean => {
return !!(window as unknown as { Telegram?: { WebApp?: unknown } }).Telegram?.WebApp;
};
// Convert hex to RGB
const hexToRgb = (hex: string): { r: number; g: number; b: number } => {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
@@ -117,7 +113,7 @@ export function ColorPicker({ value, onChange, label, description, disabled }: C
const pickerRef = useRef<HTMLDivElement>(null);
const colorInputRef = useRef<HTMLInputElement>(null);
const isTelegram = useMemo(() => isTelegramWebApp(), []);
const isTelegram = useMemo(() => isInTelegramWebApp(), []);
// Sync with external value
useEffect(() => {

View File

@@ -2,6 +2,7 @@ import { useState, useMemo, useEffect, useCallback, useRef } from 'react';
import { createPortal } from 'react-dom';
import { useTranslation } from 'react-i18next';
import { useQuery } from '@tanstack/react-query';
import { openLink as sdkOpenLink } from '@telegram-apps/sdk-react';
import { subscriptionApi } from '../api/subscription';
import { useTelegramWebApp } from '../hooks/useTelegramWebApp';
import { useBackButton, useHaptic } from '@/platform';
@@ -300,19 +301,11 @@ export default function ConnectionModal({ onClose }: ConnectionModalProps) {
// actual deep link URL. This works for both http(s) and custom schemes.
const redirectUrl = `${window.location.origin}/miniapp/redirect.html?url=${encodeURIComponent(deepLink)}&lang=${i18n.language || 'en'}`;
const tg = (
window as unknown as {
Telegram?: { WebApp?: { openLink?: (url: string, options?: object) => void } };
}
).Telegram?.WebApp;
if (tg?.openLink) {
try {
tg.openLink(redirectUrl, { try_instant_view: false, try_browser: true });
return;
} catch {
/* fallback */
}
try {
sdkOpenLink(redirectUrl, { tryInstantView: false });
return;
} catch {
// SDK not available, fallback
}
window.location.href = redirectUrl;
};

View File

@@ -2,6 +2,7 @@ import { Link, useLocation } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { useQuery } from '@tanstack/react-query';
import { useState, useEffect } from 'react';
import { initDataUser } from '@telegram-apps/sdk-react';
import { useAuthStore } from '@/store/auth';
import { useTheme } from '@/hooks/useTheme';
@@ -113,17 +114,12 @@ export function AppHeader({
// Get user photo from Telegram
useEffect(() => {
try {
const tg = (
window as unknown as {
Telegram?: { WebApp?: { initDataUnsafe?: { user?: { photo_url?: string } } } };
}
).Telegram?.WebApp;
const photoUrl = tg?.initDataUnsafe?.user?.photo_url;
if (photoUrl) {
setUserPhotoUrl(photoUrl);
const user = initDataUser();
if (user?.photo_url) {
setUserPhotoUrl(user.photo_url);
}
} catch (e) {
console.warn('Failed to get Telegram user photo:', e);
} catch {
// Not in Telegram or init data not available
}
}, []);