refactor(PostCard, PostCardGrid, HomeScreen): enhance PostCard component and remove PostCardGrid
- Introduced a new PostCardRelativeTime component for dynamic time display, improving user experience with real-time updates. - Refactored PostCard to utilize memoization for performance optimization and prevent unnecessary re-renders. - Removed the PostCardGrid component to streamline the codebase, consolidating functionality within the PostCard component. - Updated HomeScreen to leverage the new PostCard features, ensuring consistent post rendering and interaction handling.
This commit is contained in:
@@ -249,7 +249,10 @@ export function RootNavigator({ isAuthenticated, isInitializing }: RootNavigator
|
||||
>
|
||||
{isAuthenticated ? (
|
||||
<>
|
||||
<RootStack.Screen name="Main">
|
||||
<RootStack.Screen
|
||||
name="Main"
|
||||
navigationKey={isMobile ? 'main-mobile' : 'main-desktop'}
|
||||
>
|
||||
{() =>
|
||||
isMobile ? (
|
||||
<SimpleMobileTabNavigator />
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
/**
|
||||
* 简单的移动端 Tab Navigator
|
||||
* 简单的移动端 Tab Navigator(自定义 Tab 栏)
|
||||
*
|
||||
* 不使用 React Navigation 的 Tab Navigator,而是使用一个简单的自定义实现
|
||||
* 这样可以完全避免 React Navigation 状态恢复的问题
|
||||
* 策略(在 EnsureSingleNavigator 限制下尽量省加载):
|
||||
* - 首页、消息:屏幕本身不带根级 NativeStack,首次进入后保留挂载,仅 display 隐藏 → 来回切换快。
|
||||
* - 课表、我的:各含一个 NativeStack,同一时刻只挂载当前选中的一个,避免重复注册 Navigator。
|
||||
*
|
||||
* 其它可叠加的优化(按需再做,不放在本文件里):
|
||||
* - 数据:TanStack Query 调 staleTime、列表 prefetch、占位骨架
|
||||
* - 时机:InteractionManager.runAfterInteractions 再拉非首屏接口
|
||||
* - 渲染:React.memo 重列表项、避免 Tab 切换时整树不必要 setState
|
||||
*/
|
||||
|
||||
import React, { useEffect, useState, useCallback } from 'react';
|
||||
import {
|
||||
View,
|
||||
StyleSheet,
|
||||
TouchableOpacity,
|
||||
Text,
|
||||
Dimensions,
|
||||
} from 'react-native';
|
||||
import { View, StyleSheet, TouchableOpacity, Text } from 'react-native';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
||||
import { createNativeStackNavigator } from '@react-navigation/native-stack';
|
||||
@@ -22,13 +22,9 @@ import { useTotalUnreadCount } from '../stores';
|
||||
import { messageManager } from '../stores';
|
||||
|
||||
// ==================== 导入屏幕组件 ====================
|
||||
import { HomeScreen, SearchScreen } from '../screens/home';
|
||||
import { HomeScreen } from '../screens/home';
|
||||
import { ScheduleScreen, CourseDetailScreen } from '../screens/schedule';
|
||||
import {
|
||||
MessageListScreen,
|
||||
NotificationsScreen,
|
||||
PrivateChatInfoScreen,
|
||||
} from '../screens/message';
|
||||
import { MessageListScreen } from '../screens/message';
|
||||
import { ProfileScreen, SettingsScreen, EditProfileScreen, NotificationSettingsScreen, BlockedUsersScreen, AccountSecurityScreen } from '../screens/profile';
|
||||
|
||||
// ==================== 类型定义 ====================
|
||||
@@ -178,11 +174,10 @@ export function SimpleMobileTabNavigator() {
|
||||
const insets = useSafeAreaInsets();
|
||||
const messageUnreadCount = useTotalUnreadCount();
|
||||
const [activeTab, setActiveTab] = useState<TabName>('HomeTab');
|
||||
const [mountedTabs, setMountedTabs] = useState<Record<TabName, boolean>>({
|
||||
/** 无嵌套 Stack 的 Tab:保留实例,避免每次切回都整页重挂载 */
|
||||
const [plainTabEverShown, setPlainTabEverShown] = useState({
|
||||
HomeTab: true,
|
||||
MessageTab: false,
|
||||
ScheduleTab: false,
|
||||
ProfileTab: false,
|
||||
});
|
||||
|
||||
// 初始化 MessageManager
|
||||
@@ -190,36 +185,13 @@ export function SimpleMobileTabNavigator() {
|
||||
messageManager.initialize();
|
||||
}, []);
|
||||
|
||||
// 处理 Tab 切换
|
||||
const handleTabPress = useCallback((tabName: TabName) => {
|
||||
setMountedTabs((prev) => {
|
||||
if (prev[tabName]) {
|
||||
return prev;
|
||||
}
|
||||
return {
|
||||
...prev,
|
||||
[tabName]: true,
|
||||
};
|
||||
});
|
||||
if (tabName === 'HomeTab' || tabName === 'MessageTab') {
|
||||
setPlainTabEverShown((prev) => ({ ...prev, [tabName]: true }));
|
||||
}
|
||||
setActiveTab(tabName);
|
||||
}, []);
|
||||
|
||||
// 根据 Tab 名称渲染对应内容
|
||||
const renderTabView = (tabName: TabName) => {
|
||||
switch (tabName) {
|
||||
case 'HomeTab':
|
||||
return <HomeScreen />;
|
||||
case 'MessageTab':
|
||||
return <MessageListScreen />;
|
||||
case 'ScheduleTab':
|
||||
return <ScheduleStackNavigatorComponent />;
|
||||
case 'ProfileTab':
|
||||
return <ProfileStackNavigatorComponent />;
|
||||
default:
|
||||
return <HomeScreen />;
|
||||
}
|
||||
};
|
||||
|
||||
// 渲染 Tab Bar 图标
|
||||
const renderTabIcon = (tabName: TabName, isActive: boolean) => {
|
||||
const iconColor = isActive ? colors.primary.main : colors.text.secondary;
|
||||
@@ -284,31 +256,43 @@ export function SimpleMobileTabNavigator() {
|
||||
);
|
||||
};
|
||||
|
||||
// 计算底部安全距离
|
||||
const bottomSafeArea = TAB_BAR_HEIGHT + TAB_BAR_FLOATING_MARGIN * 2 + insets.bottom;
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
{/* 内容区域 */}
|
||||
<View style={styles.content}>
|
||||
{(Object.keys(mountedTabs) as TabName[]).map((tabName) => {
|
||||
if (!mountedTabs[tabName]) {
|
||||
return null;
|
||||
}
|
||||
const isActive = activeTab === tabName;
|
||||
return (
|
||||
<View
|
||||
key={tabName}
|
||||
style={[
|
||||
styles.tabScreen,
|
||||
!isActive && styles.tabScreenHidden,
|
||||
]}
|
||||
pointerEvents={isActive ? 'auto' : 'none'}
|
||||
>
|
||||
{renderTabView(tabName)}
|
||||
</View>
|
||||
);
|
||||
})}
|
||||
{plainTabEverShown.HomeTab && (
|
||||
<View
|
||||
key="HomeTab"
|
||||
style={[
|
||||
styles.tabScreen,
|
||||
activeTab !== 'HomeTab' && styles.tabScreenHidden,
|
||||
]}
|
||||
pointerEvents={activeTab === 'HomeTab' ? 'auto' : 'none'}
|
||||
>
|
||||
<HomeScreen />
|
||||
</View>
|
||||
)}
|
||||
{plainTabEverShown.MessageTab && (
|
||||
<View
|
||||
key="MessageTab"
|
||||
style={[
|
||||
styles.tabScreen,
|
||||
activeTab !== 'MessageTab' && styles.tabScreenHidden,
|
||||
]}
|
||||
pointerEvents={activeTab === 'MessageTab' ? 'auto' : 'none'}
|
||||
>
|
||||
<MessageListScreen />
|
||||
</View>
|
||||
)}
|
||||
{activeTab === 'ScheduleTab' && (
|
||||
<View key="ScheduleTab" style={styles.tabScreen}>
|
||||
<ScheduleStackNavigatorComponent />
|
||||
</View>
|
||||
)}
|
||||
{activeTab === 'ProfileTab' && (
|
||||
<View key="ProfileTab" style={styles.tabScreen}>
|
||||
<ProfileStackNavigatorComponent />
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{/* Tab Bar */}
|
||||
@@ -361,9 +345,6 @@ const styles = StyleSheet.create({
|
||||
flex: 1,
|
||||
backgroundColor: colors.background.default,
|
||||
},
|
||||
stackContainer: {
|
||||
flex: 1,
|
||||
},
|
||||
content: {
|
||||
flex: 1,
|
||||
position: 'relative',
|
||||
@@ -374,9 +355,6 @@ const styles = StyleSheet.create({
|
||||
tabScreenHidden: {
|
||||
display: 'none',
|
||||
},
|
||||
bottomSpacer: {
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
tabBar: {
|
||||
position: 'absolute',
|
||||
left: TAB_BAR_MARGIN,
|
||||
@@ -390,6 +368,9 @@ const styles = StyleSheet.create({
|
||||
justifyContent: 'space-around',
|
||||
paddingHorizontal: 8,
|
||||
...shadows.lg,
|
||||
// 叠在全屏内容之上(shadows.lg 含 elevation,需最后覆盖)
|
||||
zIndex: 100,
|
||||
elevation: 100,
|
||||
},
|
||||
tabItem: {
|
||||
flex: 1,
|
||||
|
||||
Reference in New Issue
Block a user