2026-03-16 17:47:10 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 移动端 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;
|
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 }],
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|