import { DefaultTheme, ThemeProvider } from '@react-navigation/native'; import { useFonts } from 'expo-font'; import { Stack } from 'expo-router'; import { StatusBar } from 'expo-status-bar'; import 'react-native-reanimated'; import 'react-native-gesture-handler'; import { GestureHandlerRootView } from 'react-native-gesture-handler'; import PrivacyConsentModal from '@/components/PrivacyConsentModal'; import { useAppDispatch, useAppSelector } from '@/hooks/redux'; import { useColorScheme } from '@/hooks/useColorScheme'; import { clearAiCoachSessionCache } from '@/services/aiCoachSession'; import { store } from '@/store'; import { rehydrateUser, setPrivacyAgreed } from '@/store/userSlice'; import React from 'react'; import RNExitApp from 'react-native-exit-app'; import { DialogProvider } from '@/components/ui/DialogProvider'; import { ToastProvider } from '@/contexts/ToastContext'; import { Provider } from 'react-redux'; function Bootstrapper({ children }: { children: React.ReactNode }) { const dispatch = useAppDispatch(); const { privacyAgreed } = useAppSelector((state) => state.user); const [showPrivacyModal, setShowPrivacyModal] = React.useState(false); const [userDataLoaded, setUserDataLoaded] = React.useState(false); React.useEffect(() => { const loadUserData = async () => { await dispatch(rehydrateUser()); setUserDataLoaded(true); }; loadUserData(); // 冷启动时清空 AI 教练会话缓存 clearAiCoachSessionCache(); }, [dispatch]); React.useEffect(() => { // 当用户数据加载完成后,检查是否需要显示隐私同意弹窗 if (userDataLoaded && !privacyAgreed) { setShowPrivacyModal(true); } }, [userDataLoaded, privacyAgreed]); const handlePrivacyAgree = () => { dispatch(setPrivacyAgreed()); setShowPrivacyModal(false); }; const handlePrivacyDisagree = () => { RNExitApp.exitApp(); }; return ( {children} ); } export default function RootLayout() { const colorScheme = useColorScheme(); const [loaded] = useFonts({ SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'), }); if (!loaded) { // Async font loading only occurs in development. return null; } return ( ); }