fix: add HMR guard to prevent ConcurrentCallError on SDK double-init

При HMR Vite перевыполняет main.tsx, вызывая повторный init() и mount*()
компонентов SDK. mountThemeParams() и другие бросают ConcurrentCallError
асинхронно, что не ловится синхронным try/catch.

Решение: флаг на window.__tg_sdk_initialized предотвращает повторную
инициализацию при горячей перезагрузке модуля.
This commit is contained in:
c0mrade
2026-02-04 11:25:10 +03:00
parent 61e3910981
commit bcbda17220

View File

@@ -26,35 +26,51 @@ import { getCachedFullscreenEnabled, isTelegramMobile } from './hooks/useTelegra
import './i18n';
import './styles/globals.css';
// Safe mount helper — ignores "already mounted/mounting" errors (HMR, StrictMode)
function safeMountSync(fn: () => void) {
try {
fn();
} catch {
// Already mounted or not available
}
}
// 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;
// Initialize Telegram SDK v3
try {
init();
restoreInitData();
safeMountSync(() => mountMiniApp());
safeMountSync(() => {
// Mount components — each in its own try/catch so one failure doesn't block others
try {
mountMiniApp();
} catch {
/* already mounted */
}
try {
mountThemeParams();
bindThemeParamsCssVars();
});
safeMountSync(() => {
} catch {
/* already mounted */
}
try {
mountSwipeBehavior();
disableVerticalSwipes();
});
safeMountSync(() => {
} catch {
/* already mounted */
}
try {
mountClosingBehavior();
disableClosingConfirmation();
});
safeMountSync(() => mountBackButton());
safeMountSync(() => mountMainButton());
} catch {
/* already mounted */
}
try {
mountBackButton();
} catch {
/* already mounted */
}
try {
mountMainButton();
} catch {
/* already mounted */
}
// Viewport — async, fullscreen зависит от смонтированного viewport
mountViewport()
@@ -75,6 +91,7 @@ try {
} catch {
// Not in Telegram — ok
}
}
// Preload logo from cache immediately on page load
initLogoPreload();