/** * 个人主页 ProfileScreen - 美化版(响应式适配) * 胡萝卜BBS - 当前用户个人主页 * 采用现代卡片式设计,优化视觉层次和交互体验 * 支持桌面端双栏布局 */ import React, { useState, useCallback, useMemo } from 'react'; import { View, StyleSheet, RefreshControl, Animated, ScrollView, } from 'react-native'; import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context'; import { useNavigation } from '@react-navigation/native'; import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs'; import { colors, spacing } from '../../theme'; import { Post } from '../../types'; import { useAuthStore, useUserStore } from '../../stores'; import { postService } from '../../services'; import { UserProfileHeader, PostCard, TabBar } from '../../components/business'; import { Loading, EmptyState, Text } from '../../components/common'; import { ResponsiveContainer } from '../../components/common'; import { useResponsive } from '../../hooks'; import { ProfileStackParamList, HomeStackParamList, RootStackParamList } from '../../navigation/types'; type NavigationProp = NativeStackNavigationProp; type HomeNavigationProp = NativeStackNavigationProp; type RootNavigationProp = NativeStackNavigationProp; const TABS = ['帖子', '收藏']; const TAB_ICONS = ['file-document-outline', 'bookmark-outline']; export const ProfileScreen: React.FC = () => { const navigation = useNavigation(); const insets = useSafeAreaInsets(); const tabBarHeight = useBottomTabBarHeight(); const homeNavigation = useNavigation(); // 使用 any 类型来访问根导航 const rootNavigation = useNavigation(); const { currentUser, updateUser, fetchCurrentUser } = useAuthStore(); const { followUser, unfollowUser, likePost, unlikePost, favoritePost, unfavoritePost, posts: storePosts } = useUserStore(); // 响应式布局 const { isDesktop, isTablet, width } = useResponsive(); // 页面滚动底部安全间距,避免内容被底部 TabBar 遮挡 const scrollBottomInset = useMemo(() => { if (isDesktop) return spacing.lg; return tabBarHeight + insets.bottom + spacing.md; }, [isDesktop, tabBarHeight, insets.bottom]); const [activeTab, setActiveTab] = useState(0); const [posts, setPosts] = useState([]); const [favorites, setFavorites] = useState([]); const [loading, setLoading] = useState(true); const [refreshing, setRefreshing] = useState(false); const scrollY = new Animated.Value(0); // 跳转到关注列表 const handleFollowingPress = useCallback(() => { if (currentUser) { (rootNavigation as any).navigate('FollowList', { userId: currentUser.id, type: 'following' }); } }, [currentUser, rootNavigation]); // 跳转到粉丝列表 const handleFollowersPress = useCallback(() => { if (currentUser) { (rootNavigation as any).navigate('FollowList', { userId: currentUser.id, type: 'followers' }); } }, [currentUser, rootNavigation]); // 加载用户帖子 const loadUserPosts = useCallback(async () => { if (currentUser) { try { const response = await postService.getUserPosts(currentUser.id); setPosts(response.list); } catch (error) { console.error('获取用户帖子失败:', error); } } setLoading(false); }, [currentUser]); // 加载用户收藏 const loadUserFavorites = useCallback(async () => { if (currentUser) { try { console.log('[ProfileScreen] load, userUserFavorites calledId:', currentUser.id); const response = await postService.getUserFavorites(currentUser.id); console.log('[ProfileScreen] getUserFavorites response:', response); setFavorites(response.list); } catch (error) { console.error('获取用户收藏失败:', error); } } setLoading(false); }, [currentUser]); // 监听 tab 切换,只在数据为空时加载对应数据 React.useEffect(() => { if (activeTab === 0 && posts.length === 0) { loadUserPosts(); } else if (activeTab === 1 && favorites.length === 0) { loadUserFavorites(); } }, [activeTab, loadUserPosts, loadUserFavorites, posts.length, favorites.length]); // 初始加载 React.useEffect(() => { loadUserPosts(); }, [loadUserPosts]); // 同步 store 中的帖子状态到本地(用于点赞、收藏等状态更新) React.useEffect(() => { // 同步帖子列表状态 if (posts.length > 0) { let hasChanges = false; const updatedPosts = posts.map(localPost => { const storePost = storePosts.find(sp => sp.id === localPost.id); if (storePost && ( storePost.is_liked !== localPost.is_liked || storePost.is_favorited !== localPost.is_favorited || storePost.likes_count !== localPost.likes_count || storePost.favorites_count !== localPost.favorites_count )) { hasChanges = true; return { ...localPost, is_liked: storePost.is_liked, is_favorited: storePost.is_favorited, likes_count: storePost.likes_count, favorites_count: storePost.favorites_count, }; } return localPost; }); if (hasChanges) { setPosts(updatedPosts); } } // 同步收藏列表状态 if (favorites.length > 0) { let hasChanges = false; const updatedFavorites = favorites.map(localPost => { const storePost = storePosts.find(sp => sp.id === localPost.id); if (storePost && ( storePost.is_liked !== localPost.is_liked || storePost.is_favorited !== localPost.is_favorited || storePost.likes_count !== localPost.likes_count || storePost.favorites_count !== localPost.favorites_count )) { hasChanges = true; return { ...localPost, is_liked: storePost.is_liked, is_favorited: storePost.is_favorited, likes_count: storePost.likes_count, favorites_count: storePost.favorites_count, }; } return localPost; }); if (hasChanges) { setFavorites(updatedFavorites); } } }, [storePosts]); // 下拉刷新 const onRefresh = useCallback(async () => { setRefreshing(true); try { // 刷新用户信息 await fetchCurrentUser(); // 刷新帖子列表 await loadUserPosts(); } catch (error) { console.error('刷新失败:', error); } finally { setRefreshing(false); } }, [fetchCurrentUser, loadUserPosts]); // 跳转到设置页 const handleSettings = useCallback(() => { navigation.navigate('Settings'); }, [navigation]); // 跳转到编辑资料页 const handleEditProfile = useCallback(() => { navigation.navigate('EditProfile'); }, [navigation]); // 关注/取消关注 const handleFollow = useCallback(() => { if (!currentUser) return; if (currentUser.is_following) { unfollowUser(currentUser.id); } else { followUser(currentUser.id); } }, [currentUser, unfollowUser, followUser]); // 跳转到帖子详情 const handlePostPress = useCallback((postId: string, scrollToComments: boolean = false) => { homeNavigation.navigate('PostDetail', { postId, scrollToComments }); }, [homeNavigation]); // 跳转到用户主页(当前用户) const handleUserPress = useCallback((userId: string) => { // 个人主页点击自己的头像不跳转 }, []); // 删除帖子 const handleDeletePost = useCallback(async (postId: string) => { try { const success = await postService.deletePost(postId); if (success) { // 从帖子列表中移除 setPosts(prev => prev.filter(p => p.id !== postId)); // 也从收藏列表中移除(如果存在) setFavorites(prev => prev.filter(p => p.id !== postId)); } else { console.error('删除帖子失败'); } } catch (error) { console.error('删除帖子失败:', error); throw error; } }, []); // 渲染内容 const renderContent = useCallback(() => { if (loading) return ; if (activeTab === 0) { // 帖子 if (posts.length === 0) { return ( ); } return ( {posts.map((post, index) => { const isPostAuthor = currentUser?.id === post.author?.id; return ( handlePostPress(post.id)} onUserPress={() => post.author ? handleUserPress(post.author.id) : () => {}} onLike={() => post.is_liked ? unlikePost(post.id) : likePost(post.id)} onComment={() => handlePostPress(post.id, true)} onBookmark={() => post.is_favorited ? unfavoritePost(post.id) : favoritePost(post.id)} onShare={() => {}} onDelete={() => handleDeletePost(post.id)} isPostAuthor={isPostAuthor} /> ); })} ); } if (activeTab === 1) { // 收藏 if (favorites.length === 0) { return ( ); } return ( {favorites.map((post, index) => { const isPostAuthor = currentUser?.id === post.author?.id; return ( handlePostPress(post.id)} onUserPress={() => post.author ? handleUserPress(post.author.id) : () => {}} onLike={() => post.is_liked ? unlikePost(post.id) : likePost(post.id)} onComment={() => handlePostPress(post.id, true)} onBookmark={() => post.is_favorited ? unfavoritePost(post.id) : favoritePost(post.id)} onShare={() => {}} onDelete={() => handleDeletePost(post.id)} isPostAuthor={isPostAuthor} /> ); })} ); } return null; }, [loading, activeTab, posts, favorites, currentUser?.id, handlePostPress, handleUserPress, handleDeletePost, unlikePost, likePost, unfavoritePost, favoritePost]); // 渲染用户信息头部 - 不随 tab 变化,使用 useMemo 缓存 const renderUserHeader = useMemo(() => ( ), [currentUser, handleFollow, handleSettings, handleEditProfile, handleFollowingPress, handleFollowersPress]); // 渲染 TabBar 和内容 const renderTabBarAndContent = useMemo(() => ( <> {renderContent()} ), [activeTab, renderContent]); if (!currentUser) { return ( ); } // 桌面端使用双栏布局 if (isDesktop || isTablet) { return ( {/* 左侧:用户信息 */} } > {renderUserHeader} {/* 右侧:帖子列表 */} {renderTabBarAndContent} ); } // 移动端使用单栏布局 return ( } contentContainerStyle={[styles.scrollContent, { paddingBottom: scrollBottomInset }]} > {/* 用户信息头部 - 固定在顶部,不受 tab 切换影响 */} {renderUserHeader} {/* TabBar - 分离出来,切换 tab 不会影响上面的用户信息 */} {renderTabBarAndContent} ); }; const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: colors.background.default, }, scrollContent: { flexGrow: 1, }, // 桌面端双栏布局 desktopContainer: { flex: 1, flexDirection: 'row', gap: spacing.lg, padding: spacing.lg, }, desktopSidebar: { width: 380, flexShrink: 0, }, desktopContent: { flex: 1, minWidth: 0, }, desktopScrollContent: { flexGrow: 1, }, tabBarContainer: { marginTop: spacing.xs, marginBottom: 2, }, contentContainer: { flex: 1, minHeight: 350, paddingTop: spacing.xs, }, postsContainer: { paddingHorizontal: spacing.md, paddingTop: spacing.sm, }, postWrapper: { marginBottom: spacing.md, backgroundColor: colors.background.paper, borderRadius: 16, overflow: 'hidden', shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.06, shadowRadius: 8, elevation: 2, }, lastPost: { marginBottom: spacing['2xl'], }, }); export default ProfileScreen;