import { useEffect } from 'react'; import { Stack } from 'expo-router'; import { StatusBar } from 'expo-status-bar'; import { AppState } from 'react-native'; import { useFrameworkReady } from '@/hooks/useFrameworkReady'; import { useFonts } from 'expo-font'; import { Inter_400Regular, Inter_500Medium, Inter_600SemiBold, Inter_700Bold, Inter_800ExtraBold, Inter_900Black, } from '@expo-google-fonts/inter'; import { JetBrainsMono_400Regular, JetBrainsMono_500Medium, JetBrainsMono_700Bold, JetBrainsMono_800ExtraBold, } from '@expo-google-fonts/jetbrains-mono'; import * as SplashScreen from 'expo-splash-screen'; import { ProductAvailabilityGate } from '@/components/ProductAvailabilityGate'; import { getGlobalErrorUtils } from '@/lib/error-utils'; import { createMobilePlatformSdk, mobileRuntime } from '@/lib/runtime'; import { mobileTelemetry, trackMobileError } from '@/lib/telemetry'; import { AuthGate } from '@/components/auth/AuthGate'; import { MobileAuthProvider } from '@/providers/MobileAuthProvider'; import { TradingDataProvider } from '@/providers/TradingDataProvider'; SplashScreen.preventAutoHideAsync(); const mobilePlatformSdk = createMobilePlatformSdk(); console.info('[mobile] platform bootstrap', { productId: mobileRuntime.productId, tradingApiUrl: mobileRuntime.tradingApiUrl, platformApiUrl: mobileRuntime.platformApiUrl, sdkReady: Boolean(mobilePlatformSdk), }); export default function RootLayout() { useFrameworkReady(); const [fontsLoaded, fontError] = useFonts({ 'Inter-Regular': Inter_400Regular, 'Inter-Medium': Inter_500Medium, 'Inter-SemiBold': Inter_600SemiBold, 'Inter-Bold': Inter_700Bold, 'Inter-ExtraBold': Inter_800ExtraBold, 'Inter-Black': Inter_900Black, 'JetBrainsMono-Regular': JetBrainsMono_400Regular, 'JetBrainsMono-Medium': JetBrainsMono_500Medium, 'JetBrainsMono-Bold': JetBrainsMono_700Bold, 'JetBrainsMono-ExtraBold': JetBrainsMono_800ExtraBold, }); useEffect(() => { if (fontsLoaded || fontError) { SplashScreen.hideAsync(); } }, [fontsLoaded, fontError]); useEffect(() => { mobileTelemetry.init(); mobileTelemetry.trackEvent('info', 'app_shell', 'trading_mobile_bootstrap', { feature: 'bootstrap', tags: { surface: 'mobile' }, }); const appStateSubscription = AppState.addEventListener('change', (nextState) => { mobileTelemetry.trackEvent('info', 'app_lifecycle', 'app_state_changed', { message: nextState, }); if (nextState !== 'active') { mobileTelemetry.flush(); } }); const errorUtils = getGlobalErrorUtils(); const previousGlobalHandler = errorUtils?.getGlobalHandler?.(); errorUtils?.setGlobalHandler?.((error, isFatal) => { trackMobileError('app_shell', isFatal ? 'unhandled_fatal_error' : 'unhandled_error', error, { fatal: String(Boolean(isFatal)), }); mobileTelemetry.flush(); previousGlobalHandler?.(error, isFatal); }); return () => { appStateSubscription.remove(); errorUtils?.setGlobalHandler?.(previousGlobalHandler ?? ((error) => console.error(error))); mobileTelemetry.shutdown(); }; }, []); useEffect(() => { if (fontError) { trackMobileError('app_shell', 'font_load_failed', fontError); } }, [fontError]); if (!fontsLoaded && !fontError) { return null; } return ( ); }