Files
frontend/src/navigation/MobileTabNavigatorWithDelay.tsx

556 lines
15 KiB
TypeScript
Raw Normal View History

2026-03-16 17:47:10 +08:00
/**
* Tab Navigator
*
* MainNavigator.tsx
*
*
*
* - React Navigation TabNavigator navigation state
* - SidebarBottomTabstate
* - 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;
2026-03-18 00:25:46 +08:00
CreatePost: undefined;
2026-03-16 17:47:10 +08:00
};
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<HomeStackParamList>();
const MessageStack = createNativeStackNavigator<MessageStackParamList>();
const ScheduleStack = createNativeStackNavigator<ScheduleStackParamList>();
const ProfileStack = createNativeStackNavigator<ProfileStackParamList>();
const Tab = createBottomTabNavigator<MainTabParamList>();
// ==================== 常量 ====================
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 (
<HomeStack.Navigator
screenOptions={{
headerStyle: {
backgroundColor: colors.background.paper,
},
headerTintColor: colors.text.primary,
headerTitleStyle: {
fontWeight: '600',
},
headerBackTitle: '',
headerShadowVisible: false,
}}
>
<HomeStack.Screen
name="Home"
component={HomeScreen}
options={{
title: '首页',
headerShown: false,
}}
/>
<HomeStack.Screen
name="Search"
component={SearchScreen}
options={{
title: '搜索',
headerShown: false,
}}
/>
2026-03-18 00:25:46 +08:00
<HomeStack.Screen
name="CreatePost"
component={CreatePostScreen}
options={{
title: '发帖',
headerShown: false,
presentation: 'modal',
}}
/>
2026-03-16 17:47:10 +08:00
</HomeStack.Navigator>
);
}
function MessageStackNavigatorComponent() {
return (
<MessageStack.Navigator
screenOptions={{
headerStyle: {
backgroundColor: colors.background.paper,
},
headerTintColor: colors.text.primary,
headerTitleStyle: {
fontWeight: '600',
},
headerBackTitle: '',
headerShadowVisible: false,
}}
>
<MessageStack.Screen
name="MessageList"
component={MessageListScreen}
options={{
title: '消息',
headerShown: false,
}}
/>
<MessageStack.Screen
name="Notifications"
component={NotificationsScreen}
options={{
title: '通知',
}}
/>
<MessageStack.Screen
name="PrivateChatInfo"
component={PrivateChatInfoScreen}
options={{
title: '聊天信息',
}}
/>
</MessageStack.Navigator>
);
}
function ScheduleStackNavigatorComponent() {
return (
<ScheduleStack.Navigator
screenOptions={{
headerShown: false,
}}
>
<ScheduleStack.Screen
name="Schedule"
component={ScheduleScreen}
options={{
title: '课表',
}}
/>
<ScheduleStack.Screen
name="CourseDetail"
component={CourseDetailScreen}
options={{
headerShown: false,
presentation: 'transparentModal',
animation: 'fade',
}}
/>
</ScheduleStack.Navigator>
);
}
function ProfileStackNavigatorComponent() {
return (
<ProfileStack.Navigator
screenOptions={{
headerStyle: {
backgroundColor: colors.background.paper,
},
headerTintColor: colors.text.primary,
headerTitleStyle: {
fontWeight: '600',
},
headerBackTitle: '',
headerShadowVisible: false,
}}
>
<ProfileStack.Screen
name="Profile"
component={ProfileScreen}
options={{
headerShown: false,
}}
/>
<ProfileStack.Screen
name="Settings"
component={SettingsScreen}
options={{
title: '设置',
}}
/>
<ProfileStack.Screen
name="EditProfile"
component={EditProfileScreen}
options={{
title: '编辑资料',
}}
/>
<ProfileStack.Screen
name="AccountSecurity"
component={AccountSecurityScreen}
options={{
title: '账号安全',
}}
/>
<ProfileStack.Screen
name="MyPosts"
component={ProfileScreen}
options={{
title: '我的帖子',
}}
/>
<ProfileStack.Screen
name="Bookmarks"
component={ProfileScreen}
options={{
title: '收藏',
}}
/>
<ProfileStack.Screen
name="NotificationSettings"
component={NotificationSettingsScreen}
options={{
title: '通知设置',
}}
/>
<ProfileStack.Screen
name="BlockedUsers"
component={BlockedUsersScreen}
options={{
title: '黑名单',
}}
/>
</ProfileStack.Navigator>
);
}
// ==================== 主组件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 (
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color={colors.primary.main} />
</View>
);
}
// 渲染错误状态(带重试按钮)
if (hasError) {
return (
<View style={styles.errorContainer}>
<MaterialCommunityIcons
name="alert-circle-outline"
size={48}
color={colors.error.main}
/>
<ActivityIndicator
size="small"
color={colors.primary.main}
style={styles.retryIndicator}
/>
</View>
);
}
// 渲染 Tab Navigator
return (
<Tab.Navigator
screenOptions={{
tabBarActiveTintColor: colors.primary.main,
tabBarInactiveTintColor: colors.text.secondary,
tabBarHideOnKeyboard: true,
tabBarStyle: {
backgroundColor: colors.background.paper,
borderTopWidth: 1,
borderTopColor: `${colors.divider}88`,
borderRadius: 24,
marginHorizontal: 14,
marginBottom: MOBILE_TAB_FLOATING_MARGIN + insets.bottom,
height: 64,
paddingBottom: 6,
paddingTop: 8,
paddingHorizontal: 8,
position: 'absolute',
...shadows.lg,
},
tabBarItemStyle: {
borderRadius: 18,
paddingVertical: 1,
marginHorizontal: 2,
},
tabBarLabelStyle: {
fontSize: 12,
fontWeight: '600',
marginTop: -2,
letterSpacing: 0.2,
},
tabBarBadgeStyle: {
backgroundColor: colors.error.main,
color: colors.primary.contrast,
fontSize: 10,
fontWeight: '700',
top: 4,
},
headerShown: false,
}}
>
<Tab.Screen
name="HomeTab"
component={HomeStackNavigatorComponent}
options={{
tabBarLabel: '首页',
tabBarIcon: ({ color, size, focused }) => (
<View
style={[
styles.tabIconContainer,
focused && styles.tabIconActive,
]}
>
<MaterialCommunityIcons
name={focused ? 'home' : 'home-outline'}
size={focused ? 26 : 24}
color={color}
/>
</View>
),
}}
/>
<Tab.Screen
name="MessageTab"
component={MessageStackNavigatorComponent}
options={{
tabBarLabel: '消息',
tabBarBadge:
messageUnreadCount > 0
? messageUnreadCount > 99
? '99+'
: messageUnreadCount
: undefined,
tabBarIcon: ({ color, size, focused }) => (
<View
style={[
styles.tabIconContainer,
focused && styles.tabIconActive,
]}
>
<MaterialCommunityIcons
name={focused ? 'message-text' : 'message-text-outline'}
size={focused ? 26 : 24}
color={color}
/>
</View>
),
}}
/>
<Tab.Screen
name="ScheduleTab"
component={ScheduleStackNavigatorComponent}
options={{
tabBarLabel: '课表',
tabBarIcon: ({ color, size, focused }) => (
<View
style={[
styles.tabIconContainer,
focused && styles.tabIconActive,
]}
>
<MaterialCommunityIcons
name={focused ? 'calendar-today' : 'calendar-today'}
size={focused ? 26 : 24}
color={color}
/>
</View>
),
}}
/>
<Tab.Screen
name="ProfileTab"
component={ProfileStackNavigatorComponent}
options={{
tabBarLabel: '我的',
tabBarIcon: ({ color, size, focused }) => (
<View
style={[
styles.tabIconContainer,
focused && styles.tabIconActive,
]}
>
<MaterialCommunityIcons
name={focused ? 'account' : 'account-outline'}
size={focused ? 26 : 24}
color={color}
/>
</View>
),
}}
/>
</Tab.Navigator>
);
}
// ==================== 样式 ====================
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 }],
},
});