134 lines
4.2 KiB
TypeScript
134 lines
4.2 KiB
TypeScript
import '../global.css'
|
||
import 'react-native-reanimated'
|
||
|
||
import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native'
|
||
import * as Sentry from '@sentry/react-native'
|
||
import { useKeepAwake } from 'expo-keep-awake'
|
||
import { Stack, useNavigationContainerRef } from 'expo-router'
|
||
import { StatusBar } from 'expo-status-bar'
|
||
import { useEffect } from 'react'
|
||
import { GestureHandlerRootView } from 'react-native-gesture-handler'
|
||
import { KeyboardProvider } from 'react-native-keyboard-controller'
|
||
import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context'
|
||
|
||
import { ModalPortal } from '@/@share/components'
|
||
import { bleManager } from '@/ble/managers/bleManager'
|
||
import { HotUpdate } from '@/components/hot-update'
|
||
import { useColorScheme } from '@/hooks/use-color-scheme'
|
||
import { setupGlobalFetchLogger } from '@/lib/fetch-logger'
|
||
import { useUserSession } from '@/stores/userStore'
|
||
|
||
const isProd = process.env.EXPO_PUBLIC_ENV !== 'development'
|
||
|
||
Sentry.init({
|
||
dsn: 'https://ef710a118839b1e86e38a3833a9a3c6c@o4507705403965440.ingest.us.sentry.io/4510576286302208',
|
||
sendDefaultPii: true,
|
||
enableLogs: true,
|
||
enabled: isProd,
|
||
})
|
||
|
||
if (__DEV__) {
|
||
setupGlobalFetchLogger()
|
||
}
|
||
|
||
export const unstable_settings = {
|
||
anchor: '(tabs)',
|
||
}
|
||
|
||
function RootLayout() {
|
||
const ref = useNavigationContainerRef()
|
||
|
||
useEffect(() => {
|
||
if (!ref?.current) return
|
||
|
||
const unsubscribe = ref.addListener('state', (e) => {
|
||
try {
|
||
const routes = e.data.state?.routes
|
||
if (!routes || routes.length === 0) return
|
||
|
||
const currentRoute = routes[routes.length - 1]
|
||
if (!currentRoute) return
|
||
|
||
let screenName = currentRoute.name
|
||
let params = currentRoute.params
|
||
|
||
// 处理嵌套路由
|
||
if (currentRoute.state?.routes) {
|
||
const nestedRoutes = currentRoute.state.routes
|
||
const activeNestedRoute = nestedRoutes[nestedRoutes.length - 1]
|
||
if (activeNestedRoute && activeNestedRoute.name !== '__root') {
|
||
screenName = activeNestedRoute.name
|
||
params = activeNestedRoute.params
|
||
}
|
||
}
|
||
|
||
// 使用 debug 而非 warn,避免噪音
|
||
if (__DEV__) {
|
||
console.debug(`📍 Navigation: ${screenName}`, params ? `(${JSON.stringify(params)})` : '')
|
||
}
|
||
|
||
// 上报到 Sentry(可选)
|
||
Sentry.captureMessage(`Navigation: ${screenName}`, 'info')
|
||
} catch (error) {
|
||
console.error('❌ Navigation listener error:', error)
|
||
Sentry.captureException(error)
|
||
}
|
||
})
|
||
|
||
return unsubscribe
|
||
}, [ref])
|
||
|
||
useEffect(() => {
|
||
bleManager.initialize()
|
||
|
||
return () => {
|
||
bleManager.destroy()
|
||
}
|
||
}, [])
|
||
|
||
return (
|
||
<Providers>
|
||
<Stack>
|
||
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
|
||
<Stack.Screen name="auth" options={{ headerShown: false }} />
|
||
<Stack.Screen name="forgotPassword" options={{ headerShown: false }} />
|
||
<Stack.Screen name="pointList" options={{ headerShown: false }} />
|
||
<Stack.Screen name="webview" options={{ headerShown: false }} />
|
||
</Stack>
|
||
</Providers>
|
||
)
|
||
}
|
||
|
||
function Providers({ children }: { children: React.ReactNode }) {
|
||
const colorScheme = useColorScheme()
|
||
useUserSession()
|
||
useKeepAwake()
|
||
|
||
return (
|
||
<SafeAreaProvider>
|
||
<GestureHandlerRootView style={{ flex: 1 }}>
|
||
<KeyboardProvider>
|
||
<ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
|
||
<SafeAreaView
|
||
className="flex-1"
|
||
edges={['top']}
|
||
style={{ backgroundColor: colorScheme === 'dark' ? '#21221D' : '#ffffff' }}
|
||
>
|
||
<StatusBar />
|
||
<HotUpdate />
|
||
{children}
|
||
</SafeAreaView>
|
||
|
||
<ModalPortal ref={(ref) => ((global as any).actionSheet = ref)} />
|
||
<ModalPortal ref={(ref) => ((global as any).modal = ref)} />
|
||
<ModalPortal ref={(ref) => ((global as any).loading = ref)} />
|
||
<ModalPortal ref={(ref) => ((global as any).toast = ref)} />
|
||
</ThemeProvider>
|
||
</KeyboardProvider>
|
||
</GestureHandlerRootView>
|
||
</SafeAreaProvider>
|
||
)
|
||
}
|
||
|
||
export default Sentry.wrap(RootLayout)
|