/** * 首页 HomeScreen * 威友 - 首页展示 * 支持列表和多列网格模式(响应式布局) */ import React, { useState, useEffect, useLayoutEffect, useCallback, useMemo, useRef } from 'react'; import { View, FlatList, ScrollView, Platform, StyleSheet, RefreshControl, StatusBar, TouchableOpacity, NativeSyntheticEvent, NativeScrollEvent, Alert, Clipboard, Modal, } from 'react-native'; import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context'; import { useRouter, useFocusEffect } from 'expo-router'; import { MaterialCommunityIcons } from '@expo/vector-icons'; import { PagerView } from '../../components/common'; import { useAppColors, useResolvedColorScheme, spacing, borderRadius, shadows, type AppColors } from '../../theme'; import { Post } from '../../types'; import { useUserStore, useHomeTabBarVisibilityStore, useHomeTabPressStore } from '../../stores'; import { useCurrentUser, useIsAuthenticated, useIsVerified, useVerificationStore } from '../../stores/auth'; import { channelService, postService } from '../../services'; import { PostCard, TabBar, SearchBar, ShareSheet } from '../../components/business'; import type { PostCardAction } from '../../components/business/PostCard'; import { Loading, EmptyState, Text, ImageGallery, ImageGridItem, ResponsiveGrid } from '../../components/common'; import { useResponsive, useResponsiveSpacing } from '../../hooks/useResponsive'; import { useDifferentialPosts } from '../../hooks/useDifferentialPosts'; import { postSyncService } from '@/services/post'; import { SearchScreen } from './SearchScreen'; import { CreatePostScreen } from '../create/CreatePostScreen'; import * as hrefs from '../../navigation/hrefs'; import { blurActiveElement } from '../../infrastructure/platform'; const TABS = ['关注', '最新', '热门']; const TAB_ICONS = ['account-heart-outline', 'clock-outline', 'fire']; const DEFAULT_PAGE_SIZE = 20; const MOBILE_TAB_BAR_HEIGHT = 64; const MOBILE_TAB_FLOATING_MARGIN = 12; const MOBILE_FAB_GAP = 12; /** 首页纵向滑动超过此阈值视为明确向下/向上划,用于隐藏或显示底部 Tab */ const TAB_BAR_SCROLL_DELTA_Y = 10; /** 停止滑动多久后自动恢复底部 Tab */ const TAB_BAR_IDLE_RESTORE_MS = 2200; type ViewMode = 'list' | 'grid'; type PostType = 'follow' | 'hot' | 'latest'; type LatestCapsule = { id: string; name: string }; function createHomeStyles(colors: AppColors) { return StyleSheet.create({ container: { flex: 1, backgroundColor: colors.background.default, }, header: { backgroundColor: colors.background.paper, }, searchWrapper: { paddingTop: spacing.lg, paddingBottom: spacing.xs, shadowColor: 'transparent', shadowOffset: { width: 0, height: 0 }, shadowOpacity: 0, shadowRadius: 0, elevation: 0, }, homeTabBar: { marginTop: spacing.sm, marginBottom: spacing.md, }, viewToggleBtn: { width: 44, height: 44, alignItems: 'center', justifyContent: 'center', }, capsuleWrapper: { paddingBottom: spacing.sm, backgroundColor: colors.background.paper, }, capsuleList: { gap: spacing.xs, paddingRight: spacing.lg, }, capsuleItem: { paddingHorizontal: spacing.md, paddingVertical: spacing.xs, borderRadius: 999, }, capsuleText: { fontSize: 13, fontWeight: '600', color: colors.text.secondary, }, capsuleTextActive: { color: colors.primary.main, }, listContent: { flexGrow: 1, }, listHeader: { width: '100%', }, contentContainer: { flex: 1, }, listItem: {}, waterfallScroll: { flex: 1, }, waterfallContainer: { width: '100%', flexGrow: 1, }, waterfallColumnsRow: { width: '100%', flexDirection: 'row', alignItems: 'flex-start', }, waterfallColumn: { flex: 1, }, waterfallItem: {}, floatingButton: { position: 'absolute', right: 20, bottom: 20, width: 56, height: 56, borderRadius: 28, backgroundColor: colors.primary.main, alignItems: 'center', justifyContent: 'center', ...shadows.lg, }, floatingButtonDesktop: { right: 40, bottom: 40, width: 64, height: 64, borderRadius: 32, }, floatingButtonWide: { right: 60, bottom: 60, width: 72, height: 72, borderRadius: 36, }, loadingMoreFooter: { paddingVertical: 20, alignItems: 'center', justifyContent: 'center', }, }); } export const HomeScreen: React.FC = () => { const router = useRouter(); const insets = useSafeAreaInsets(); const { posts: storePosts } = useUserStore(); const currentUser = useCurrentUser(); const isAuthenticated = useIsAuthenticated(); const isVerified = useIsVerified(); const fetchVerificationStatus = useVerificationStore((s) => s.fetchStatus); const hasFetchedVerification = useVerificationStore((s) => s.hasFetchedStatus); const colors = useAppColors(); const resolvedScheme = useResolvedColorScheme(); const styles = useMemo(() => createHomeStyles(colors), [colors]); const statusBarStyle = resolvedScheme === 'dark' ? 'light-content' : 'dark-content'; // 使用响应式 hook const { width, isMobile, isTablet, isDesktop, isWideScreen, } = useResponsive(); // 响应式间距 const responsiveGap = useResponsiveSpacing({ xs: 4, sm: 6, md: 8, lg: 12, xl: 16 }); const responsivePadding = useResponsiveSpacing({ xs: 8, sm: 12, md: 16, lg: 24, xl: 32 }); const [activeIndex, setActiveIndex] = useState(1); const [viewMode, setViewMode] = useState('list'); const [latestCapsules, setLatestCapsules] = useState([{ id: '', name: '全部' }]); const [activeCapsuleId, setActiveCapsuleId] = useState(''); // 图片查看器状态 const [showImageViewer, setShowImageViewer] = useState(false); const [postImages, setPostImages] = useState([]); const [selectedImageIndex, setSelectedImageIndex] = useState(0); // 搜索显示状态(用于内嵌搜索页面) const [showSearch, setShowSearch] = useState(false); // 发帖弹窗状态 const [showCreatePost, setShowCreatePost] = useState(false); // 分享面板状态 const [showShareSheet, setShowShareSheet] = useState(false); const [sharePost, setSharePost] = useState(null); useEffect(() => { if (isAuthenticated && !hasFetchedVerification) { fetchVerificationStatus(); } }, [isAuthenticated, hasFetchedVerification, fetchVerificationStatus]); useEffect(() => { if (showCreatePost) { blurActiveElement(); } }, [showCreatePost]); // 用于跟踪当前页面显示的帖子 ID,以便从 store 同步状态 const postIdsRef = useRef>(new Set()); const isLoadingMoreRef = useRef(false); // eslint-disable-next-line @typescript-eslint/no-explicit-any const pagerRef = useRef(null); const setBottomTabBarHiddenByScroll = useHomeTabBarVisibilityStore((s) => s.setBottomTabBarHiddenByScroll); const bottomTabBarHiddenByScroll = useHomeTabBarVisibilityStore((s) => s.bottomTabBarHiddenByScroll); const homeTabPressCount = useHomeTabPressStore((s) => s.pressCount); const homeListScrollYRef = useRef(0); const tabBarIdleRestoreTimerRef = useRef | null>(null); // FlatList 和 ScrollView 的 ref,用于滚动到顶部 const flatListRef = useRef | null>(null); const scrollViewRef = useRef(null); const clearTabBarIdleRestoreTimer = useCallback(() => { if (tabBarIdleRestoreTimerRef.current != null) { clearTimeout(tabBarIdleRestoreTimerRef.current); tabBarIdleRestoreTimerRef.current = null; } }, []); const scheduleTabBarIdleRestore = useCallback(() => { clearTabBarIdleRestoreTimer(); tabBarIdleRestoreTimerRef.current = setTimeout(() => { setBottomTabBarHiddenByScroll(false); tabBarIdleRestoreTimerRef.current = null; }, TAB_BAR_IDLE_RESTORE_MS); }, [clearTabBarIdleRestoreTimer, setBottomTabBarHiddenByScroll]); const handleHomeVerticalScroll = useCallback( (event: NativeSyntheticEvent) => { const y = event.nativeEvent.contentOffset.y; const dy = y - homeListScrollYRef.current; homeListScrollYRef.current = y; scheduleTabBarIdleRestore(); if (y <= 0) { setBottomTabBarHiddenByScroll(false); return; } if (dy > TAB_BAR_SCROLL_DELTA_Y) { setBottomTabBarHiddenByScroll(true); } else if (dy < -TAB_BAR_SCROLL_DELTA_Y) { setBottomTabBarHiddenByScroll(false); } }, [scheduleTabBarIdleRestore, setBottomTabBarHiddenByScroll] ); useFocusEffect( useCallback(() => { return () => { clearTabBarIdleRestoreTimer(); homeListScrollYRef.current = 0; setBottomTabBarHiddenByScroll(false); }; }, [clearTabBarIdleRestoreTimer, setBottomTabBarHiddenByScroll]) ); useEffect(() => { if (showSearch) { clearTabBarIdleRestoreTimer(); setBottomTabBarHiddenByScroll(false); } }, [showSearch, clearTabBarIdleRestoreTimer, setBottomTabBarHiddenByScroll]); // 监听首页 Tab 点击事件,滚动到顶部 useEffect(() => { if (homeTabPressCount > 0) { // 滚动 FlatList 到顶部 flatListRef.current?.scrollToOffset({ offset: 0, animated: true }); // 滚动 ScrollView 到顶部(网格模式) scrollViewRef.current?.scrollTo({ y: 0, animated: true }); // 重置底部 Tab 栏状态 setBottomTabBarHiddenByScroll(false); homeListScrollYRef.current = 0; } }, [homeTabPressCount, setBottomTabBarHiddenByScroll]); /** 横向胶囊条滚动位置:切换频道刷新列表时不重置 */ const capsuleHScrollRef = useRef(null); const capsuleScrollXRef = useRef(0); const restoreCapsuleStripScroll = useCallback(() => { const x = capsuleScrollXRef.current; if (x <= 0) return; const scroll = () => capsuleHScrollRef.current?.scrollTo({ x, animated: false }); scroll(); requestAnimationFrame(() => { requestAnimationFrame(scroll); }); }, []); const onCapsuleHorizontalScroll = useCallback((e: NativeSyntheticEvent) => { capsuleScrollXRef.current = e.nativeEvent.contentOffset.x; }, []); // 构建一个以 postId 为 key 的 map,用于快速查找 const postsMap = useMemo(() => { const map = new Map(); storePosts.forEach(post => { map.set(post.id, post); }); return map; }, [storePosts]); // 获取当前 tab 对应的帖子类型 const getPostType = useCallback((): PostType => { switch (activeIndex) { case 0: return 'follow'; case 1: return 'latest'; case 2: return 'hot'; default: return 'latest'; } }, [activeIndex]); const isLatestTab = activeIndex === 1; const currentChannelId = isLatestTab && activeCapsuleId ? activeCapsuleId : undefined; useEffect(() => { homeListScrollYRef.current = 0; setBottomTabBarHiddenByScroll(false); }, [activeIndex, currentChannelId, viewMode, setBottomTabBarHiddenByScroll]); useLayoutEffect(() => { if (!isLatestTab) return; restoreCapsuleStripScroll(); }, [isLatestTab, activeCapsuleId, latestCapsules.length, restoreCapsuleStripScroll]); // 使用差异更新 Hook 获取帖子列表 const listKey = useMemo( () => `home_${getPostType()}_${currentChannelId || 'all'}`, [getPostType, currentChannelId] ); const { posts, loading: isLoading, isInitialLoading, isLoadingMore, refreshing: isRefreshing, hasMore, error, reset, } = useDifferentialPosts( [], { listKey, enableDiff: true, enableBatching: true, autoSubscribe: true, } ); // 加载更多方法 const loadMore = useCallback(async () => { if (hasMore && !isLoadingMore && !isLoadingMoreRef.current) { isLoadingMoreRef.current = true; const startedAt = Date.now(); try { await postSyncService.loadMorePosts(listKey); console.log('[PostPerf] home loadMore', { listKey, costMs: Date.now() - startedAt, totalItems: posts.length, }); } catch (err) { console.error('加载更多失败:', err); } finally { isLoadingMoreRef.current = false; } } }, [posts.length, listKey, hasMore, isLoadingMore]); // 网格模式滚动处理 - 检测是否滚动到底部 + 底部 Tab 显隐 const handleGridScroll = useCallback( (event: NativeSyntheticEvent) => { handleHomeVerticalScroll(event); const { layoutMeasurement, contentOffset, contentSize } = event.nativeEvent; const scrollY = contentOffset.y; const visibleHeight = layoutMeasurement.height; const contentHeight = contentSize.height; if (scrollY + visibleHeight >= contentHeight - 200) { loadMore(); } }, [handleHomeVerticalScroll, loadMore] ); // 刷新方法 - 先获取正确类型的帖子,再刷新 const refresh = useCallback(async () => { try { // 先获取正确类型的帖子 await postSyncService.fetchPosts( { page: 1, pageSize: DEFAULT_PAGE_SIZE, post_type: getPostType(), channelId: currentChannelId, }, listKey ); } catch (err) { console.error('刷新失败:', err); } }, [listKey, getPostType, currentChannelId]); // Tab 切换时刷新数据并重置列表 useEffect(() => { reset(); refresh(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [activeIndex, currentChannelId]); // 将获取到的原始帖子与 store 中的状态合并 // 这样 UI 始终显示的是 store 中的最新状态(包括点赞、收藏等) const displayPosts = useMemo(() => { return posts.map(post => { const storePost = postsMap.get(post.id); if (storePost) { // store 同步点赞等状态;channel 等列表专用字段在 store 里常缺失,从列表帖合并 return { ...post, ...storePost, channel: storePost.channel ?? post.channel, }; } return post; }); }, [posts, postsMap]); /** 按 id 取当前列表中的最新 post,配合 PostCard memo 忽略 onAction 引用 */ const postByIdRef = useRef>(new Map()); postByIdRef.current = new Map(displayPosts.map((p) => [p.id, p])); // 根据屏幕尺寸确定网格列数 const gridColumns = useMemo(() => { if (isWideScreen || width >= 1440) return 4; if (isDesktop || width >= 1024) return 3; if (isTablet || width >= 768) return 2; return 2; // 移动端瀑布流保持2列 }, [width, isTablet, isDesktop, isWideScreen]); // 列表模式下始终使用单列,宽屏下居中显示 const useMultiColumnList = useMemo(() => { return false; // 改为始终返回false,使用单列布局 }, []); // 宽屏下内容最大宽度 const contentMaxWidth = useMemo(() => { if (isWideScreen) return 1200; if (isDesktop) return 1000; if (isTablet) return 800; return width; // 移动端使用全宽 }, [width, isTablet, isDesktop, isWideScreen]); // 列表模式横向内边距:移动端适当收窄,减少两侧空白 const listHorizontalPadding = useMemo(() => { if (isMobile) { return Math.max(6, responsivePadding - responsiveGap); } return responsivePadding; }, [isMobile, responsivePadding, responsiveGap]); // 列表模式卡片宽度:宽屏限宽并居中,移动端占用更多可用宽度 const listItemWidth = useMemo(() => { const availableWidth = Math.max(0, width - listHorizontalPadding * 2); if (isDesktop || isWideScreen) { return Math.min(contentMaxWidth, availableWidth); } return availableWidth; }, [width, listHorizontalPadding, isDesktop, isWideScreen, contentMaxWidth]); const floatingButtonBottom = useMemo(() => { if (!isMobile) { return undefined; } if (bottomTabBarHiddenByScroll) { return MOBILE_TAB_FLOATING_MARGIN + MOBILE_FAB_GAP + insets.bottom; } // TabBar 悬浮在底部,发帖按钮需要在 TabBar 上方 return MOBILE_TAB_BAR_HEIGHT + MOBILE_TAB_FLOATING_MARGIN * 2 + MOBILE_FAB_GAP + insets.bottom; }, [isMobile, insets.bottom, bottomTabBarHiddenByScroll]); // 切换视图模式 const toggleViewMode = () => { setViewMode(prev => prev === 'list' ? 'grid' : 'list'); }; // 切换Tab(点击 TabBar 或手势滑动) const changeTab = useCallback((nextIndex: number) => { if (nextIndex < 0 || nextIndex >= TABS.length || nextIndex === activeIndex) { return; } setActiveIndex(nextIndex); if (Platform.OS !== 'web') { pagerRef.current?.setPage(nextIndex); } }, [activeIndex]); const onPagerPageSelected = useCallback((e: { nativeEvent: { position: number } }) => { const nextIndex = e.nativeEvent.position; if (nextIndex !== activeIndex) { setActiveIndex(nextIndex); } }, [activeIndex]); useEffect(() => { const loadChannels = async () => { const list = await channelService.list(); const capsules: LatestCapsule[] = [ { id: '', name: '全部' }, ...list.map(item => ({ id: item.id, name: item.name })), ]; setLatestCapsules(capsules); }; loadChannels(); }, []); // 跳转到搜索页(使用内嵌模式,不再依赖导航) const handleSearchPress = () => { setShowSearch(true); }; // 跳转到帖子详情 const handlePostPress = (postId: string, scrollToComments: boolean = false) => { router.push(hrefs.hrefPostDetail(postId, scrollToComments)); }; // 跳转到用户主页 const handleUserPress = (userId: string) => { router.push(hrefs.hrefUserProfile(userId)); }; // 点赞帖子 const handleLike = async (post: Post) => { try { if (post.is_liked) { await postSyncService.unlikePost(post.id); } else { await postSyncService.likePost(post.id); } } catch (error) { console.error('点赞操作失败:', error); } }; // 收藏帖子 const handleBookmark = async (post: Post) => { try { if (post.is_favorited) { await postSyncService.unfavoritePost(post.id); } else { await postSyncService.favoritePost(post.id); } } catch (error) { console.error('收藏操作失败:', error); } }; // 分享帖子 - 打开分享面板 const handleShare = (post: Post) => { setSharePost(post); setShowShareSheet(true); }; // 删除帖子 - 删除后刷新列表 const handleDeletePost = async (postId: string) => { try { const success = await postService.deletePost(postId); if (success) { // 刷新列表以移除已删除的帖子 refresh(); } else { console.error('删除帖子失败'); } } catch (deleteError) { console.error('删除帖子失败:', deleteError); throw deleteError; // 重新抛出错误,让 PostCard 处理错误提示 } }; // 处理图片点击 - 打开图片查看器 const handleImagePress = (images: ImageGridItem[], index: number) => { setPostImages(images); setSelectedImageIndex(index); setShowImageViewer(true); }; // 统一处理 PostCard 的所有操作 const handlePostAction = (post: Post, action: PostCardAction) => { switch (action.type) { case 'press': handlePostPress(post.id); break; case 'userPress': const authorId = post.author?.id; if (authorId) handleUserPress(authorId); break; case 'like': case 'unlike': handleLike(post); break; case 'comment': handlePostPress(post.id, true); break; case 'bookmark': case 'unbookmark': handleBookmark(post); break; case 'share': handleShare(post); break; case 'imagePress': if (action.payload?.images && action.payload?.imageIndex !== undefined) { handleImagePress(action.payload.images, action.payload.imageIndex); } break; case 'delete': handleDeletePost(post.id); break; } }; const handlePostActionRef = useRef(handlePostAction); handlePostActionRef.current = handlePostAction; const stableOnPostAction = useCallback((postId: string, action: PostCardAction) => { const post = postByIdRef.current.get(postId); if (post) { handlePostActionRef.current(post, action); } }, []); // 跳转到发帖页面(使用 Modal 方式) const handleCreatePost = () => { if (!isAuthenticated) { router.push('/login'); return; } if (!isVerified) { router.push(hrefs.hrefVerificationGuide()); return; } setShowCreatePost(true); }; const renderLatestCapsules = () => { if (!isLatestTab) { return null; } return ( {latestCapsules.map((item) => { const isActive = item.id === activeCapsuleId; return ( setActiveCapsuleId(item.id)} style={styles.capsuleItem} > {item.name} ); })} ); }; // 渲染帖子卡片(列表模式) const keyExtractor = useCallback((item: Post) => item.id, []); const renderPostList = useCallback(({ item }: { item: Post }) => { const authorId = item.author?.id || ''; const isPostAuthor = currentUser?.id === authorId; return ( stableOnPostAction(item.id, action)} isPostAuthor={isPostAuthor} /> ); }, [currentUser?.id, stableOnPostAction, isMobile, listItemWidth, responsiveGap]); // 估算帖子在瀑布流中的高度(用于均匀分配) const estimatePostHeight = (post: Post, columnWidth: number): number => { const hasImage = post.images && post.images.length > 0; const hasTitle = !!post.title; const hasContent = !!post.content; let height = 0; // 图片区域高度(如果有图) if (hasImage) { // 使用帖子 ID 生成一致的宽高比 const hash = post.id.split('').reduce((a, b) => a + b.charCodeAt(0), 0); const aspectRatios = [0.7, 0.75, 0.8, 0.85, 0.9, 1, 1.1, 1.2]; const aspectRatio = aspectRatios[hash % aspectRatios.length]; height += columnWidth / aspectRatio; } else { // 无图帖子显示正文预览区域 if (hasContent) { // 根据内容长度估算高度(每行约20像素,最多6行) const contentLength = post.content?.length || 0; const estimatedLines = Math.min(6, Math.max(3, Math.ceil(contentLength / 20))); height += 16 + estimatedLines * 20; // padding + 文本高度 } } // 标题高度 if (hasTitle) { const titleLines = hasImage ? 2 : 3; height += 8 + titleLines * 20; // paddingTop + 文本高度 } // 底部信息栏高度 height += 40; // 用户信息 + 点赞数 // 间距 height += 2; // marginBottom return height; }; // 将帖子分成多列(瀑布流)- 使用贪心算法使各列高度尽量均匀 const distributePostsToColumns = useMemo(() => { const columns: Post[][] = Array.from({ length: gridColumns }, () => []); const columnHeights: number[] = Array(gridColumns).fill(0); // 防御性检查:确保 posts 存在且是数组 if (!displayPosts || !Array.isArray(displayPosts) || displayPosts.length === 0) { return columns; } // 计算单列宽度 const totalGap = (gridColumns - 1) * responsiveGap; const columnWidth = (width - responsivePadding * 2 - totalGap) / gridColumns; displayPosts.forEach((post) => { const postHeight = estimatePostHeight(post, columnWidth); // 找到当前高度最小的列 const minHeightIndex = columnHeights.indexOf(Math.min(...columnHeights)); columns[minHeightIndex].push(post); columnHeights[minHeightIndex] += postHeight; }); return columns; }, [displayPosts, gridColumns, width, responsiveGap, responsivePadding]); // 渲染单列帖子 const renderWaterfallColumn = (column: Post[], columnIndex: number) => ( {column.map(post => { const authorId = post.author?.id || ''; const isPostAuthor = currentUser?.id === authorId; return ( stableOnPostAction(post.id, action)} isPostAuthor={isPostAuthor} /> ); })} ); // 渲染响应式网格布局(平板/桌面端使用多列) const renderResponsiveGrid = () => { if (isMobile && !isTablet) { // 移动端使用瀑布流布局(2列) return ( } > {renderLatestCapsules()} {distributePostsToColumns.map((column, index) => renderWaterfallColumn(column, index))} {/* 独立底部加载区:避免参与横向列布局导致列宽抖动 */} {isLoadingMore && displayPosts.length > 0 && ( )} ); } // 平板/桌面端使用 ResponsiveGrid 组件 return ( {displayPosts.map(post => { const authorId = post.author?.id || ''; const isPostAuthor = currentUser?.id === authorId; return ( stableOnPostAction(post.id, action)} isPostAuthor={isPostAuthor} /> ); })} ); }; // 渲染空状态 const renderEmpty = useCallback(() => { if (isInitialLoading) return null; return ( ); }, [activeIndex, isInitialLoading]); // 渲染列表内容 const renderListContent = () => { if (useMultiColumnList) { // 平板/桌面端使用多列网格 return renderResponsiveGrid(); } // 移动端和宽屏都使用单列 FlatList,宽屏下居中显示 return ( } onEndReached={loadMore} onEndReachedThreshold={0.3} onScroll={handleHomeVerticalScroll} scrollEventThrottle={16} ListEmptyComponent={renderEmpty} ListFooterComponent={isLoadingMore ? : null} initialNumToRender={8} maxToRenderPerBatch={8} updateCellsBatchingPeriod={60} windowSize={7} removeClippedSubviews={false} /> ); }; // 搜索页面内嵌模式 if (showSearch) { return ( setShowSearch(false)} /> ); } // 正常首页内容 return ( {/* 顶部Header */} {/* 搜索栏 */} {}} onSubmit={handleSearchPress} onFocus={handleSearchPress} placeholder="搜索帖子、用户" /> {/* Tab切换 */} } /> {/* 帖子列表 */} {Platform.OS === 'web' ? ( {isInitialLoading ? ( ) : viewMode === 'list' ? ( renderListContent() ) : ( renderResponsiveGrid() )} ) : ( {isInitialLoading && activeIndex === 0 ? ( ) : activeIndex === 0 ? ( viewMode === 'list' ? renderListContent() : renderResponsiveGrid() ) : ( )} {isInitialLoading && activeIndex === 1 ? ( ) : activeIndex === 1 ? ( viewMode === 'list' ? renderListContent() : renderResponsiveGrid() ) : ( )} {isInitialLoading && activeIndex === 2 ? ( ) : activeIndex === 2 ? ( viewMode === 'list' ? renderListContent() : renderResponsiveGrid() ) : ( )} )} {/* 漂浮发帖按钮 */} {/* 图片查看器 */} ({ id: img.id || img.url || String(Math.random()), url: img.url || img.uri || '' }))} initialIndex={selectedImageIndex} onClose={() => setShowImageViewer(false)} enableSave /> {/* 发帖弹窗 */} setShowCreatePost(false)} > setShowCreatePost(false)} /> {/* 分享面板 */} { setShowShareSheet(false); setSharePost(null); }} onShareAction={(actionKey) => { if (sharePost) { (async () => { try { const res = await postService.sharePost(sharePost.id, { channel: actionKey }); if (res.ok && res.shares_count != null) { postSyncService.applyShareCountUpdate(sharePost.id, res.shares_count); } } catch (e) { console.error('上报分享次数失败:', e); } })(); } }} /> ); };