feat(navigation): add schedule tab and stack navigator
Add new ScheduleTab to main navigation with ScheduleStackNavigator for managing course schedule screens. Includes CourseDetailScreen with modal presentation. Also exports scheduleService and related types for the new schedule feature. fix(message): add group_id parameter to invite and request handlers Pass group_id from extra_data when calling respondInvite and reviewJoinRequest APIs, as the backend now requires this parameter. refactor(message): extract mergeMessagesById helper method Centralize message merging logic into a reusable private method to avoid duplication and ensure consistent message deduplication behavior.
This commit is contained in:
@@ -40,12 +40,14 @@ import type {
|
||||
MainTabParamList,
|
||||
HomeStackParamList,
|
||||
MessageStackParamList,
|
||||
ScheduleStackParamList,
|
||||
ProfileStackParamList,
|
||||
AuthStackParamList,
|
||||
} from './types';
|
||||
|
||||
// ==================== 导入屏幕组件 ====================
|
||||
import { HomeScreen, PostDetailScreen, SearchScreen } from '../screens/home';
|
||||
import { ScheduleScreen, CourseDetailScreen } from '../screens/schedule';
|
||||
import {
|
||||
MessageListScreen,
|
||||
ChatScreen,
|
||||
@@ -69,6 +71,7 @@ const RootStack = createNativeStackNavigator<RootStackParamList>();
|
||||
const AuthStack = createNativeStackNavigator<AuthStackParamList>();
|
||||
const HomeStack = createNativeStackNavigator<HomeStackParamList>();
|
||||
const MessageStack = createNativeStackNavigator<MessageStackParamList>();
|
||||
const ScheduleStack = createNativeStackNavigator<ScheduleStackParamList>();
|
||||
const ProfileStack = createNativeStackNavigator<ProfileStackParamList>();
|
||||
const Tab = createBottomTabNavigator<MainTabParamList>();
|
||||
|
||||
@@ -79,7 +82,7 @@ const SIDEBAR_COLLAPSED_WIDTH = 72;
|
||||
const MOBILE_TAB_FLOATING_MARGIN = 12;
|
||||
|
||||
// ==================== 导航项类型 ====================
|
||||
type TabName = 'HomeTab' | 'MessageTab' | 'ProfileTab';
|
||||
type TabName = 'HomeTab' | 'MessageTab' | 'ScheduleTab' | 'ProfileTab';
|
||||
|
||||
type IconName = React.ComponentProps<typeof MaterialCommunityIcons>['name'];
|
||||
|
||||
@@ -306,6 +309,7 @@ function Sidebar({
|
||||
const navItems: NavItem[] = [
|
||||
{ name: 'HomeTab', label: '首页', icon: 'home', iconOutline: 'home-outline' },
|
||||
{ name: 'MessageTab', label: '消息', icon: 'message-text', iconOutline: 'message-text-outline', badge: unreadCount },
|
||||
{ name: 'ScheduleTab', label: '课表', icon: 'calendar-today', iconOutline: 'calendar-today' },
|
||||
{ name: 'ProfileTab', label: '我的', icon: 'account', iconOutline: 'account-outline' },
|
||||
];
|
||||
|
||||
@@ -418,6 +422,37 @@ function Text({ style, children, numberOfLines }: { style?: any; children: React
|
||||
);
|
||||
}
|
||||
|
||||
// ==================== 课程表Stack导航 ====================
|
||||
function ScheduleStackNavigator() {
|
||||
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',
|
||||
contentStyle: {
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</ScheduleStack.Navigator>
|
||||
);
|
||||
}
|
||||
|
||||
// ==================== 响应式 Tab Navigator ====================
|
||||
interface ResponsiveTabNavigatorProps {
|
||||
children?: React.ReactNode;
|
||||
@@ -441,7 +476,7 @@ function ResponsiveTabNavigator({ children }: ResponsiveTabNavigatorProps) {
|
||||
useEffect(() => {
|
||||
const targetTab = route.params?.screen;
|
||||
if (!targetTab) return;
|
||||
if ((targetTab === 'HomeTab' || targetTab === 'MessageTab' || targetTab === 'ProfileTab') && targetTab !== activeTab) {
|
||||
if ((targetTab === 'HomeTab' || targetTab === 'MessageTab' || targetTab === 'ScheduleTab' || targetTab === 'ProfileTab') && targetTab !== activeTab) {
|
||||
setActiveTab(targetTab);
|
||||
}
|
||||
}, [route.params?.screen, activeTab]);
|
||||
@@ -482,6 +517,8 @@ function ResponsiveTabNavigator({ children }: ResponsiveTabNavigatorProps) {
|
||||
return <HomeStackNavigator />;
|
||||
case 'MessageTab':
|
||||
return <MessageStackNavigator />;
|
||||
case 'ScheduleTab':
|
||||
return <ScheduleStackNavigator />;
|
||||
case 'ProfileTab':
|
||||
return <ProfileStackNavigator />;
|
||||
default:
|
||||
@@ -609,6 +646,22 @@ function MainTabNavigator() {
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen
|
||||
name="ScheduleTab"
|
||||
component={ScheduleStackNavigator}
|
||||
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={ProfileStackNavigator}
|
||||
|
||||
@@ -5,11 +5,13 @@
|
||||
|
||||
import { NavigatorScreenParams } from '@react-navigation/native';
|
||||
import type { SystemMessageResponse } from '../types/dto';
|
||||
import type { Course } from '../types/schedule';
|
||||
|
||||
// ==================== 主Tab导航参数 ====================
|
||||
export type MainTabParamList = {
|
||||
HomeTab: NavigatorScreenParams<HomeStackParamList>;
|
||||
MessageTab: NavigatorScreenParams<MessageStackParamList>;
|
||||
ScheduleTab: NavigatorScreenParams<ScheduleStackParamList>;
|
||||
ProfileTab: NavigatorScreenParams<ProfileStackParamList>;
|
||||
};
|
||||
|
||||
@@ -59,6 +61,12 @@ export type ProfileStackParamList = {
|
||||
BlockedUsers: undefined;
|
||||
};
|
||||
|
||||
// ==================== 课程表Stack ====================
|
||||
export type ScheduleStackParamList = {
|
||||
Schedule: undefined;
|
||||
CourseDetail: { course: Course; relatedCourses: Course[] };
|
||||
};
|
||||
|
||||
// ==================== 认证Stack ====================
|
||||
export type AuthStackParamList = {
|
||||
Login: undefined;
|
||||
|
||||
Reference in New Issue
Block a user