/** * FollowListScreen 关注/粉丝列表页面(响应式适配) * 显示用户的关注列表或粉丝列表 * 支持互关状态显示和关注/回关操作 * 在宽屏下使用网格布局 */ import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react'; import { View, FlatList, StyleSheet, RefreshControl, TouchableOpacity, ListRenderItem, ActivityIndicator, } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { useRouter, useLocalSearchParams } from 'expo-router'; import { spacing, borderRadius, shadows, useAppColors, type AppColors } from '../../theme'; import { User } from '../../types'; import { useAuthStore, useUserStore } from '../../stores'; import { authService } from '../../services'; import { Avatar, Text, Button, Loading, EmptyState, ResponsiveContainer } from '../../components/common'; import { SearchBar } from '../../components/business'; import * as hrefs from '../../navigation/hrefs'; import { useResponsive, useColumnCount } from '../../hooks'; const FollowListScreen: React.FC = () => { const colors = useAppColors(); const styles = useMemo(() => createFollowListStyles(colors), [colors]); const router = useRouter(); const { userId = '', type: typeParam } = useLocalSearchParams<{ userId?: string; type?: string }>(); const type = typeParam === 'followers' ? 'followers' : 'following'; const currentUser = useAuthStore((s) => s.currentUser); const { followUser, unfollowUser } = useUserStore.getState(); // 响应式布局 const { isWideScreen, isDesktop, width } = useResponsive(); const columnCount = useColumnCount({ xs: 1, sm: 1, md: 2, lg: 2, xl: 3, '2xl': 3, '3xl': 4, '4xl': 4, }); const [users, setUsers] = useState([]); const [loading, setLoading] = useState(true); const [refreshing, setRefreshing] = useState(false); const [page, setPage] = useState(1); const [hasMore, setHasMore] = useState(true); const [keyword, setKeyword] = useState(''); const [debouncedKeyword, setDebouncedKeyword] = useState(''); const debounceRef = useRef | null>(null); const isCurrentUser = currentUser?.id === userId; const title = type === 'following' ? '关注' : '粉丝'; const isSearching = debouncedKeyword.trim().length > 0; // 加载用户列表 const loadUsers = useCallback(async (pageNum: number = 1, refresh: boolean = false) => { if (!hasMore && !refresh) return; try { const pageSize = 20; let userList: User[] = []; const trimmedKeyword = debouncedKeyword.trim(); if (type === 'following') { userList = await authService.getFollowingList(userId, pageNum, pageSize, trimmedKeyword); } else { userList = await authService.getFollowersList(userId, pageNum, pageSize, trimmedKeyword); } if (refresh) { setUsers(userList); setPage(1); } else { setUsers(prev => [...prev, ...userList]); } setHasMore(userList.length === pageSize); } catch (error) { console.error('加载用户列表失败:', error); } setLoading(false); setRefreshing(false); }, [userId, type, hasMore, debouncedKeyword]); // 初始加载(包括 type/userId 切换以及防抖后关键词变化) useEffect(() => { setLoading(true); setHasMore(true); loadUsers(1, true); // 仅在 userId/type/debouncedKeyword 变化时重新加载 // eslint-disable-next-line react-hooks/exhaustive-deps }, [userId, type, debouncedKeyword]); // 关键词输入防抖(300ms) useEffect(() => { if (debounceRef.current) { clearTimeout(debounceRef.current); } debounceRef.current = setTimeout(() => { setDebouncedKeyword(keyword); }, 300); return () => { if (debounceRef.current) { clearTimeout(debounceRef.current); debounceRef.current = null; } }; }, [keyword]); // 下拉刷新 const onRefresh = useCallback(() => { setRefreshing(true); setHasMore(true); loadUsers(1, true); }, [loadUsers]); // 加载更多 const loadMore = useCallback(() => { if (!loading && hasMore) { const nextPage = page + 1; setPage(nextPage); loadUsers(nextPage); } }, [loading, hasMore, page, loadUsers]); // 关注/取消关注 const handleFollowToggle = async (user: User) => { const isFollowing = user.is_following ?? false; // 乐观更新 setUsers(prev => prev.map(u => { if (u.id === user.id) { return { ...u, is_following: !isFollowing, followers_count: isFollowing ? u.followers_count - 1 : u.followers_count + 1, }; } return u; })); if (isFollowing) { await unfollowUser(user.id); } else { await followUser(user.id); } }; // 跳转到用户主页 const handleUserPress = (targetUserId: string) => { if (targetUserId !== currentUser?.id) { router.push(hrefs.hrefUserProfile(targetUserId)); } }; // 获取按钮状态 const getButtonConfig = (user: User): { title: string; variant: 'primary' | 'outline' } => { const isFollowing = user.is_following ?? false; const isFollowingMe = user.is_following_me ?? false; if (isFollowing && isFollowingMe) { // 已互关 return { title: '互相关注', variant: 'outline' }; } else if (isFollowing) { // 已关注但对方未回关 return { title: '已关注', variant: 'outline' }; } else if (isFollowingMe) { // 对方关注了我,但我没关注对方 return { title: '回关', variant: 'primary' }; } else { // 互不关注 return { title: '关注', variant: 'primary' }; } }; // 渲染用户项 - 列表模式(移动端) const renderUserListItem: ListRenderItem = ({ item }) => { const buttonConfig = getButtonConfig(item); const isSelf = item.id === currentUser?.id; return ( handleUserPress(item.id)} activeOpacity={0.7} > {item.nickname} @{item.username} {item.bio?.trim() ? ( {item.bio} ) : null} {isCurrentUser && !isSelf && (