feat(pagination): implement cursor-based pagination across the app

Add useCursorPagination hook and update multiple screens and services
to use cursor-based pagination for better performance and consistency.

- Add useCursorPagination hook with deduplication and caching support
- Add cursor pagination types to infrastructure layer
- Refactor HomeScreen, PostDetailScreen, SearchScreen for posts/comments
- Refactor GroupMembersScreen, JoinGroupScreen for groups/members
- Refactor MessageListScreen, NotificationsScreen for messages
- Update post, message, group, comment, notification services with cursor endpoints
- Add CursorPaginationRequest/Response DTOs
- Remove deprecated OPTIMIZATION_DESIGN.md documentation
This commit is contained in:
lafay
2026-03-20 23:00:27 +08:00
parent a005fb0a15
commit 8a0aea1c59
20 changed files with 2464 additions and 2166 deletions

View File

@@ -30,12 +30,13 @@ import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs';
import { MaterialCommunityIcons } from '@expo/vector-icons';
import { colors, spacing, fontSizes, shadows, borderRadius } from '../../theme';
import { ConversationResponse, UserDTO, MessageResponse, extractTextFromSegments, extractTextFromSegmentsAsync, MessageSegment } from '../../types/dto';
import { authService } from '../../services';
import { authService, messageService } from '../../services';
import { useUserStore, useAuthStore } from '../../stores';
// 【新架构】使用MessageManager hooks
import { useMessageList, messageManager, useMessageListRefresh, useCreateConversation } from '../../stores';
import { messageManager, useMessageListRefresh, useCreateConversation, useUnreadCount, useMarkAsRead } from '../../stores';
import { Avatar, Text, EmptyState, ResponsiveContainer } from '../../components/common';
import { useResponsive, useBreakpointGTE } from '../../hooks/useResponsive';
import { useCursorPagination } from '../../hooks/useCursorPagination';
import { RootStackParamList, MessageStackParamList } from '../../navigation/types';
import { getUserCache } from '../../services/database';
// 导入 EmbeddedChat 组件用于桌面端双栏布局
@@ -160,22 +161,43 @@ export const MessageListScreen: React.FC = () => {
const { isDesktop, isTablet, width } = useResponsive();
const isWideScreen = useBreakpointGTE('lg');
// 【新架构】使用MessageManager的hook获取数据
// 【游标分页】使用 useCursorPagination hook 获取会话列表
const {
conversations,
items: conversations,
isLoading,
isRefreshing,
hasMore,
loadMore,
refresh,
totalUnreadCount,
systemUnreadCount,
markAllAsRead,
isMarking,
} = useMessageList();
error: paginationError,
} = useCursorPagination(
async ({ cursor, pageSize }) => {
return await messageService.getConversationsCursor({
cursor,
page_size: pageSize,
});
},
{ pageSize: 20 }
);
// 使用 MessageManager 获取未读数和系统通知数
const { totalUnreadCount, systemUnreadCount } = useUnreadCount();
const { markAllAsRead, isMarking } = useMarkAsRead(null);
// 【新架构】使用MessageManager的hook创建会话
const { createConversation } = useCreateConversation();
// 本地刷新状态仅用于下拉刷新的UI显示
const [refreshing, setRefreshing] = useState(false);
const [loadingMore, setLoadingMore] = useState(false);
// 上拉加载更多
const onEndReached = useCallback(async () => {
if (isLoading || loadingMore || !hasMore) return;
setLoadingMore(true);
await loadMore();
setLoadingMore(false);
}, [isLoading, loadingMore, hasMore, loadMore]);
// 搜索相关状态
const [isSearchMode, setIsSearchMode] = useState(false);
@@ -885,7 +907,7 @@ export const MessageListScreen: React.FC = () => {
</View>
</TouchableOpacity>
{isLoading ? (
{isLoading && conversations.length === 0 ? (
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color={colors.primary.main} />
</View>
@@ -901,6 +923,8 @@ export const MessageListScreen: React.FC = () => {
]}
showsVerticalScrollIndicator={false}
ListEmptyComponent={renderEmpty}
onEndReached={onEndReached}
onEndReachedThreshold={0.5}
refreshControl={
<RefreshControl
refreshing={refreshing}
@@ -909,6 +933,14 @@ export const MessageListScreen: React.FC = () => {
tintColor={colors.primary.main}
/>
}
ListFooterComponent={
loadingMore ? (
<View style={styles.loadingMoreContainer}>
<ActivityIndicator size="small" color={colors.primary.main} />
<Text style={styles.loadingMoreText}>...</Text>
</View>
) : null
}
/>
)}
</>
@@ -1261,6 +1293,17 @@ const styles = StyleSheet.create({
alignItems: 'center',
paddingVertical: spacing.xl * 2,
},
loadingMoreContainer: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
paddingVertical: spacing.md,
},
loadingMoreText: {
marginLeft: spacing.sm,
fontSize: 14,
color: '#999',
},
searchModeContainer: {
flex: 1,
backgroundColor: '#FAFAFA',