diff --git a/src/hooks/useTelegramSDK.ts b/src/hooks/useTelegramSDK.ts index 28f8e02..81b46dd 100644 --- a/src/hooks/useTelegramSDK.ts +++ b/src/hooks/useTelegramSDK.ts @@ -75,23 +75,11 @@ export function initTelegramSDK() { initCleanup = init(); sdkInitialized = true; - // Mount viewport and bind CSS variables - viewport - .mount() - .then(() => { - viewport.bindCssVars(); - // Expand the mini app - viewport.expand(); - }) - .catch((err) => { - console.warn('Viewport mount failed:', err); - }); - - // Mount mini app and call ready + // Mount mini app and call ready (sync) miniApp.mount(); miniApp.ready(); - // Mount theme params and bind CSS variables + // Mount theme params and bind CSS variables (sync) try { themeParams.mount(); themeParams.bindCssVars(); @@ -99,25 +87,38 @@ export function initTelegramSDK() { // Theme params may already be mounted or not supported } - // Disable vertical swipes if supported + // Disable vertical swipes if supported (sync) if (swipeBehavior.isSupported()) { swipeBehavior.mount(); swipeBehavior.disableVertical(); } - // Auto-enter fullscreen if enabled in settings (mobile only) - const fullscreenEnabled = getCachedFullscreenEnabled(); - if (fullscreenEnabled && isTelegramMobile()) { - // Wait for viewport to be mounted - setTimeout(() => { - const isFullscreen = viewport.isFullscreen(); - if (!isFullscreen) { - viewport.requestFullscreen().catch((e) => { - console.warn('Auto-fullscreen failed:', e); - }); + // Mount viewport and bind CSS variables (async) + viewport + .mount() + .then(() => { + viewport.bindCssVars(); + // Expand the mini app + viewport.expand(); + + // Auto-enter fullscreen if enabled in settings (mobile only) + const fullscreenEnabled = getCachedFullscreenEnabled(); + if (fullscreenEnabled && isTelegramMobile()) { + try { + const isFullscreen = viewport.isFullscreen(); + if (!isFullscreen) { + viewport.requestFullscreen().catch((e) => { + console.warn('Auto-fullscreen failed:', e); + }); + } + } catch { + // Viewport signal not available + } } - }, 100); - } + }) + .catch((err) => { + console.warn('Viewport mount failed:', err); + }); } catch (e) { console.warn('Telegram SDK initialization failed:', e); } @@ -153,6 +154,7 @@ const defaultInsets = { top: 0, bottom: 0, left: 0, right: 0 }; /** * Helper to subscribe to SDK signals using useSyncExternalStore pattern + * Safely handles unmounted components by returning default values */ function useSDKSignal( signal: { (): T; sub(fn: VoidFunction): VoidFunction } | null, @@ -161,9 +163,22 @@ function useSDKSignal( return useSyncExternalStore( (callback) => { if (!signal) return () => {}; - return signal.sub(callback); + try { + return signal.sub(callback); + } catch { + // Signal not available (component unmounted) + return () => {}; + } + }, + () => { + if (!signal) return defaultValue; + try { + return signal(); + } catch { + // Signal not available (component unmounted) + return defaultValue; + } }, - () => (signal ? signal() : defaultValue), () => defaultValue, ); } diff --git a/src/platform/adapters/TelegramAdapter.ts b/src/platform/adapters/TelegramAdapter.ts index d95d93d..bf50dd6 100644 --- a/src/platform/adapters/TelegramAdapter.ts +++ b/src/platform/adapters/TelegramAdapter.ts @@ -65,8 +65,12 @@ function createBackButtonController(): BackButtonController { return { get isVisible() { - if (!inTelegram) return false; - return backButton.isVisible(); + if (!inTelegram || !mounted) return false; + try { + return backButton.isVisible(); + } catch { + return false; + } }, show(onClick: () => void) { @@ -115,8 +119,12 @@ function createMainButtonController(): MainButtonController { return { get isVisible() { - if (!inTelegram) return false; - return mainButton.isVisible(); + if (!inTelegram || !mounted) return false; + try { + return mainButton.isVisible(); + } catch { + return false; + } }, show(config: MainButtonConfig) { @@ -301,24 +309,28 @@ function createThemeController(): ThemeController { getThemeParams() { if (!inTelegram) return null; ensureMounted(); - const state = themeParams.state(); - // Convert SDK format to our format - return { - bg_color: state.bgColor, - text_color: state.textColor, - hint_color: state.hintColor, - link_color: state.linkColor, - button_color: state.buttonColor, - button_text_color: state.buttonTextColor, - secondary_bg_color: state.secondaryBgColor, - header_bg_color: state.headerBgColor, - bottom_bar_bg_color: state.bottomBarBgColor, - accent_text_color: state.accentTextColor, - section_bg_color: state.sectionBgColor, - section_header_text_color: state.sectionHeaderTextColor, - subtitle_text_color: state.subtitleTextColor, - destructive_text_color: state.destructiveTextColor, - }; + try { + const state = themeParams.state(); + // Convert SDK format to our format + return { + bg_color: state.bgColor, + text_color: state.textColor, + hint_color: state.hintColor, + link_color: state.linkColor, + button_color: state.buttonColor, + button_text_color: state.buttonTextColor, + secondary_bg_color: state.secondaryBgColor, + header_bg_color: state.headerBgColor, + bottom_bar_bg_color: state.bottomBarBgColor, + accent_text_color: state.accentTextColor, + section_bg_color: state.sectionBgColor, + section_header_text_color: state.sectionHeaderTextColor, + subtitle_text_color: state.subtitleTextColor, + destructive_text_color: state.destructiveTextColor, + }; + } catch { + return null; + } }, }; }