refactor: standardize pagination response structure and enhance cursor handling
All checks were successful
Frontend CI / build-and-push-web (push) Successful in 5m8s
Frontend CI / ota-android (push) Successful in 11m22s
Frontend CI / build-android-apk (push) Successful in 45m46s

- Update various services and hooks to replace 'items' with 'list' for consistency in pagination responses.
- Modify PostRepository, commentService, groupService, messageService, and postService to align with the new response structure.
- Enhance cursor pagination logic in hooks and components to ensure proper handling of pagination states.
- Refactor HomeScreen, PostDetailScreen, SearchScreen, and other components to utilize the updated pagination structure.
This commit is contained in:
lafay
2026-03-23 03:58:26 +08:00
parent 88510aa6ae
commit 26ae288470
19 changed files with 312 additions and 309 deletions

View File

@@ -88,6 +88,10 @@ export const HomeScreen: React.FC = () => {
// 用于跟踪当前页面显示的帖子 ID以便从 store 同步状态
const postIdsRef = useRef<Set<string>>(new Set());
const lastSwipeAtRef = useRef(0);
// 网格模式滚动位置检测
const gridScrollYRef = useRef(0);
const isLoadingMoreRef = useRef(false);
// 构建一个以 postId 为 key 的 map用于快速查找
const postsMap = useMemo(() => {
@@ -131,14 +135,30 @@ export const HomeScreen: React.FC = () => {
// 加载更多方法
const loadMore = useCallback(async () => {
if (hasMore && !isLoading) {
if (hasMore && !isLoading && !isLoadingMoreRef.current) {
isLoadingMoreRef.current = true;
try {
await processPostUseCase.loadMorePosts(listKey);
} catch (err) {
console.error('加载更多失败:', err);
} finally {
isLoadingMoreRef.current = false;
}
}
}, [listKey, hasMore, isLoading]);
// 网格模式滚动处理 - 检测是否滚动到底部
const handleGridScroll = useCallback((event: any) => {
const { layoutMeasurement, contentOffset, contentSize } = event.nativeEvent;
const scrollY = contentOffset.y;
const visibleHeight = layoutMeasurement.height;
const contentHeight = contentSize.height;
// 当滚动到距离底部 200px 时触发加载更多
if (scrollY + visibleHeight >= contentHeight - 200) {
loadMore();
}
}, [loadMore]);
// 刷新方法 - 先获取正确类型的帖子,再刷新
const refresh = useCallback(async () => {
@@ -485,6 +505,7 @@ export const HomeScreen: React.FC = () => {
]}
showsVerticalScrollIndicator={false}
scrollEventThrottle={100}
onScroll={handleGridScroll}
refreshControl={
<RefreshControl
refreshing={isRefreshing}
@@ -495,6 +516,12 @@ export const HomeScreen: React.FC = () => {
}
>
{distributePostsToColumns.map((column, index) => renderWaterfallColumn(column, index))}
{/* 加载更多指示器 */}
{isLoading && displayPosts.length > 0 && (
<View style={styles.loadingMore}>
<Loading size="sm" />
</View>
)}
</ScrollView>
);
}
@@ -767,4 +794,9 @@ const styles = StyleSheet.create({
height: 72,
borderRadius: 36,
},
loadingMore: {
paddingVertical: 20,
alignItems: 'center',
justifyContent: 'center',
},
});

View File

@@ -72,7 +72,7 @@ export const PostDetailScreen: React.FC = () => {
// 使用游标分页 Hook 管理评论列表
const {
items: paginatedComments,
list: paginatedComments,
isLoading: isCommentsLoading,
isRefreshing: isCommentsRefreshing,
hasMore: hasMoreComments,
@@ -206,13 +206,13 @@ export const PostDetailScreen: React.FC = () => {
// 如果是从评论按钮跳转过来的,加载完成后滚动到评论区
useEffect(() => {
if (shouldScrollToComments && !loading && comments.length > 0) {
if (shouldScrollToComments && !loading && (comments?.length ?? 0) > 0) {
// 延迟确保列表已完成渲染和布局
setTimeout(() => {
flatListRef.current?.scrollToIndex({ index: 0, animated: true, viewPosition: 0 });
}, 500);
}
}, [shouldScrollToComments, loading, comments.length]);
}, [shouldScrollToComments, loading, comments?.length]);
// 动态设置导航头部
useEffect(() => {
@@ -1413,7 +1413,7 @@ export const PostDetailScreen: React.FC = () => {
</Text>
</TouchableOpacity>
) : comments.length > 0 ? (
) : (comments?.length ?? 0) > 0 ? (
<Text variant="caption" color={colors.text.hint} style={styles.noMoreComments}>
</Text>
@@ -1546,7 +1546,7 @@ export const PostDetailScreen: React.FC = () => {
</Text>
</TouchableOpacity>
) : comments.length > 0 ? (
) : (comments?.length ?? 0) > 0 ? (
<Text variant="caption" color={colors.text.hint} style={styles.noMoreComments}>
</Text>

View File

@@ -83,7 +83,7 @@ export const SearchScreen: React.FC<SearchScreenProps> = ({ onBack, navigation:
// 使用游标分页进行帖子搜索
const {
items: searchResults,
list: searchResults,
isLoading,
isRefreshing,
hasMore,
@@ -93,7 +93,7 @@ export const SearchScreen: React.FC<SearchScreenProps> = ({ onBack, navigation:
} = useCursorPagination<Post, { query: string }>(
async ({ cursor, pageSize, extraParams }) => {
if (!extraParams?.query) {
return { items: [], next_cursor: null, prev_cursor: null, has_more: false };
return { list: [], next_cursor: null, prev_cursor: null, has_more: false };
}
const response = await postService.searchPostsCursor(extraParams.query, {
cursor,

View File

@@ -72,7 +72,7 @@ const GroupMembersScreen: React.FC = () => {
// 使用游标分页 Hook 管理成员列表
const {
items: members,
list: members,
isLoading,
isRefreshing,
hasMore,

View File

@@ -36,7 +36,7 @@ const JoinGroupScreen: React.FC = () => {
// 使用游标分页 Hook 管理群组列表
const {
items: groups,
list: groups,
isLoading,
isRefreshing,
hasMore,

View File

@@ -164,7 +164,7 @@ export const MessageListScreen: React.FC = () => {
// 【游标分页】使用 useCursorPagination hook 获取会话列表
const {
items: conversations,
list: conversationList,
isLoading,
isRefreshing,
hasMore,
@@ -453,7 +453,7 @@ export const MessageListScreen: React.FC = () => {
if (activeSearchTab === 'chat') {
const results: SearchResultItem[] = [];
for (const conv of conversations) {
for (const conv of conversationList) {
await messageManager.fetchMessages(String(conv.id));
const messages = messageManager.getMessages(String(conv.id));
const matchedMessages = messages.filter(msg => {
@@ -484,7 +484,7 @@ export const MessageListScreen: React.FC = () => {
} finally {
setIsSearching(false);
}
}, [conversations, activeSearchTab]);
}, [conversationList, activeSearchTab]);
// 搜索文本变化时触发搜索
useEffect(() => {
@@ -533,7 +533,7 @@ export const MessageListScreen: React.FC = () => {
// 合并列表数据
const listData: ConversationResponse[] = [
createSystemMessageItem(),
...conversations,
...conversationList,
];
// 总未读数
@@ -923,7 +923,7 @@ export const MessageListScreen: React.FC = () => {
</View>
</TouchableOpacity>
{isLoading && conversations.length === 0 ? (
{isLoading && conversationList.length === 0 ? (
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color={colors.primary.main} />
</View>

View File

@@ -88,7 +88,7 @@ export const NotificationsScreen: React.FC<{ onBack?: () => void }> = ({ onBack
);
const {
items: messages,
list: messages,
isLoading,
isRefreshing,
hasMore,