refactor(App, navigation): migrate to Expo Router and clean up navigation structure
All checks were successful
Frontend CI / build-and-push-web (push) Successful in 4m27s
Frontend CI / ota-android (push) Successful in 11m6s
Frontend CI / build-android-apk (push) Successful in 1h16m45s

- Updated App entry point to utilize Expo Router, simplifying the navigation setup.
- Removed legacy navigation components and services to streamline the codebase.
- Adjusted package.json to reflect the new entry point and updated dependencies for compatibility.
- Enhanced overall application structure by consolidating navigation logic and improving maintainability.
This commit is contained in:
lafay
2026-03-24 14:21:31 +08:00
parent b91e78c921
commit 2ddb9cadd8
111 changed files with 1829 additions and 3214 deletions

View File

@@ -0,0 +1,96 @@
import { Platform, useWindowDimensions } from 'react-native';
import { Tabs } from 'expo-router';
import { MaterialCommunityIcons } from '@expo/vector-icons';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { colors, shadows } from '../../../src/theme';
import { BREAKPOINTS } from '../../../src/hooks/useResponsive';
import { useTotalUnreadCount } from '../../../src/stores';
const TAB_BAR_HEIGHT = 64;
const TAB_BAR_MARGIN = 14;
const TAB_BAR_FLOATING_MARGIN = 12;
export default function TabsLayout() {
const { width } = useWindowDimensions();
const insets = useSafeAreaInsets();
const unreadCount = useTotalUnreadCount();
const hideTabBar = Platform.OS === 'web' && width >= BREAKPOINTS.desktop;
return (
<Tabs
screenOptions={{
headerShown: false,
tabBarActiveTintColor: colors.primary.main,
tabBarInactiveTintColor: colors.text.secondary,
tabBarStyle: hideTabBar
? { display: 'none', height: 0, overflow: 'hidden' }
: {
position: 'absolute',
left: TAB_BAR_MARGIN,
right: TAB_BAR_MARGIN,
bottom: TAB_BAR_FLOATING_MARGIN + insets.bottom,
height: TAB_BAR_HEIGHT,
borderRadius: 24,
backgroundColor: colors.background.paper,
...shadows.lg,
borderTopWidth: 0,
elevation: 100,
zIndex: 100,
},
tabBarShowLabel: true,
tabBarLabelStyle: { fontSize: 12, fontWeight: '600', marginTop: -2 },
}}
>
<Tabs.Screen
name="home"
options={{
title: '首页',
tabBarIcon: ({ color, focused }) => (
<MaterialCommunityIcons
name={focused ? 'home' : 'home-outline'}
size={focused ? 26 : 24}
color={color}
/>
),
}}
/>
<Tabs.Screen
name="messages"
options={{
title: '消息',
tabBarBadge: unreadCount > 0 ? (unreadCount > 99 ? 99 : unreadCount) : undefined,
tabBarIcon: ({ color, focused }) => (
<MaterialCommunityIcons
name={focused ? 'message-text' : 'message-text-outline'}
size={focused ? 26 : 24}
color={color}
/>
),
}}
/>
<Tabs.Screen
name="schedule"
options={{
title: '课表',
tabBarIcon: ({ color, focused }) => (
<MaterialCommunityIcons name="calendar-today" size={focused ? 26 : 24} color={color} />
),
}}
/>
<Tabs.Screen
name="profile"
options={{
title: '我的',
tabBarIcon: ({ color, focused }) => (
<MaterialCommunityIcons
name={focused ? 'account' : 'account-outline'}
size={focused ? 26 : 24}
color={color}
/>
),
}}
/>
</Tabs>
);
}

View File

@@ -0,0 +1,5 @@
import { Stack } from 'expo-router';
export default function HomeStackLayout() {
return <Stack screenOptions={{ headerShown: false }} />;
}

View File

@@ -0,0 +1,5 @@
import { HomeScreen } from '../../../../src/screens/home';
export default function HomeRoute() {
return <HomeScreen />;
}

View File

@@ -0,0 +1,5 @@
import { SearchScreen } from '../../../../src/screens/home';
export default function HomeSearchRoute() {
return <SearchScreen />;
}

View File

@@ -0,0 +1,5 @@
import { Stack } from 'expo-router';
export default function MessagesStackLayout() {
return <Stack screenOptions={{ headerShown: false }} />;
}

View File

@@ -0,0 +1,5 @@
import { MessageListScreen } from '../../../../src/screens/message';
export default function MessagesRoute() {
return <MessageListScreen />;
}

View File

@@ -0,0 +1,5 @@
import { NotificationsScreen } from '../../../../src/screens/message';
export default function NotificationsRoute() {
return <NotificationsScreen />;
}

View File

@@ -0,0 +1,36 @@
import { Stack } from 'expo-router';
import { useRouter } from 'expo-router';
import { AppBackButton } from '../../../../src/components/common';
import { colors } from '../../../../src/theme';
const headerOptions = {
headerStyle: { backgroundColor: colors.background.paper },
headerTintColor: colors.text.primary,
headerTitleStyle: { fontWeight: '600' as const },
headerShadowVisible: false,
headerBackTitle: '',
};
export default function ProfileStackLayout() {
const router = useRouter();
return (
<Stack
screenOptions={{
...headerOptions,
headerBackVisible: false,
headerLeft: () => <AppBackButton onPress={() => router.back()} />,
}}
>
<Stack.Screen name="index" options={{ headerShown: false }} />
<Stack.Screen name="settings" options={{ headerShown: true, title: '设置' }} />
<Stack.Screen name="edit-profile" options={{ title: '编辑资料' }} />
<Stack.Screen name="account-security" options={{ title: '账号安全' }} />
<Stack.Screen name="my-posts" options={{ title: '我的帖子' }} />
<Stack.Screen name="bookmarks" options={{ title: '收藏' }} />
<Stack.Screen name="notification-settings" options={{ title: '通知设置' }} />
<Stack.Screen name="blocked-users" options={{ title: '黑名单' }} />
</Stack>
);
}

View File

@@ -0,0 +1,5 @@
import { AccountSecurityScreen } from '../../../../src/screens/profile';
export default function AccountSecurityRoute() {
return <AccountSecurityScreen />;
}

View File

@@ -0,0 +1,5 @@
import { BlockedUsersScreen } from '../../../../src/screens/profile';
export default function BlockedUsersRoute() {
return <BlockedUsersScreen />;
}

View File

@@ -0,0 +1,5 @@
import { ProfileScreen } from '../../../../src/screens/profile';
export default function BookmarksRoute() {
return <ProfileScreen />;
}

View File

@@ -0,0 +1,5 @@
import { EditProfileScreen } from '../../../../src/screens/profile';
export default function EditProfileRoute() {
return <EditProfileScreen />;
}

View File

@@ -0,0 +1,5 @@
import { ProfileScreen } from '../../../../src/screens/profile';
export default function ProfileRoute() {
return <ProfileScreen />;
}

View File

@@ -0,0 +1,5 @@
import { ProfileScreen } from '../../../../src/screens/profile';
export default function MyPostsRoute() {
return <ProfileScreen />;
}

View File

@@ -0,0 +1,5 @@
import { NotificationSettingsScreen } from '../../../../src/screens/profile';
export default function NotificationSettingsRoute() {
return <NotificationSettingsScreen />;
}

View File

@@ -0,0 +1,5 @@
import { SettingsScreen } from '../../../../src/screens/profile';
export default function SettingsRoute() {
return <SettingsScreen />;
}

View File

@@ -0,0 +1,17 @@
import { Stack } from 'expo-router';
export default function ScheduleStackLayout() {
return (
<Stack screenOptions={{ headerShown: false }}>
<Stack.Screen name="index" />
<Stack.Screen
name="course"
options={{
headerShown: false,
presentation: 'transparentModal',
animation: 'fade',
}}
/>
</Stack>
);
}

View File

@@ -0,0 +1,5 @@
import { CourseDetailScreen } from '../../../../src/screens/schedule';
export default function CourseDetailRoute() {
return <CourseDetailScreen />;
}

View File

@@ -0,0 +1,5 @@
import { ScheduleScreen } from '../../../../src/screens/schedule';
export default function ScheduleRoute() {
return <ScheduleScreen />;
}