/** * 移动端 Tab Navigator(完全独立版本) * * 这个组件完全独立于 MainNavigator.tsx 中的其他导航组件, * 用于解决从大屏切换到小屏时的白屏问题。 * * 问题根源: * - React Navigation 的 TabNavigator 在初始化时需要完整的 navigation state * - 从大屏(Sidebar)切换到小屏(BottomTab)时,state 可能还没有准备好 * - 导致 TabRouter 报错:"Cannot read properties of undefined (reading 'filter')" * * 解决方案: * - 完全独立的组件,不依赖外部 navigation state * - 使用延迟初始化,确保 React Navigation 内部状态就绪 * - 错误边界和重试机制 */ import React, { useEffect, useState, useRef, useCallback } from 'react'; import { View, ActivityIndicator, StyleSheet, AppState, Platform, } from 'react-native'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { MaterialCommunityIcons } from '@expo/vector-icons'; import { colors, shadows } from '../theme'; import { useTotalUnreadCount } from '../stores'; import { messageManager } from '../stores'; // ==================== 导入屏幕组件 ==================== import { HomeScreen, PostDetailScreen, SearchScreen } from '../screens/home'; import { ScheduleScreen, CourseDetailScreen } from '../screens/schedule'; import { MessageListScreen, ChatScreen, NotificationsScreen, CreateGroupScreen, JoinGroupScreen, GroupRequestDetailScreen, GroupInviteDetailScreen, GroupInfoScreen, GroupMembersScreen, PrivateChatInfoScreen } from '../screens/message'; import { ProfileScreen, SettingsScreen, EditProfileScreen, NotificationSettingsScreen, BlockedUsersScreen, AccountSecurityScreen } from '../screens/profile'; import { CreatePostScreen } from '../screens/create'; import { UserScreen } from '../screens/profile'; import FollowListScreen from '../screens/profile/FollowListScreen'; // ==================== 类型定义 ==================== export type MainTabParamList = { HomeTab: undefined; MessageTab: undefined; ScheduleTab: undefined; ProfileTab: undefined; }; export type HomeStackParamList = { Home: undefined; Search: undefined; }; export type MessageStackParamList = { MessageList: undefined; Notifications: undefined; PrivateChatInfo: { conversationId: string; userId: string }; }; export type ScheduleStackParamList = { Schedule: undefined; CourseDetail: { courseId: string }; }; export type ProfileStackParamList = { Profile: undefined; Settings: undefined; EditProfile: undefined; AccountSecurity: undefined; MyPosts: undefined; Bookmarks: undefined; NotificationSettings: undefined; BlockedUsers: undefined; }; // ==================== Stack Navigators ==================== const HomeStack = createNativeStackNavigator(); const MessageStack = createNativeStackNavigator(); const ScheduleStack = createNativeStackNavigator(); const ProfileStack = createNativeStackNavigator(); const Tab = createBottomTabNavigator(); // ==================== 常量 ==================== const MOBILE_TAB_FLOATING_MARGIN = 12; const INITIALIZATION_DELAY = 500; // ms,增加延迟时间确保 React Navigation 完全初始化 const MAX_RETRY_ATTEMPTS = 5; const RETRY_DELAY = 500; // ms // ==================== Stack Navigator 组件 ==================== function HomeStackNavigatorComponent() { return ( ); } function MessageStackNavigatorComponent() { return ( ); } function ScheduleStackNavigatorComponent() { return ( ); } function ProfileStackNavigatorComponent() { return ( ); } // ==================== 主组件:MobileTabNavigatorWithDelay ==================== export function MobileTabNavigatorWithDelay() { const insets = useSafeAreaInsets(); const messageUnreadCount = useTotalUnreadCount(); const [isReady, setIsReady] = useState(false); const [hasError, setHasError] = useState(false); const initAttemptRef = useRef(0); // 初始化 MessageManager useEffect(() => { messageManager.initialize(); }, []); // 延迟初始化 useEffect(() => { let timeoutId: NodeJS.Timeout; let isMounted = true; const initializeNavigator = async () => { try { initAttemptRef.current += 1; console.log( `[MobileTabNavigator] Initialization attempt ${initAttemptRef.current}/${MAX_RETRY_ATTEMPTS}` ); // 延迟初始化 await new Promise((resolve) => { timeoutId = setTimeout(resolve, INITIALIZATION_DELAY); }); if (!isMounted) return; // 后台检测 if (Platform.OS !== 'web' && AppState.currentState === 'background') { console.log('[MobileTabNavigator] App in background, delaying...'); await new Promise((resolve) => { timeoutId = setTimeout(resolve, 300); }); } if (!isMounted) return; setIsReady(true); setHasError(false); console.log('[MobileTabNavigator] Initialization successful'); } catch (error) { console.error('[MobileTabNavigator] Initialization error:', error); if (!isMounted) return; if (initAttemptRef.current < MAX_RETRY_ATTEMPTS) { console.log('[MobileTabNavigator] Retrying...'); timeoutId = setTimeout(initializeNavigator, RETRY_DELAY); } else { setHasError(true); } } }; initializeNavigator(); return () => { isMounted = false; if (timeoutId) { clearTimeout(timeoutId); } }; }, []); // 渲染加载状态 if (!isReady && !hasError) { return ( ); } // 渲染错误状态(带重试按钮) if (hasError) { return ( ); } // 渲染 Tab Navigator return ( ( ), }} /> 0 ? messageUnreadCount > 99 ? '99+' : messageUnreadCount : undefined, tabBarIcon: ({ color, size, focused }) => ( ), }} /> ( ), }} /> ( ), }} /> ); } // ==================== 样式 ==================== const styles = StyleSheet.create({ loadingContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: colors.background.default, }, errorContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: colors.background.default, padding: 20, }, retryIndicator: { marginTop: 16, }, tabIconContainer: { alignItems: 'center', justifyContent: 'center', width: 38, height: 30, borderRadius: 15, }, tabIconActive: { backgroundColor: `${colors.primary.main}20`, transform: [{ translateY: -1 }], }, });