125 lines
4.0 KiB
TypeScript
125 lines
4.0 KiB
TypeScript
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 (
|
|
<ProductAvailabilityGate>
|
|
<MobileAuthProvider>
|
|
<AuthGate>
|
|
<TradingDataProvider>
|
|
<Stack screenOptions={{ headerShown: false }}>
|
|
<Stack.Screen name="(tabs)" />
|
|
<Stack.Screen name="marketplace" options={{ presentation: 'modal' }} />
|
|
<Stack.Screen name="chat" options={{ presentation: 'transparentModal', animation: 'fade' }} />
|
|
<Stack.Screen name="+not-found" />
|
|
</Stack>
|
|
<StatusBar style="light" />
|
|
</TradingDataProvider>
|
|
</AuthGate>
|
|
</MobileAuthProvider>
|
|
</ProductAvailabilityGate>
|
|
);
|
|
}
|