Files
bedolaga-cabinet/src/main.tsx
Fringg 962dd43756 perf: improve LCP — move font loading to HTML, defer logo preload
- Move Google Fonts from CSS @import (render-blocking) to <link> in
  index.html with preconnect for earlier discovery
- Remove @import from globals.css — browser no longer waits for CSS
  parse -> @import fetch -> font CSS parse before first paint
- Defer initLogoPreload() to requestIdleCallback so logo fetch
  doesn't compete with critical resources during page load
2026-02-25 08:19:26 +03:00

120 lines
3.2 KiB
TypeScript

import React from 'react';
import ReactDOM from 'react-dom/client';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import {
init,
restoreInitData,
retrieveRawInitData,
mountMiniApp,
miniAppReady,
mountViewport,
expandViewport,
mountSwipeBehavior,
disableVerticalSwipes,
mountClosingBehavior,
disableClosingConfirmation,
mountBackButton,
bindThemeParamsCssVars,
bindViewportCssVars,
requestFullscreen,
isFullscreen,
} from '@telegram-apps/sdk-react';
import { clearStaleSessionIfNeeded } from './utils/token';
import { AppWithNavigator } from './AppWithNavigator';
import { ErrorBoundary } from './components/ErrorBoundary';
import { initLogoPreload } from './api/branding';
import { getCachedFullscreenEnabled, isTelegramMobile } from './hooks/useTelegramSDK';
import './i18n';
import './styles/globals.css';
// HMR guard — prevent double init when Vite hot-reloads the module
const HMR_KEY = '__tg_sdk_initialized';
const alreadyInitialized = (window as unknown as Record<string, unknown>)[HMR_KEY] === true;
if (!alreadyInitialized) {
(window as unknown as Record<string, unknown>)[HMR_KEY] = true;
try {
init();
restoreInitData();
// Сбрасываем старые токены если init data изменился (новая сессия Telegram)
clearStaleSessionIfNeeded(retrieveRawInitData() || null);
// Mount components — each in its own try/catch so one failure doesn't block others
// Note: mountMiniApp() internally mounts themeParams in SDK v3,
// so we don't call mountThemeParams() separately to avoid ConcurrentCallError
try {
mountMiniApp();
} catch {
/* already mounted */
}
try {
bindThemeParamsCssVars();
} catch {
/* theme params not yet available */
}
try {
mountSwipeBehavior();
disableVerticalSwipes();
} catch {
/* already mounted */
}
try {
mountClosingBehavior();
disableClosingConfirmation();
} catch {
/* already mounted */
}
try {
mountBackButton();
} catch {
/* already mounted */
}
// Viewport — async, fullscreen зависит от смонтированного viewport
mountViewport()
.then(() => {
bindViewportCssVars();
expandViewport();
// Auto-enter fullscreen if enabled in settings (mobile only)
if (getCachedFullscreenEnabled() && isTelegramMobile()) {
if (!isFullscreen()) {
requestFullscreen();
}
}
})
.catch(() => {});
miniAppReady();
} catch {
// Not in Telegram — ok
}
}
// Preload logo from cache — defer to idle time so it doesn't compete with LCP
if ('requestIdleCallback' in window) {
requestIdleCallback(() => initLogoPreload());
} else {
setTimeout(initLogoPreload, 100);
}
const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: 1,
refetchOnWindowFocus: false,
},
},
});
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<ErrorBoundary level="app">
<QueryClientProvider client={queryClient}>
<AppWithNavigator />
</QueryClientProvider>
</ErrorBoundary>
</React.StrictMode>,
);